魚脳の池

CTF:Little Twoos

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()

他の問題

気が向いたら...