読者です 読者をやめる 読者になる 読者になる

SSCTF 2016 Quals Writeup

CTF writeup

この大会は2016/2/27 9:00(JST)~2016/2/29 9:00(JST)に開催されました。
今回も個人で参戦。結果は110点で259位でした。
参加チームは2921チームいたので、まあまあの順位でした。
解けた問題2問をWriteupとして書いておきます。

Welcome (Misc 10)

ただリンク先が書かれている。リンク先はこんなページ。
f:id:satou-y:20160314222658p:plain

よく見てもフラグは見たらない。
終了間近にヒント"look at https://twitter.com/01001000entai"が出ていた。
そのURLにアクセスすると、フラグが載っていた。
f:id:satou-y:20160314223303p:plain

SSCTF{WelcOme_TO_S3CTF_2o16}

HeHeDa (Crypto&Exploit 100)

暗号のソースが添付されている。

def LShift(t, k):
    k %= 8
    return ((t << k) | (t >> (8 - k))) & 0xff


def encode(p):
    ret = ""
    for i in range(8):
        ret = ('|' if (p >> i) & 1 else 'O') + ret
    return ret


A = [85, 128, 177, 163, 7, 242, 231, 69, 185, 1, 91, 89, 80, 156, 81, 9, 102, 221, 195, 33, 31, 131, 179, 246, 15, 139, 205, 49, 107, 193, 5, 63, 117, 74, 140, 29, 135, 43, 197, 212, 0, 189, 218, 190, 112, 83, 238, 47, 194, 68, 233, 67, 122, 138, 53, 14, 35, 76, 79, 162, 145, 51, 90, 234, 50, 6, 225, 250, 215, 133, 180, 97, 141, 96, 20, 226, 3, 191, 187, 57, 168, 171, 105, 113, 196, 71, 239, 200, 254, 175, 164, 203, 61, 16, 241, 40, 176, 59, 70, 169, 146, 247, 232, 152, 165, 62, 253, 166, 167, 182, 160, 125, 78, 28, 130, 159, 255, 124, 153, 56, 58, 143, 150, 111, 207, 206, 32, 144,
     75, 39, 10, 201, 204, 77, 104, 65, 219, 98, 210, 173, 249, 13, 12, 103, 101, 21, 115, 48, 157, 147, 11, 99, 227, 45, 202, 158, 213, 100, 244, 54, 17, 161, 123, 92, 181, 243, 184, 188, 84, 95, 27, 72, 106, 192, 52, 44, 55, 129, 208, 109, 26, 24, 223, 64, 114, 19, 198, 23, 82, 120, 142, 178, 214, 186, 116, 94, 222, 86, 251, 36, 4, 248, 132, 25, 211, 199, 30, 87, 60, 127, 155, 41, 224, 151, 237, 136, 245, 37, 170, 252, 8, 42, 209, 46, 108, 88, 183, 149, 110, 66, 235, 229, 134, 73, 38, 118, 236, 119, 154, 216, 217, 240, 22, 121, 174, 93, 126, 230, 228, 18, 148, 220, 172, 2, 137, 34]
B = [0, 2, 3, 7, 1, 5, 6, 4]
C = [179, 132, 74, 60, 94, 252, 166, 242, 208, 217, 117, 255, 20, 99, 225, 58, 54, 184, 243, 37, 96, 106, 64, 151, 148, 248, 44, 175, 152, 40, 171, 251, 210, 118, 56, 6, 138, 77, 45, 169, 209, 232, 68, 182, 91, 203, 9, 16, 172, 95, 154, 90, 164, 161, 231, 11, 21, 3, 97, 70, 34, 86, 124, 114, 119, 223, 123, 167, 47, 219, 197, 221, 193, 192, 126, 78, 39, 233, 4, 120, 33, 131, 145, 183, 143, 31, 76, 121, 92, 153, 85, 100, 52, 109, 159, 112, 71, 62, 8, 244, 116, 245, 240, 215, 111, 134, 199, 214, 196, 213, 180, 189, 224, 101, 202, 201, 168, 32, 250, 59, 43, 27, 198, 239, 137, 238, 50,
     149, 107, 247, 7, 220, 246, 204, 127, 83, 146, 147, 48, 17, 67, 23, 93, 115, 41, 191, 2, 227, 87, 173, 108, 82, 205, 49, 1, 66, 105, 176, 22, 236, 29, 170, 110, 18, 28, 185, 235, 61, 88, 13, 165, 188, 177, 230, 130, 253, 150, 211, 42, 129, 125, 141, 19, 190, 133, 53, 84, 140, 135, 10, 241, 222, 73, 12, 155, 57, 237, 181, 36, 72, 174, 207, 98, 5, 229, 254, 156, 178, 128, 55, 14, 69, 30, 194, 122, 46, 136, 160, 206, 26, 102, 218, 103, 139, 195, 0, 144, 186, 249, 79, 81, 75, 212, 234, 158, 163, 80, 226, 65, 200, 38, 187, 113, 63, 24, 25, 142, 51, 228, 35, 157, 216, 104, 162, 15, 89]
