ASIS CTF 2019 write-up
チームm1z0r3として参加しました、warm-up問題2問しか解けず、大した貢献はできなかった。
解けた問題
A delicious soup[Crypto,44pts]
最初に取り込んだ問題、他の問題proof of workがついていて、逃げ腰でこの問題に切り替えました。まずは問題文↓
# flag.enc 11d.3ilVk_d3CpIO_4nlS.ncnz3e_0S}M_kn5scpm345n3nSe_u_S{iy__4EYLP_aAAall
# simple_and_delicious.py #!/usr/bin/env python #-*- coding:utf-8 -*- import random from flag import flag def encrypt(msg, perm): W = len(perm) while len(msg) % (2*W): msg += "." msg = msg[1:] + msg[:1] msg = msg[0::2] + msg[1::2] msg = msg[1:] + msg[:1] res = "" for j in xrange(0, len(msg), W): for k in xrange(W): res += msg[j:j+W][perm[k]] msg = res return msg def encord(msg, perm, l): for _ in xrange(l): msg = encrypt(msg, perm) return msg W, l = 7, random.randint(0, 1337) perm = range(W) random.shuffle(perm) *** enc = encord(flag, perm, l) f = open('flag.enc', 'w') f.write(enc) f.close()
パッと見線形に見えたが、よくよく見るとflagの文字列の配列をゴニョゴニョシャッフルしただけ。
具体的にどういうシャッフルなのかというと:
1. 最初の1文字を後ろに入れる
ASIS{8A4Qwl4K6p95N8Rqr5lneSzz6dUxzfgTwbFbV10zvY7T3qmYhRAkQj9gXlzpxK}..
↓
SIS{8A4Qwl4K6p95N8Rqr5lneSzz6dUxzfgTwbFbV10zvY7T3qmYhRAkQj9gXlzpxK}..A
2. 次にindexは偶数の文字を集め前半部分に入れる、後ろに奇数の集まりを入れる
SS84w469NRrlez6UzgwFV0v73mhAQ9Xzx}.I{AQlKp58q5nSzdxfTbb1zYTqYRkjglpK.A
3. もう一度1.の手順をやる
S84w469NRrlez6UzgwFV0v73mhAQ9Xzx}.I{AQlKp58q5nSzdxfTbb1zYTqYRkjglpK.SA
4. 文字列を7文字を一括りにして、あらかじめ決めたシャッフルルールperm
にしたがって変換
例:
[S,8,4,w,4,6,9]
↓
[2,1,3,5,6,4,0]
↓
[4,8,w,6,9,4,S]……
5. 手順1-4の繰り返しをrandom(0,1337)回をやる
幸いもうちょっと暗号文を見てみると、flagの形式ASIS{...}
の中I
,{
,}
は1回ずつしか出現しなかったので、この3つをマークとして利用出来そう。
さらにシャッフルルールの長さは7かつ繰り返し回数もたかが1337程度。ここでブルートフォースの回数を軽く見積もると
$$
7! * 1337 =6738480
$$
7桁はまだいける範囲内ことがわかり、とりあえずperm
の順列に沿って毎回1337まで回して、シャッフルされたI{}
3つのindexを暗号文と照合して、一致したら復号したらいいの話です。
import itertools import random import sys def encrypt(msg, perm): W = len(perm) while len(msg) % (2*W): msg += "." msg = msg[1:] + msg[:1] msg = msg[0::2] + msg[1::2] msg = msg[1:] + msg[:1] res = "" for j in xrange(0, len(msg), W): for k in xrange(W): res += msg[j:j+W][perm[k]] msg = res return msg def encord(msg, perm, l): for i in xrange(l): msg = encrypt(msg, perm) if msg.index('I') == 14 and msg.index('{')==53 and msg.index('}')==31: print perm,str(i) print decord(open('flag.enc','r').read(),perm,i+1) return msg def decord(msg, perm, l): for _ in xrange(l): msg = decrypt(msg, perm) return msg def decrypt(msg,perm): W = 7 res = "" for j in xrange(0,len(msg), W): k = ['a','b','c','d','e','f','g'] for i in xrange(W): k[perm[i]] = msg[j:j+W][i] res += ''.join(k) r = ['a' for _ in range(70)] p = [3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 0, 1] for z in range(70): r[p[z]] = res[z] return ''.join(r) #W, l = 7, random.randint(0, 1337) W = 7 perm = range(W) #random.shuffle(perm) f = open('flag.enc', 'r') msg = f.read() #flag = exploit(msg,perm) fff = 'ASIS{'+'8A4Qwl4K6p95N8Rqr5lneSzz6dUxzfgTwbFbV10zvY7T3qmYhRAkQj9gXlzpxK'+'}' mid = encord(fff,[2,1,3,5,6,4,0],555) print decord(mid,[2,1,3,5,6,4,0],555) for a in itertools.permutations(perm): flag = 'ASIS{'+'8A4Qwl4K6p95N8Rqr5lneSzz6dUxzfgTwbFbV10zvY7T3qmYhRAkQj9gXlzpxK'+'}' ff = encord(flag,list(a),1337) #print flag
flag collision[Coding,67pts]
指定のアドレスまでnetcatしたら、ASISお馴染みのPoWが現れた、しかも毎回hash関数が変わる。
とりあえずhash関数の種類をまとめました。
md5 sha1 sha224 sha256 sha384 sha512
まぁ向こうの言う通りに回せばなんとかなる。(ここではpwntoolの方が良い、最後interactiveに切り替えてさらに調査できる)
PoWを突破した後急に「CRCの衝突をやれ」と言われ、早速ググりました。色々実装方法を実装してもうまくいけなくて、ダメ元にブルートフォースをやったら秒で結果が出ました...(時間の無駄でした)
毎回random文字数の衝突を10?回ぐらいやればflagが降ってきました。
from pwn import * from zlib import crc32 from m1z0r3 import * from hashlib import md5,sha1,sha224,sha256,sha384,sha512 from Crypto.Util.number import long_to_bytes import random, string def randomname(n): randlst = [random.choice(string.ascii_letters + string.digits) for i in range(n)] return ''.join(randlst) def exploit(dic): #io = remote('37.139.9.232','19199') ip = '37.139.9.232' port = 19199 s,f = sock(ip,port) hash_list = [md5,sha1,sha224,sha256,sha384,sha512] m = recv_line(f) print m last6 = m[-7:-1] print last6 h = '' def PoW(idx,last): print '[+] start pow' x = 1 while True: bx = b64e(long_to_bytes(x)) hexx = hash_list[idx](bx).hexdigest()[-6:] if hexx == last: print '[+] bingo' return bx x += 1 if 'md5' in m: ans = PoW(0,last6) s.send(ans+'\n') elif 'sha1' in m: ans = PoW(1,last6) s.send(ans+'\n') elif 'sha224' in m: ans = PoW(2,last6) s.send(ans+'\n') elif 'sha256' in m: ans = PoW(3,last6) s.send(ans+'\n') elif 'sha384' in m: ans = PoW(4,last6) s.send(ans+'\n') else: ans = PoW(5,last6) s.send(ans+'\n') for _ in range(4): print recv_line(f) while True: m = '' while True: m = recv_line(f) print m if m.endswith('CRC32:|\n'): break elif 'ASIS{' in m: print m print m idx1 = m.index('=') idx2 = m.index(' and') ll = eval(m[idx1+1:idx2]) if dic.has_key(str(ll)): a = dic[str(ll)][0] b = dic[str(ll)][1] print a,b s.send('({},{})'.format(a,b)+'\n') else: r = dict() while True: x = 'ASIS{%s}' % randomname(ll-6) c = str(crc32(x)) if r.has_key(c): print 'bingo' print [r[c],x] dic[str(ll)] = [r[c],x] break else: r[c] = x a = dic[str(ll)][0] b = dic[str(ll)][1] print a,b s.send('({},{})'.format(a,b)+'\n') def main(): dic = { '15':["ASIS{wpQ78d6lk}","ASIS{4LEVv9no8}"], '16':['ASIS{XJKqgpXG3h}','ASIS{2vIy7kh3I1}'], '17': ['ASIS{N3oUn2fNYDe}', 'ASIS{cEqP0TLR0ep}'], '18': ['ASIS{Yvc6vEOVKcqg}', 'ASIS{jqgNv5MplomO}'], '19': ['ASIS{HBGExWcsHkjIJ}', 'ASIS{001ZSSGoPajSa}'], '20': ['ASIS{vIOUyVByrhVNiu}', 'ASIS{v5otW7kNsZ2gdG}'], '21': ['ASIS{0I0cdxHShRxhNfh}', 'ASIS{ctvedzhS29uwdJj}'], '22': ['ASIS{uzC5dEMHRQt4VFNg}', 'ASIS{ATEJH1PDf4d19l4A}'], '23': ['ASIS{2URJbCqOry560DLB3}', 'ASIS{lDR7VMpKSNQ8I6SLf}'], '24': ['ASIS{yO2n6qyDdfPba8sB0a}', 'ASIS{3BC6wb9SnJ0B6F2KWC}'], '25': ['ASIS{tqRfvUiKKpouaEixLND}', 'ASIS{FhaNaHZvWzkG4guZgog}'], '26': ['ASIS{mujJoqinfBNiMuE5xCjX}', 'ASIS{PDAQbu8qsWFsjyq5qjDG}'], '27': ['ASIS{CFOsCBguwBrrjhj4dNbXt}', 'ASIS{wcaEpHuL34P9PhMx4EeVb}'], '28': ['ASIS{Y1tZFZ4y08jjF0VV2vVD67}', 'ASIS{vK2T2EzNaL489acYZjkvHg}'], '29': ['ASIS{XiCWA7vZnDsi8hGQMybac0G}', 'ASIS{F08rynuBCChtJcfF0lk2t7y}'] } while True: print dic if exploit(dic) == 0: continue if __name__ == '__main__': main()
他の問題
気が向いたら...