st98 の日記帳


[ctf] Pragyan CTF 2019 の write-up

3 月 8 日から 3 月 10 日にかけて開催された Pragyan CTF 2019 に、チーム Harekaze で参加しました。最終的にチームで 1325 点を獲得し、順位は得点 499 チーム中 74 位でした。うち、私は 10 問を解いて 1325 点を入れました。

以下、解いた問題の write-up です。

Binary

Feed_me (150)

Can you cook the most delicious recipe?
nc 159.89.166.12 9800
添付ファイル: challenge1 (x86_64 の ELF ファイル)

逆アセンブルしてどのような処理を行っているか確認します。以下のように srand(time(NULL)) を行ったあと rand() で 3 つの数値 (var_90 var_8c var_88) を生成し、

 a58:	bf 00 00 00 00       	mov    edi,0x0
 a5d:	e8 3e fe ff ff       	call   8a0 <time@plt>
 a62:	89 c7                	mov    edi,eax
 a64:	e8 17 fe ff ff       	call   880 <srand@plt>
 a69:	e8 92 fe ff ff       	call   900 <rand@plt>
︙
 a96:	89 85 70 ff ff ff    	mov    DWORD PTR [rbp-0x90],eax
 a9c:	e8 5f fe ff ff       	call   900 <rand@plt>
︙
 ac9:	89 85 74 ff ff ff    	mov    DWORD PTR [rbp-0x8c],eax
 acf:	e8 2c fe ff ff       	call   900 <rand@plt>
︙
 afc:	89 85 78 ff ff ff    	mov    DWORD PTR [rbp-0x88],eax

それらの数値を出力、

 b0e:	8b 8d 78 ff ff ff    	mov    ecx,DWORD PTR [rbp-0x88]
 b14:	8b 95 74 ff ff ff    	mov    edx,DWORD PTR [rbp-0x8c]
 b1a:	8b 85 70 ff ff ff    	mov    eax,DWORD PTR [rbp-0x90]
 b20:	89 c6                	mov    esi,eax
 b22:	48 8d 3d a9 02 00 00 	lea    rdi,[rip+0x2a9]        # "%d ; %d ; %d ;\n"
 b29:	b8 00 00 00 00       	mov    eax,0x0
 b2e:	e8 3d fd ff ff       	call   870 <printf@plt>

scanf("%s", var_6e) で文字列の入力を行っています。

 b33:	48 8d 45 92          	lea    rax,[rbp-0x6e]
 b37:	48 89 c6             	mov    rsi,rax
 b3a:	48 8d 3d a1 02 00 00 	lea    rdi,[rip+0x2a1]        # "%s"
 b41:	b8 00 00 00 00       	mov    eax,0x0
 b46:	e8 85 fd ff ff       	call   8d0 <__isoc99_scanf@plt>

その後、ユーザ入力の文字数をチェックしてから、atoi(ユーザ入力) atoi(ユーザ入力[10:]) atoi(ユーザ入力[20:]) をそれぞれ変数として var_84 var_80 var_7c に保存し、

 bc1:	48 8d 45 92          	lea    rax,[rbp-0x6e]
 bc5:	48 89 c7             	mov    rdi,rax
 bc8:	e8 f3 fc ff ff       	call   8c0 <atoi@plt>
 bcd:	89 85 7c ff ff ff    	mov    DWORD PTR [rbp-0x84],eax
 bd3:	48 8d 45 9c          	lea    rax,[rbp-0x64]
 bd7:	48 89 c7             	mov    rdi,rax
 bda:	e8 e1 fc ff ff       	call   8c0 <atoi@plt>
 bdf:	89 45 80             	mov    DWORD PTR [rbp-0x80],eax
 be2:	48 8d 45 a6          	lea    rax,[rbp-0x5a]
 be6:	48 89 c7             	mov    rdi,rax
 be9:	e8 d2 fc ff ff       	call   8c0 <atoi@plt>
 bee:	89 45 84             	mov    DWORD PTR [rbp-0x7c],eax