D = [2, 4, 0, 5, 6, 7, 1, 3]

plain = bytearray("asdfghjk123456")
key = bytearray(/*Missed*/)
assert len(key) == 8
t1 = bytearray()
for i in plain:
    t1.append(A[i])
t2 = bytearray()
for i in range(len(t1)):
    t2.append(LShift(t1[i], B[i % 8]))
for times in range(16):
    for i in range(len(t2)):
        t2[i] = C[t2[i]]
    for i in range(len(t2)):
        t2[i] = LShift(t2[i], i ^ D[i % 8])
    for i in range(len(t2)):
        t2[i] ^= key[i % 8]
out = ""
for i in t2:
    out += encode(i)
print out

# out>>
# OO|OO||OO|||||OO|OO||O||O|O||O|||O|OOOOOOO|O|O|O|||||OO|||O|||OO||O|OOOOOO|O|OO|OO||||OO|||OOOO|||||O||||O|OO|O|O|O||OO|O||O|OO|O||O|||O||O|OO|OOOOOO||OOO|O|O|O|||O|OO|O|O||O||O||OOOOO|||OO|O|

# flag >>
# OO||O||O|O|||OOOO||||||O|O|||OOO||O|OOOO||O|O|OO|||||OOOO||||O||OO|OO||O||O|O|O|||||OOOOOO|O|O||OOOOOOO||O|||OOOO||OO|OO|||O|OO|O|||O|O|OO|OOOO|OOO|OOO|OOOO||O|OO||||OO||||OOO|O|O||OO||||O||OOO|||O|OO|OO||OO||OOOO|O|

これからflagを復号する問題。
この暗号のkeyは8バイトで、1バイト単位で計算している。
outでもflagでも8バイトごとに同じ文字で計算しているものを探すスクリプトを作る。

import string

def LShift(t, k):
    k %= 8
    return ((t << k) | (t >> (8 - k))) & 0xff

def encode(p):
    ret = ""
    for i in range(8):
        ret = ('|' if (p >> i) & 1 else 'O') + ret
    return ret

A = [85, 128, 177, 163, 7, 242, 231, 69, 185, 1, 91, 89, 80, 156, 81, 9, 102, 221, 195, 33, 31, 131, 179, 246, 15, 139, 205, 49, 107, 193, 5, 63, 117, 74, 140, 29, 135, 43, 197, 212, 0, 189, 218, 190, 112, 83, 238, 47, 194, 68, 233, 67, 122, 138, 53, 14, 35, 76, 79, 162, 145, 51, 90, 234, 50, 6, 225, 250, 215, 133, 180, 97, 141, 96, 20, 226, 3, 191, 187, 57, 168, 171, 105, 113, 196, 71, 239, 200, 254, 175, 164, 203, 61, 16, 241, 40, 176, 59, 70, 169, 146, 247, 232, 152, 165, 62, 253, 166, 167, 182, 160, 125, 78, 28, 130, 159, 255, 124, 153, 56, 58, 143, 150, 111, 207, 206, 32, 144,
     75, 39, 10, 201, 204, 77, 104, 65, 219, 98, 210, 173, 249, 13, 12, 103, 101, 21, 115, 48, 157, 147, 11, 99, 227, 45, 202, 158, 213, 100, 244, 54, 17, 161, 123, 92, 181, 243, 184, 188, 84, 95, 27, 72, 106, 192, 52, 44, 55, 129, 208, 109, 26, 24, 223, 64, 114, 19, 198, 23, 82, 120, 142, 178, 214, 186, 116, 94, 222, 86, 251, 36, 4, 248, 132, 25, 211, 199, 30, 87, 60, 127, 155, 41, 224, 151, 237, 136, 245, 37, 170, 252, 8, 42, 209, 46, 108, 88, 183, 149, 110, 66, 235, 229, 134, 73, 38, 118, 236, 119, 154, 216, 217, 240, 22, 121, 174, 93, 126, 230, 228, 18, 148, 220, 172, 2, 137, 34]
