この大会は2021/3/27 23:00(JST)~2021/3/28 23:00(JST)に開催されました。
今回もチームで参戦。結果は173点で231チーム中144位でした。
自分で解けた問題をWriteupとして書いておきます。
Knock-Knock (Crypto)
添付のPythonコードを見ると、処理概要は以下のようになっている。
・Mersenne Twisterの乱数で32ビットの乱数を取得 ・port1: 取得した乱数の前半16bit ・port2: 取得した乱数の後半16bit ・ポートノッキングで以下の条件でsshが使用できる。 port1, port2の順でたたく ・ポートノッキングで以下の条件でsshを閉じる。 port2, port1の順でたたく。
pcapファイルを見ると、2222ポートの通信を除くと、連続でこの通信が走っていることがわかる。
例えば、No1,2、No13,14のパケットでは、ポート番号のみ着目すると、以下のようになっている。
(送信元ポート) → (送信先ポート) 55880 → 53177 38062 → 60973 38066 → 60973 55888 → 53177
この場合、port1は53177、port2は60973であることがわかる。この情報から乱数を復元できる。
これを繰り返し見ていけば624個の乱数を取得でき、Mersenne Twisterの性質から次の乱数を予測できる。つまり次のport1, port2がわかり、フラグが得られる。
from scapy.all import * import random 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 packets = rdpcap('knockd.pcap') N = 624 state = [] i = 0 for p in packets: if p[TCP].dport != 2222 and p[TCP].sport != 2222 and p[TCP].ack == 0: if i % 4 == 0: port1 = p[TCP].dport elif i % 4 == 1: port2 = p[TCP].dport number = (port1 << 16) + port2 state.append(untemper(number)) i += 1 state.append(N) random.setstate([3, tuple(state), None]) number = random.getrandbits(32) a = (number & (2 ** 32 - 2 ** 16)) >> 16 b = number & (2 ** 16 - 1) flag = 'VolgaCTF{%d,%d}' % (a, b) print flag
VolgaCTF{15094,7850}
Feedback (Feedback)
アンケートに答えたら、フラグが表示された。
VolgaCTF{fei8ighie1aiY5ka}