var_84 + var_80 == var_90 var_80 + var_7c == var_8c var_7c + var_84 == var_88 であるか確認しています。

 bf1:	8b 95 7c ff ff ff    	mov    edx,DWORD PTR [rbp-0x84]
 bf7:	8b 45 80             	mov    eax,DWORD PTR [rbp-0x80]
 bfa:	01 d0                	add    eax,edx
 bfc:	39 85 70 ff ff ff    	cmp    DWORD PTR [rbp-0x90],eax
 c02:	0f 85 b6 00 00 00    	jne    cbe <main+0x281>
 c08:	8b 55 80             	mov    edx,DWORD PTR [rbp-0x80]
 c0b:	8b 45 84             	mov    eax,DWORD PTR [rbp-0x7c]
 c0e:	01 d0                	add    eax,edx
 c10:	39 85 74 ff ff ff    	cmp    DWORD PTR [rbp-0x8c],eax
 c16:	0f 85 96 00 00 00    	jne    cb2 <main+0x275>
 c1c:	8b 55 84             	mov    edx,DWORD PTR [rbp-0x7c]
 c1f:	8b 85 7c ff ff ff    	mov    eax,DWORD PTR [rbp-0x84]
 c25:	01 d0                	add    eax,edx
 c27:	39 85 78 ff ff ff    	cmp    DWORD PTR [rbp-0x88],eax
 c2d:	75 77                	jne    ca6 <main+0x269>

一度の入力で 3 つの数値を入力する必要があるのが少し厄介ですが、atoi の数字以外の文字が出現すればその時点で処理を終了するという仕様を利用して、1---------2---------3 のように - を padding にしてしまいましょう。また、var_84 var_80 var_7c の探索については Z3Py に任せてしまいましょう。

import sys
from z3 import *
a, b, c = Ints('a b c')
x = [int(x) for x in sys.argv[1].split(' ; ')]

solver = Solver()
solver.add(b + a == x[0], c + b == x[1], a + c == x[2])
solver.check()
m = solver.model()

print('{:-<10}{:-<10}{:-<10}'.format(m[a], m[b], m[c]))
$ nc 159.89.166.12 9800
Can you cook my favourite food using these ingredients :)
-12220 ; -19820 ; -1566 ;
3017-------15237-----4583-----
That's yummy.... Here is your gift:
pctf{p1zz4_t0pp3d_w1th_p1n34ppl3_s4uc3}

フラグが得られました。

pctf{p1zz4_t0pp3d_w1th_p1n34ppl3_s4uc3}

Miscellaneous

EXORcism (300)

My friend Alex needs your help very fast. He has been possessed by a ghost and the only way to save him is if you tell the flag to the ghost. Hurry up, time is running out!
添付ファイル: encoded.txt

以下のような内容のファイルが与えられました。

1
1
1
1
1
1
1
1
1
1
1
︙

0 もしくは 1 のみの行が 10000 行続いています。1 の場合には黒、0 の場合には白として、100 ピクセル * 100 ピクセルの画像にしてみましょう。

import binascii
from PIL import Image
w, h = 100, 100
im = Image.new('1', (w, h))
pix = im.load()

with open('encoded.txt', 'r') as f:
  s = f.read().replace('\n', '')

for y in range(h):
  for x in range(w):
    pix[x, y] = int(s[y * w + x])

im.show()
im.save('res.png')

2019-03-11_1.png

QR コードが出てきました。これを zbarimg で読んでみましょう。

>zbarimg res.png
QR-Code:160f15011d1b095339595138535f135613595e1a
scanned 1 barcode symbols from 1 images