B = [0, 2, 3, 7, 1, 5, 6, 4]
C = [179, 132, 74, 60, 94, 252, 166, 242, 208, 217, 117, 255, 20, 99, 225, 58, 54, 184, 243, 37, 96, 106, 64, 151, 148, 248, 44, 175, 152, 40, 171, 251, 210, 118, 56, 6, 138, 77, 45, 169, 209, 232, 68, 182, 91, 203, 9, 16, 172, 95, 154, 90, 164, 161, 231, 11, 21, 3, 97, 70, 34, 86, 124, 114, 119, 223, 123, 167, 47, 219, 197, 221, 193, 192, 126, 78, 39, 233, 4, 120, 33, 131, 145, 183, 143, 31, 76, 121, 92, 153, 85, 100, 52, 109, 159, 112, 71, 62, 8, 244, 116, 245, 240, 215, 111, 134, 199, 214, 196, 213, 180, 189, 224, 101, 202, 201, 168, 32, 250, 59, 43, 27, 198, 239, 137, 238, 50,
     149, 107, 247, 7, 220, 246, 204, 127, 83, 146, 147, 48, 17, 67, 23, 93, 115, 41, 191, 2, 227, 87, 173, 108, 82, 205, 49, 1, 66, 105, 176, 22, 236, 29, 170, 110, 18, 28, 185, 235, 61, 88, 13, 165, 188, 177, 230, 130, 253, 150, 211, 42, 129, 125, 141, 19, 190, 133, 53, 84, 140, 135, 10, 241, 222, 73, 12, 155, 57, 237, 181, 36, 72, 174, 207, 98, 5, 229, 254, 156, 178, 128, 55, 14, 69, 30, 194, 122, 46, 136, 160, 206, 26, 102, 218, 103, 139, 195, 0, 144, 186, 249, 79, 81, 75, 212, 234, 158, 163, 80, 226, 65, 200, 38, 187, 113, 63, 24, 25, 142, 51, 228, 35, 157, 216, 104, 162, 15, 89]
D = [2, 4, 0, 5, 6, 7, 1, 3]

out = 'OO|OO||OO|||||OO|OO||O||O|O||O|||O|OOOOOOO|O|O|O|||||OO|||O|||OO||O|OOOOOO|O|OO|OO||||OO|||OOOO|||||O||||O|OO|O|O|O||OO|O||O|OO|O||O|||O||O|OO|OOOOOO||OOO|O|O|O|||O|OO|O|O||O||O||OOOOO|||OO|O|'
flag = 'OO||O||O|O|||OOOO||||||O|O|||OOO||O|OOOO||O|O|OO|||||OOOO||||O||OO|OO||O||O|O|O|||||OOOOOO|O|O||OOOOOOO||O|||OOOO||OO|OO|||O|OO|O|||O|O|OO|OOOO|OOO|OOO|OOOO||O|OO||||OO||||OOO|O|O||OO||||O||OOO|||O|OO|OO||OO||OOOO|O|'

d1 = {}
d2 = {}
d3 = {}
for i in range(len(out) / 8):
    for try_key in string.printable:
        for try_plain in string.printable:
            t1 = A[ord(try_plain)]
            t2 = LShift(t1, B[i % 8])
            for times in range(16):
                t2 = C[t2]
                t2 = LShift(t2, i ^ D[i % 8])
                t2 ^= ord(try_key)
            if encode(t2) == out[i*8:i*8+8]:
                index1 = str(i % 8).zfill(2) + ':' + try_key
                if i / 8 == 0:
                    d1[index1] = try_plain
                elif i / 8 == 1:
                    index2 = str(i).zfill(2) + ':' + try_key
                    if index1 in d1:
                        d2[index1] = d1[index1]
                        d2[index2] = try_plain
                else:
                    index2 = str(i - 8).zfill(2) + ':' + try_key
                    index3 = str(i).zfill(2) + ':' + try_key
                    if index1 in d2:
                        d3[index1] = d2[index1]
                        d3[index2] = d2[index2]
                        d3[index3] = try_plain

#for key, value in sorted(d3.items()):
#    print key, value

d4 = {}
d5 = {}
d6 = {}
d7 = {}
for i in range(len(flag) / 8):
    for try_key in string.printable:
        for try_plain in string.printable:
            t1 = A[ord(try_plain)]
            t2 = LShift(t1, B[i % 8])
            for times in range(16):
                t2 = C[t2]
                t2 = LShift(t2, i ^ D[i % 8])
                t2 ^= ord(try_key)
            if encode(t2) == flag[i*8:i*8+8]:
                index1 = str(i % 8).zfill(2) + ':' + try_key
                if i / 8 == 0:
                    index2 = str(i + 8).zfill(2) + ':' + try_key
                    index3 = str(i + 16).zfill(2) + ':' + try_key
                    index4 = str(i + 24).zfill(2) + ':' + try_key
                    if index1 in d3:
                        d4[index1] = d3[index1]
                        d4[index2] = d3[index2]
                        d4[index3] = d3[index3]
                        d4[index4] = try_plain
                elif i / 8 == 1:
                    index2 = str(i).zfill(2) + ':' + try_key
                    index3 = str(i + 8).zfill(2) + ':' + try_key
                    index4 = str(i + 16).zfill(2) + ':' + try_key
                    index5 = str(i + 24).zfill(2) + ':' + try_key
                    if index1 in d4:
                        d5[index1] = d4[index1]
                        d5[index2] = d4[index2]
                        d5[index3] = d4[index3]
                        d5[index4] = d4[index4]
                        d5[index5] = try_plain
                elif i / 8 == 2:
                    index2 = str(i - 8).zfill(2) + ':' + try_key
                    index3 = str(i).zfill(2) + ':' + try_key
                    index4 = str(i + 8).zfill(2) + ':' + try_key
                    index5 = str(i + 16).zfill(2) + ':' + try_key
                    index6 = str(i + 24).zfill(2) + ':' + try_key
                    if index1 in d5:
                        d6[index1] = d5[index1]
                        d6[index2] = d5[index2]
                        d6[index3] = d5[index3]
                        d6[index4] = d5[index4]
                        d6[index5] = d5[index5]
                        d6[index6] = try_plain
                else:
                    index7 = str(i + 24).zfill(2) + ':' + try_key
                    if index1 in d6:
                        d6.update({index7:try_plain})

for key, value in sorted(d6.items()):
    print key, value

これを実行すると、次のような結果となる。

00:^ a
01:& s
02:# d
03:q f
04:D g
05:9 h
06:3 j
07:_ k
08:^ 1
09:& 2
10:# 3
11:q 4
12:D 5
13:9 6
14:3 7
15:_ 8
16:^ !
17:& @
18:# #
19:q $
20:D %
21:9 ^
22:3 &
23:_ *
24:^ S
25:& S
26:# C
27:q T
28:D F
29:9 {
30:3 1
31:_ q
32:^ a
33:& z
34:# 9
35:q o
36:D l
37:9 .
38:3 n
39:_ h
40:^ y
41:& 6
42:# 4
43:q r
44:D f
45:9 v
46:3 7
47:_ u
48:^ j
49:& m
50:# }
SSCTF{1qaz9ol.nhy64rfv7ujm}