hex としてデコードし、フラグフォーマットの pctf{ と xor してみましょう。

$ python
>>> s = "160f15011d1b095339595138535f135613595e1a".decode('hex')
>>> xor(s, 'pctf{')
'flagfkj\'_"![\'9h&p-8a'

flag と xor されているようです。

>>> xor(s, 'flag')
'pctf{wh4_50_53r1u5?}'

フラグが得られました。

pctf{wh4_50_53r1u5?}

Cryptography

Spoiler (50)

Bran Stark, wants to convey an important information to the Sansa back at winterfell. He sends her a message. The message however, is encrypted though.
Can you find out what Bran is trying to convey??
添付ファイル: key.pdf

key.pdf を開くと、3a2c3a35152538272c2d213e332e3c25383030373a15 と表示されました。

バイナリエディタで key.pdf を開くと、以下のように %%EOF (PDF の終端) 以降にも何かがあることが分かります。

︙
%%EOF0000006a0000006f0000006e000000730000006e0000006f000000770000006900000073000000640000007200000061000000670000006f0000006e00000062000000790000006200000069000000720000007400000068

これらを xor してみましょう。

$ python
>>> from pwn import *
>>> s = "3a2c3a35152538272c2d213e332e3c25383030373a15".decode('hex')
>>> t = """0000006a0000006f0000006e000000730000006e0000006f000000770000006900000073000000640000007200000061000000670000006f0000006e00000062000000790000006200000069000000720000007400000068""".replace('000000', '').decode('hex')
>>> xor(s, t).lower()
'pctf{jon_is_targaryen}'

フラグが得られました。

pctf{jon_is_targaryen}

Add them Sneaky Polynomials (100)

Rahul, the geek boy of his class, doesn’t like doing things the conventional way. He’s just learned polynomials in class, and wants to prove a point to his friend Sandhya. But Sandhya is sitting in the first bench, so Ram decides to write what he wants to convey on a chit and pass it through the guys sitting in front of him. The guys in between try to read it, but do not understand. Sadly, nor does Sandhya. Can you help him out?
添付ファイル: polynomials.txt

polynomials.txt は以下のような内容でした。

p = x^406 + x^405 + x^402 + x^399 + x^397 + x^391 + x^390 + x^387 + x^386 + x^378 + x^374 + x^372 + x^371 + x^369 + x^367 + x^364 + x^360 + x^358 + x^357 + x^352 + x^350 + x^345 + x^344 + x^341 + x^336 + x^335 + x^334 + x^333 + x^331 + x^330 + x^329 + x^328 + x^327 + x^324 + x^322 + x^320 + x^314 + x^311 + x^308 + x^307 + x^303 + x^300 + x^299 + x^296 + x^295 + x^290 + x^289 + x^287 + x^279 + x^271 + x^266 + x^264 + x^262 + x^260 + x^257 + x^256 + x^252 + x^249 + x^248 + x^246 + x^243 + x^239 + x^238 + x^236 + x^233 + x^230 + x^227 + x^225 + x^223 + x^222 + x^220 + x^218 + x^216 + x^215 + x^209 + x^208 + x^207 + x^204 + x^202 + x^199 + x^190 + x^189 + x^185 + x^184 + x^180 + x^177 + x^176 + x^175 + x^172 + x^167 + x^166 + x^162 + x^160 + x^159 + x^155 + x^154 + x^149 + x^147 + x^143 + x^137 + x^135 + x^131 + x^129 + x^126 + x^124 + x^122 + x^116 + x^110 + x^108 + x^105 + x^104 + x^100 + x^99 + x^97 + x^94 + x^93 + x^90 + x^88 + x^87 + x^86 + x^85 + x^83 + x^75 + x^73 + x^69 + x^63 + x^62 + x^57 + x^54 + x^51 + x^44 + x^41 + x^38 + x^37 + x^36 + x^34 + x^29 + x^28 + x^26 + x^25 + x^21 + x^20 + x^19 + x^16 + x^15 + x^14 + x^13 + x^6 + x^5 + x^2 

q = x^399 + x^398 + x^396 + x^393 + x^392 + x^391 + x^388 + x^386 + x^384 + x^381 + x^377 + x^376 + x^368 + x^364 + x^360 + x^355 + x^354 + x^353 + x^352 + x^348 + x^346 + x^345 + x^344 + x^343 + x^335 + x^334 + x^329 + x^326 + x^325 + x^321 + x^318 + x^317 + x^315 + x^314 + x^311 + x^307 + x^306 + x^304 + x^300 + x^296 + x^293 + x^291 + x^282 + x^277 + x^270 + x^263 + x^261 + x^260 + x^256 + x^254 + x^253 + x^252 + x^251 + x^248 + x^245 + x^242 + x^241 + x^239 + x^238 + x^236 + x^232 + x^226 + x^225 + x^222 + x^220 + x^219 + x^214 + x^209 + x^208 + x^207 + x^206 + x^202 + x^200 + x^196 + x^191 + x^190 + x^186 + x^181 + x^180 + x^178 + x^177 + x^169 + x^168 + x^165 + x^164 + x^163 + x^162 + x^161 + x^159 + x^157 + x^156 + x^151 + x^149 + x^148 + x^147 + x^146 + x^144 + x^141 + x^140 + x^138 + x^137 + x^136 + x^134 + x^133 + x^132 + x^130 + x^129 + x^128 + x^126 + x^123 + x^121 + x^113 + x^109 + x^103 + x^101 + x^100 + x^95 + x^93 + x^91 + x^85 + x^84 + x^81 + x^74 + x^73 + x^71 + x^68 + x^67 + x^54 + x^52 + x^51 + x^50 + x^48 + x^46 + x^45 + x^43 + x^39 + x^35 + x^32 + x^31 + x^30 + x^29 + x^21 + x^15 + x^14 + x^9 + x^8 + x^5 + x^4 + x^2 + 1 

r = x^404 + x^402 + x^396 + x^389 + x^387 + x^386 + x^384 + x^382 + x^376 + x^373 + x^367 + x^366 + x^365 + x^362 + x^361 + x^358 + x^356 + x^355 + x^354 + x^353 + x^352 + x^349 + x^348 + x^347 + x^345 + x^343 + x^340 + x^334 + x^332 + x^331 + x^328 + x^327 + x^326 + x^322 + x^317 + x^316 + x^314 + x^313 + x^312 + x^310 + x^309 + x^308 + x^305 + x^304 + x^303 + x^301 + x^300 + x^299 + x^296 + x^295 + x^292 + x^291 + x^290 + x^288 + x^287 + x^286 + x^285 + x^283 + x^279 + x^278 + x^274 + x^271 + x^269 + x^268 + x^266 + x^265 + x^263 + x^261 + x^260 + x^259 + x^258 + x^256 + x^254 + x^252 + x^251 + x^250 + x^249 + x^244 + x^243 + x^242 + x^237 + x^236 + x^228 + x^225 + x^224 + x^223 + x^222 + x^221 + x^215 + x^214 + x^213 + x^212 + x^205 + x^201 + x^200 + x^199 + x^197 + x^193 + x^192 + x^191 + x^190 + x^189 + x^188 + x^187 + x^182 + x^180 + x^175 + x^174 + x^173 + x^167 + x^166 + x^163 + x^158 + x^156 + x^155 + x^153 + x^151 + x^150 + x^149 + x^143 + x^142 + x^140 + x^139 + x^136 + x^135 + x^133 + x^129 + x^126 + x^125 + x^123 + x^121 + x^118 + x^117 + x^116 + x^115 + x^113 + x^110 + x^106 + x^105 + x^104 + x^103 + x^102 + x^98 + x^95 + x^92 + x^89 + x^87 + x^85 + x^81 + x^80 + x^77 + x^76 + x^75 + x^74 + x^71 + x^70 + x^67 + x^66 + x^64 + x^63 + x^60 + x^59 + x^58 + x^56 + x^54 + x^53 + x^48 + x^44 + x^41 + x^39 + x^38 + x^35 + x^34 + x^31 + x^29 + x^28 + x^27 + x^22 + x^21 + x^20 + x^17 + x^14 + x^12 + x^11 + x^10 + x^9 + x^6 + x^4 + x^3 + x + 1 

それぞれビット列で表してみましょう。

import re
s = '''x^406 + x^405 + x^402 + x^399 + x^397 + x^391 + x^390 + x^387 + x^386 + x^378 + x^374 + x^372 + x^371 + x^369 + x^367 + x^364 + x^360 + x^358 + x^357 + x^352 + x^350 + x^345 + x^344 + x^341 + x^336 + x^335 + x^334 + x^333 + x^331 + x^330 + x^329 + x^328 + x^327 + x^324 + x^322 + x^320 + x^314 + x^311 + x^308 + x^307 + x^303 + x^300 + x^299 + x^296 + x^295 + x^290 + x^289 + x^287 + x^279 + x^271 + x^266 + x^264 + x^262 + x^260 + x^257 + x^256 + x^252 + x^249 + x^248 + x^246 + x^243 + x^239 + x^238 + x^236 + x^233 + x^230 + x^227 + x^225 + x^223 + x^222 + x^220 + x^218 + x^216 + x^215 + x^209 + x^208 + x^207 + x^204 + x^202 + x^199 + x^190 + x^189 + x^185 + x^184 + x^180 + x^177 + x^176 + x^175 + x^172 + x^167 + x^166 + x^162 + x^160 + x^159 + x^155 + x^154 + x^149 + x^147 + x^143 + x^137 + x^135 + x^131 + x^129 + x^126 + x^124 + x^122 + x^116 + x^110 + x^108 + x^105 + x^104 + x^100 + x^99 + x^97 + x^94 + x^93 + x^90 + x^88 + x^87 + x^86 + x^85 + x^83 + x^75 + x^73 + x^69 + x^63 + x^62 + x^57 + x^54 + x^51 + x^44 + x^41 + x^38 + x^37 + x^36 + x^34 + x^29 + x^28 + x^26 + x^25 + x^21 + x^20 + x^19 + x^16 + x^15 + x^14 + x^13 + x^6 + x^5 + x^2
x^399 + x^398 + x^396 + x^393 + x^392 + x^391 + x^388 + x^386 + x^384 + x^381 + x^377 + x^376 + x^368 + x^364 + x^360 + x^355 + x^354 + x^353 + x^352 + x^348 + x^346 + x^345 + x^344 + x^343 + x^335 + x^334 + x^329 + x^326 + x^325 + x^321 + x^318 + x^317 + x^315 + x^314 + x^311 + x^307 + x^306 + x^304 + x^300 + x^296 + x^293 + x^291 + x^282 + x^277 + x^270 + x^263 + x^261 + x^260 + x^256 + x^254 + x^253 + x^252 + x^251 + x^248 + x^245 + x^242 + x^241 + x^239 + x^238 + x^236 + x^232 + x^226 + x^225 + x^222 + x^220 + x^219 + x^214 + x^209 + x^208 + x^207 + x^206 + x^202 + x^200 + x^196 + x^191 + x^190 + x^186 + x^181 + x^180 + x^178 + x^177 + x^169 + x^168 + x^165 + x^164 + x^163 + x^162 + x^161 + x^159 + x^157 + x^156 + x^151 + x^149 + x^148 + x^147 + x^146 + x^144 + x^141 + x^140 + x^138 + x^137 + x^136 + x^134 + x^133 + x^132 + x^130 + x^129 + x^128 + x^126 + x^123 + x^121 + x^113 + x^109 + x^103 + x^101 + x^100 + x^95 + x^93 + x^91 + x^85 + x^84 + x^81 + x^74 + x^73 + x^71 + x^68 + x^67 + x^54 + x^52 + x^51 + x^50 + x^48 + x^46 + x^45 + x^43 + x^39 + x^35 + x^32 + x^31 + x^30 + x^29 + x^21 + x^15 + x^14 + x^9 + x^8 + x^5 + x^4 + x^2 + x^0
x^404 + x^402 + x^396 + x^389 + x^387 + x^386 + x^384 + x^382 + x^376 + x^373 + x^367 + x^366 + x^365 + x^362 + x^361 + x^358 + x^356 + x^355 + x^354 + x^353 + x^352 + x^349 + x^348 + x^347 + x^345 + x^343 + x^340 + x^334 + x^332 + x^331 + x^328 + x^327 + x^326 + x^322 + x^317 + x^316 + x^314 + x^313 + x^312 + x^310 + x^309 + x^308 + x^305 + x^304 + x^303 + x^301 + x^300 + x^299 + x^296 + x^295 + x^292 + x^291 + x^290 + x^288 + x^287 + x^286 + x^285 + x^283 + x^279 + x^278 + x^274 + x^271 + x^269 + x^268 + x^266 + x^265 + x^263 + x^261 + x^260 + x^259 + x^258 + x^256 + x^254 + x^252 + x^251 + x^250 + x^249 + x^244 + x^243 + x^242 + x^237 + x^236 + x^228 + x^225 + x^224 + x^223 + x^222 + x^221 + x^215 + x^214 + x^213 + x^212 + x^205 + x^201 + x^200 + x^199 + x^197 + x^193 + x^192 + x^191 + x^190 + x^189 + x^188 + x^187 + x^182 + x^180 + x^175 + x^174 + x^173 + x^167 + x^166 + x^163 + x^158 + x^156 + x^155 + x^153 + x^151 + x^150 + x^149 + x^143 + x^142 + x^140 + x^139 + x^136 + x^135 + x^133 + x^129 + x^126 + x^125 + x^123 + x^121 + x^118 + x^117 + x^116 + x^115 + x^113 + x^110 + x^106 + x^105 + x^104 + x^103 + x^102 + x^98 + x^95 + x^92 + x^89 + x^87 + x^85 + x^81 + x^80 + x^77 + x^76 + x^75 + x^74 + x^71 + x^70 + x^67 + x^66 + x^64 + x^63 + x^60 + x^59 + x^58 + x^56 + x^54 + x^53 + x^48 + x^44 + x^41 + x^39 + x^38 + x^35 + x^34 + x^31 + x^29 + x^28 + x^27 + x^22 + x^21 + x^20 + x^17 + x^14 + x^12 + x^11 + x^10 + x^9 + x^6 + x^4 + x^3 + x^1 + x^0'''
res = 0
for line in s.splitlines():
  tmp = [0 for _ in range(408)]
  for x in re.findall(r'\d+', line):
    tmp[int(x)] = 1
  tmp = tmp[::-1]
  print(''.join(str(x) for x in tmp))
$ python test.py
011001001010000011001100000001000101101010010001011000010100001100100001111011111001010100000100100110001001100110000110100000001000000010000101010100110001001101001000110100100100101011010101100000111001010010000000011000110001001110010000110001011000110000101000100000101000101001010100000100000101001100011010011001011110100000001010001000001100001001001000000100100111010000110110001110011110000001100100
000000001101001110010101001000110000000100010001000011110001011110000000110000100110001001101100100011010001000100101000000001000010000001000000101100010111100100100110110100010000011001011000010000111100010100010000110001000011011000000011001111101011000010111101001101110111011101001010000000100010000010110000101010000011001000000110100110000000000001011101011010001000100111100000001000001100001100110101
000101000001000000101101010000010010000011100110010111110011101010010000010110011100010000110111011100111011100110011101111010001100010010110110101111010101111000011100001100000001001111100000111100000010001110100011111110000101000011100000110010000101101011100000110110011010001001101010011110100100011111000100100100101010001100111100110011011001110101100001000100101100110010111000011100100101111001011011

XOR is your best friend というヒントが与えられているので、xor するように修正してみましょう。

import binascii
import re
s = '''x^406 + x^405 + x^402 + x^399 + x^397 + x^391 + x^390 + x^387 + x^386 + x^378 + x^374 + x^372 + x^371 + x^369 + x^367 + x^364 + x^360 + x^358 + x^357 + x^352 + x^350 + x^345 + x^344 + x^341 + x^336 + x^335 + x^334 + x^333 + x^331 + x^330 + x^329 + x^328 + x^327 + x^324 + x^322 + x^320 + x^314 + x^311 + x^308 + x^307 + x^303 + x^300 + x^299 + x^296 + x^295 + x^290 + x^289 + x^287 + x^279 + x^271 + x^266 + x^264 + x^262 + x^260 + x^257 + x^256 + x^252 + x^249 + x^248 + x^246 + x^243 + x^239 + x^238 + x^236 + x^233 + x^230 + x^227 + x^225 + x^223 + x^222 + x^220 + x^218 + x^216 + x^215 + x^209 + x^208 + x^207 + x^204 + x^202 + x^199 + x^190 + x^189 + x^185 + x^184 + x^180 + x^177 + x^176 + x^175 + x^172 + x^167 + x^166 + x^162 + x^160 + x^159 + x^155 + x^154 + x^149 + x^147 + x^143 + x^137 + x^135 + x^131 + x^129 + x^126 + x^124 + x^122 + x^116 + x^110 + x^108 + x^105 + x^104 + x^100 + x^99 + x^97 + x^94 + x^93 + x^90 + x^88 + x^87 + x^86 + x^85 + x^83 + x^75 + x^73 + x^69 + x^63 + x^62 + x^57 + x^54 + x^51 + x^44 + x^41 + x^38 + x^37 + x^36 + x^34 + x^29 + x^28 + x^26 + x^25 + x^21 + x^20 + x^19 + x^16 + x^15 + x^14 + x^13 + x^6 + x^5 + x^2
x^399 + x^398 + x^396 + x^393 + x^392 + x^391 + x^388 + x^386 + x^384 + x^381 + x^377 + x^376 + x^368 + x^364 + x^360 + x^355 + x^354 + x^353 + x^352 + x^348 + x^346 + x^345 + x^344 + x^343 + x^335 + x^334 + x^329 + x^326 + x^325 + x^321 + x^318 + x^317 + x^315 + x^314 + x^311 + x^307 + x^306 + x^304 + x^300 + x^296 + x^293 + x^291 + x^282 + x^277 + x^270 + x^263 + x^261 + x^260 + x^256 + x^254 + x^253 + x^252 + x^251 + x^248 + x^245 + x^242 + x^241 + x^239 + x^238 + x^236 + x^232 + x^226 + x^225 + x^222 + x^220 + x^219 + x^214 + x^209 + x^208 + x^207 + x^206 + x^202 + x^200 + x^196 + x^191 + x^190 + x^186 + x^181 + x^180 + x^178 + x^177 + x^169 + x^168 + x^165 + x^164 + x^163 + x^162 + x^161 + x^159 + x^157 + x^156 + x^151 + x^149 + x^148 + x^147 + x^146 + x^144 + x^141 + x^140 + x^138 + x^137 + x^136 + x^134 + x^133 + x^132 + x^130 + x^129 + x^128 + x^126 + x^123 + x^121 + x^113 + x^109 + x^103 + x^101 + x^100 + x^95 + x^93 + x^91 + x^85 + x^84 + x^81 + x^74 + x^73 + x^71 + x^68 + x^67 + x^54 + x^52 + x^51 + x^50 + x^48 + x^46 + x^45 + x^43 + x^39 + x^35 + x^32 + x^31 + x^30 + x^29 + x^21 + x^15 + x^14 + x^9 + x^8 + x^5 + x^4 + x^2 + x^0
x^404 + x^402 + x^396 + x^389 + x^387 + x^386 + x^384 + x^382 + x^376 + x^373 + x^367 + x^366 + x^365 + x^362 + x^361 + x^358 + x^356 + x^355 + x^354 + x^353 + x^352 + x^349 + x^348 + x^347 + x^345 + x^343 + x^340 + x^334 + x^332 + x^331 + x^328 + x^327 + x^326 + x^322 + x^317 + x^316 + x^314 + x^313 + x^312 + x^310 + x^309 + x^308 + x^305 + x^304 + x^303 + x^301 + x^300 + x^299 + x^296 + x^295 + x^292 + x^291 + x^290 + x^288 + x^287 + x^286 + x^285 + x^283 + x^279 + x^278 + x^274 + x^271 + x^269 + x^268 + x^266 + x^265 + x^263 + x^261 + x^260 + x^259 + x^258 + x^256 + x^254 + x^252 + x^251 + x^250 + x^249 + x^244 + x^243 + x^242 + x^237 + x^236 + x^228 + x^225 + x^224 + x^223 + x^222 + x^221 + x^215 + x^214 + x^213 + x^212 + x^205 + x^201 + x^200 + x^199 + x^197 + x^193 + x^192 + x^191 + x^190 + x^189 + x^188 + x^187 + x^182 + x^180 + x^175 + x^174 + x^173 + x^167 + x^166 + x^163 + x^158 + x^156 + x^155 + x^153 + x^151 + x^150 + x^149 + x^143 + x^142 + x^140 + x^139 + x^136 + x^135 + x^133 + x^129 + x^126 + x^125 + x^123 + x^121 + x^118 + x^117 + x^116 + x^115 + x^113 + x^110 + x^106 + x^105 + x^104 + x^103 + x^102 + x^98 + x^95 + x^92 + x^89 + x^87 + x^85 + x^81 + x^80 + x^77 + x^76 + x^75 + x^74 + x^71 + x^70 + x^67 + x^66 + x^64 + x^63 + x^60 + x^59 + x^58 + x^56 + x^54 + x^53 + x^48 + x^44 + x^41 + x^39 + x^38 + x^35 + x^34 + x^31 + x^29 + x^28 + x^27 + x^22 + x^21 + x^20 + x^17 + x^14 + x^12 + x^11 + x^10 + x^9 + x^6 + x^4 + x^3 + x^1 + x^0'''
res = 0
for line in s.splitlines():
  tmp = [0 for _ in range(408)]
  for x in re.findall(r'\d+', line):
    tmp[int(x)] = 1
  tmp = tmp[::-1]
  res ^= int(''.join(str(x) for x in tmp),2)
print(binascii.unhexlify(hex(res)[2:]).decode())
$ python solve.py
pctf{f1n1t3_f13lds_4r3_m0r3_us3ful_th4n_y0u_th1nk}

フラグが得られました。

pctf{f1n1t3_f13lds_4r3_m0r3_us3ful_th4n_y0u_th1nk}

Forensics

Magic PNGs (100)

Magic PNGs:
Can you help me open this zip file? I seem to have forgotten its password. I think the image file has something to do with it.
添付ファイル: tryme.zip, you_cant_see_me.png

tryme.zip はパスワード付きの ZIP ファイル、you_cant_see_me.png はそのままでは開けない PNG ファイルのようです。

バイナリエディタで you_cant_see_me.png を眺めていると、tEXt チャンクに md5_MEf89jf4h9 という文字列があり、また IDAT チャンクの Chunk Type が idat と小文字になっていることが分かりました。

IDAT チャンクの Chunk Type を IDAT と大文字に修正すると、画像として開くことができるようになりました。内容は h4CK3RM4n という文字列でした。

tEXt チャンクの md5 me というヒントと組み合わせて、2c919f82ee2ed6985d5c5e275d67e4f8 (md5('h4CK3RM4n')) をパスワードとして tryme.zip を展開することができました。

pctf{y0u_s33_m33_n0w!}

Web

Do prepare to see cookies lurking everywhere. http://159.89.166.12:13500/

与えられた URL にアクセスすると、flag=bc54f4d60f1cec0f9a6cb70e13f2127a という Cookie が発行されました。Cookie を保持して更新すると今度は flag=114d6a415b3d04db792ca7c0da0c7a55 という Cookie が発行されました。

bc54f4d60f1cec0f9a6cb70e13f2127a114d6a415b3d04db792ca7c0da0c7a55 でググると、それぞれ pctf の MD5 ハッシュであることが分かります。

自動で Cookie の取得と元の文字列の探索をしてくれるスクリプトを書きましょう。

import hashlib
import requests
URL = 'http://159.89.166.12:13500/'

table = {}
for a in range(256):
  for b in range(256):
    tmp = chr(a) + chr(b)
    table[hashlib.md5(tmp).hexdigest()] = tmp

sess = requests.Session()
res = ''
while True:
  sess.get(URL)
  res += table[sess.cookies.get('flag')]
  print res
  if '}' in res:
    break
>python2 solve.py
pc
pctf
pctf{c
pctf{c0o
pctf{c0oki
pctf{c0oki3s
pctf{c0oki3s_@
pctf{c0oki3s_@re
pctf{c0oki3s_@re_y
pctf{c0oki3s_@re_yUm
pctf{c0oki3s_@re_yUm_b
pctf{c0oki3s_@re_yUm_bUt
pctf{c0oki3s_@re_yUm_bUt_t
pctf{c0oki3s_@re_yUm_bUt_tHE
pctf{c0oki3s_@re_yUm_bUt_tHEy_
pctf{c0oki3s_@re_yUm_bUt_tHEy_@l
pctf{c0oki3s_@re_yUm_bUt_tHEy_@ls0
pctf{c0oki3s_@re_yUm_bUt_tHEy_@ls0_r
pctf{c0oki3s_@re_yUm_bUt_tHEy_@ls0_r3v
pctf{c0oki3s_@re_yUm_bUt_tHEy_@ls0_r3vEa
pctf{c0oki3s_@re_yUm_bUt_tHEy_@ls0_r3vEaL_
pctf{c0oki3s_@re_yUm_bUt_tHEy_@ls0_r3vEaL_@_
pctf{c0oki3s_@re_yUm_bUt_tHEy_@ls0_r3vEaL_@_l0
pctf{c0oki3s_@re_yUm_bUt_tHEy_@ls0_r3vEaL_@_l0t}

フラグが得られました。

pctf{c0oki3s_@re_yUm_bUt_tHEy_@ls0_r3vEaL_@_l0t}

Game of Faces (100)

The Game of Faces, welcomes you. In this era, where AIs generate a lot of faces, we would like you to contribute to the same by uploading your image. Thank you for contributing, to continue.
http://159.89.166.12:15000/

与えられた URL にアクセスすると、以下のようなフォームが含まれるページが表示されました。

	  <form action='#' method = "GET" target="resultFrame">
              Upload Your Profile Picture : <input type="file" name="profile_pic" >
              <input type="submit" value="Upload Image" name="submit">
          </form>

適当なファイルをアップロードすると /?profile_pic=favicon.png&submit=Upload+Image# に遷移し、以下のような文字列が表示されました。

<div class="row">
        <div class="col-lg-12" >
          <h1>VGhlX3Njcm9sbF9zYXlzPXRoZV9uaWdodF9raW5nVlN2YWx5cmlhbi50eHQ==</h1>        </div>

VGhlX3Njcm9sbF9zYXlzPXRoZV9uaWdodF9raW5nVlN2YWx5cmlhbi50eHQ== を Base64 デコードすると The_scroll_says=the_night_kingVSvalyrian.txt になります。/the_night_kingVSvalyrian.txt にアクセスするとフラグが得られました。

pctf{You_L00K_Wi3Rd_IN_H3R3}

Mandatory PHP (150)

PHP, PHP everywhere get the flag and earn your points there.
http://159.89.166.12:14000/

与えられた URL にアクセスすると、以下のようなソースコードが表示されました。

<?php 
include 'flag.php'; 
highlight_file('index.php'); 
$a = $_GET["val1"]; 
$b = $_GET["val2"]; 
$c = $_GET["val3"]; 
$d = $_GET["val4"]; 
if(preg_match('/[^A-Za-z]/', $a)) 
die('oh my gawd...'); 
$a=hash("sha256",$a); 
$a=(log10($a**(0.5)))**2; 
if($c>0&&$d>0&&$d>$c&&$a==$c*$c+$d*$d) 
$s1="true"; 
else 
    die("Bye..."); 
if($s1==="true") 
    echo $flag1; 
for($i=1;$i<=10;$i++){ 
    if($b==urldecode($b)) 
        die('duck'); 
    else 
        $b=urldecode($b); 
}     
if($b==="WoAHh!") 
$s2="true"; 
else 
    die('oops..'); 
if($s2==="true") 
    echo $flag2; 
die('end...'); 
?> 

まず $a $c $d について考えていきましょう。1e309 === INF かつ INF == '1e309aaaa' であることを利用して、SHA256 のハッシュ値が /^1e309[abcde]/ に当てはまるような文字列を探索してみましょう。

import hashlib
import itertools
import re

for x in itertools.product('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', repeat=8):
  s = ''.join(x)
  hash = hashlib.sha256(s.encode()).hexdigest()
  if re.match('^1e309[abcdf]', hash):
    print(s)
    break

実行すると AAAAFGWQ が見つかりました。$c$d については、$c1$dINF になるようにすればよいでしょう。

$b について考えてみましょう。10 回 urldecode した結果が WoAHh! になればよいようなので、urlencode でその逆の操作をすればよいでしょう。

<?php
$b = "WoAHh!";
for ($i = 0; $i < 11; $i++) {
  $b = urlencode($b);
}
var_dump($b);

これらを組み合わせて /?val1=AAAAFGWQ&val2=WoAHh%2525252525252525252521&val3=1&val4=1e309 でフラグが得られました。

pctf{b3_c4r3fu1_w1th_pHp_f31145}
このエントリーをはてなブックマークに追加
st98.github.io / st98 の日記帳