Writeup for Crypto Problems in hgame 2020

Week1

InfantRSA 50pt

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# -*- coding: utf-8 -*-

# 真*签到题
# p = 681782737450022065655472455411;
# q = 675274897132088253519831953441;
# e = 13;
# c = pow(m,e,p*q) = 275698465082361070145173688411496311542172902608559859019841

from Crypto.Util.number import *

p = 681782737450022065655472455411
q = 675274897132088253519831953441
e = 13
c = 275698465082361070145173688411496311542172902608559859019841

print(long_to_bytes(pow(c, inverse(e, (p-1)*(q-1)), p*q)))

# b'hgame{t3Xt6O0k_R5A!!!}'

Affine 75pt

Affine_task.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import gmpy2
from secret import A, B, flag
assert flag.startswith('hgame{') and flag.endswith('}')

TABLE = 'zxcvbnmasdfghjklqwertyuiop1234567890QWERTYUIOPASDFGHJKLZXCVBNM'
MOD = len(TABLE)

cipher = ''
for b in flag:
    i = TABLE.find(b)
    if i == -1:
        cipher += b
    else:
        ii = (A*i + B) % MOD
        cipher += TABLE[ii]

print(cipher)
# A8I5z{xr1A_J7ha_vG_TpH410}

exp.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from Crypto.Util.number import *

TABLE = 'zxcvbnmasdfghjklqwertyuiop1234567890QWERTYUIOPASDFGHJKLZXCVBNM'
m = len(TABLE)
cton = lambda x: TABLE.index(x)
ntoc = lambda x: TABLE[x]

# cipher:  A8I5z{xr1A_J7ha_vG_TpH410}
# message: hgame{???????????????????}
# y1 = A*x1 + B (mod m)
# y2 = A*x2 + B (mod m)
x1, x2 = cton('h'), cton('g')
y1, y2 = cton('A'), cton('8')

# A = (y1-y2) * (x1-x2)^-1
# B = y1 - A*x1
A = (y1-y2) * inverse(x1-x2, m)
B = (y1 - A*x1) % m
print(A, B) # 13 14

for c in 'A8I5z{xr1A_J7ha_vG_TpH410}':
    if c in TABLE:
        y = cton(c)
        # x = (y-B) * A^-1
        x = ntoc(((y-B) * inverse(A, m)) % m)
    else:
        x = c
    print(x, end='')
# hgame{M4th_u5Ed_iN_cRYpt0}

not_One-time 150pt

not_One-time_task.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os, random
import string, binascii, base64


from secret import flag
assert flag.startswith(b'hgame{') and flag.endswith(b'}')

flag_len = len(flag)

def xor(s1, s2):
    #assert len(s1)==len(s2)
    return bytes( map( (lambda x: x[0]^x[1]), zip(s1, s2) ) )

random.seed( os.urandom(8) )
keystream = ''.join( [ random.choice(string.ascii_letters+string.digits) for _ in range(flag_len) ] )
keystream = keystream.encode()
print( base64.b64encode(xor(flag, keystream)).decode() )

Flag重用可还行。

  • 可以获取任意数量密文

  • key space仅26*2+10=62


这里用了一个统计方法。

先获取很多个密文c,将其他密文和第一个密文异或,消去flag,得到的就只有ki = key0 ^ keyi

我们的目标是要获得这个key0

长度都是43。

考虑key0的第i位字母,只能是string.ascii_letters+string.digits中的某一个ch

那么这个特定的ch与其他keyi对应位置的字母异或得到的结果,就只有62种可能,将其看作一个集合set_ch

且不同的ch得到的结果集合set_ch中至少是会有几个元素是不同的,即不同的ch得到的结果集合不相同。

那么,(当我们掌握足够多的数据时)我们就可以通过每一位字母得到的结果集合,去反推这个字母是什么。

例如:如果key0i位字母是a,那么ki = key0 ^ keyi的所有62种可能的结果就是set_a = {a^a, a^b, ..., a^A, a^B, ..., a^0, ..., a^9};如果key0i位字母是b,那么ki = key0 ^ keyi的所有62种可能的结果就是set_b = {b^a, b^b, ..., b^A, b^B, ..., b^0, ..., b^9};。。。。。。

且这些set_a != set_b != ... !=set_9

在第i位,如果我们得到的结果集合是set_a,那么我们就可以推断出这一位的字母一定是a

可能画图会更清晰一些。

算一些没用的:1000组数据,这个结果集合的长度不满62的概率。

也就是说有一个位置,1000次都没选中。 $$ P = C_{62}^{1} (1 - \frac{1}{62})^{1000} \approx 5 \times 10^{-6} $$ 所以1000组数据足够了


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import string
import base64
from pwn import *


def XOR(s1, s2):
    #assert len(s1)==len(s2)
    return bytes(map((lambda x: x[0] ^ x[1]), zip(s1, s2)))

key_space = bytes(string.ascii_letters + string.digits, encoding='utf-8')

# Construct dict.
l = []
for x in key_space:
    s = set()
    for y in key_space:
        s.add(x ^ y)
    l.append(s)


# ci = flag ^ keyi
all_c = []
for i in range(1001):
    r = remote('47.98.192.231', 25001)
    all_c.append(base64.b64decode(r.recvall()))
    r.close()

# Goal: recover key0
xored_keys = []  # [key0 ^ keyi for i in range(1, 1001)]
for c in all_c[1:]:
    xored_keys.append( XOR(all_c[0], c) )

# byte-by-byte
key0 = b''
for i in range(len(xored_keys[0])):
    s = set(xored_key[i] for xored_key in xored_keys) # keyi[i]
    for index in range(len(key_space)):
        if s <= l[index]:
            key0 += bytes([ key_space[index] ])
print(key0, XOR(key0, all_c[0]))

# hgame{r3us1nG+M3$5age-&&~rEduC3d_k3Y-5P4Ce}

Reorder 75pt

Input: hgame{ABCDEFGHIJKLMNOPQRSTUVWXY}

Output: {hHDCFABGemEaJIgPKXTSVQRWONUM}YL

Encrypted flag: {hmt$5jUIem+aLpgm3!TA0uTnReiP}!_

1
2
3
4
5
6
7
8
c = '{hmt$5jUIem+aLpgm3!TA0uTnReiP}!_'

x = 'hgame{ABCDEFGHIJKLMNOPQRSTUVWXY}'
y = '{hHDCFABGemEaJIgPKXTSVQRWONUM}YL'

for i in x:
    print(c[y.index(i)], end='')
# hgame{jU$t+5ImpL3_PeRmuTATi0n!!}

Week2

Verification_code 125pt

Verification_code.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import socketserver
import os, sys, signal
import string, random
from hashlib import sha256

from secret import FLAG

class Task(socketserver.BaseRequestHandler):
    def _recvall(self):
        BUFF_SIZE = 2048
        data = b''
        while True:
            part = self.request.recv(BUFF_SIZE)
            data += part
            if len(part) < BUFF_SIZE:
                break
        return data.strip()

    def send(self, msg, newline=True):
        try:
            if newline: msg += b'\n'
            self.request.sendall(msg)
        except:
            pass

    def recv(self, prompt=b'> '):
        self.send(prompt, newline=False)
        return self._recvall()

    def proof_of_work(self):
        random.seed( os.urandom(8) )
        proof = ''.join([ random.choice(string.ascii_letters+string.digits) for _ in range(20) ])
        _hexdigest = sha256(proof.encode()).hexdigest()
        self.send(str.encode( "sha256(XXXX+%s) == %s" % (proof[4:],_hexdigest) ))
        x = self.recv(prompt=b'Give me XXXX: ')
        if len(x) != 4 or sha256(x+proof[4:].encode()).hexdigest() != _hexdigest:
            return False
        return True

    def handle(self):
        signal.alarm(60)
        if not self.proof_of_work():
            return
        self.send(b'The secret code?')
        _code = self.recv()
        if _code == b'I like playing Hgame':
            self.send(b'Ok, you find me.')
            self.send(b'Here is the flag: ' + FLAG)
            self.send(b'Bye~')
        else:
            self.send(b'Rua!!!')
        self.request.close()

class ThreadedServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    pass

class ForkedServer(socketserver.ForkingMixIn, socketserver.TCPServer):
    pass

if __name__ == "__main__":
    HOST, PORT = '0.0.0.0', 1234
    server = ForkedServer((HOST, PORT), Task)
    server.allow_reuse_address = True
    server.serve_forever()

exp.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#nc 47.98.192.231 25678
import re
from hashlib import sha256
from itertools import product

from pwn import *

s = string.ascii_letters + string.digits

r = remote('47.98.192.231', 25678)

rec = r.recvline().decode()

suffix = re.findall(r'\(XXXX\+(.*?)\)', rec)[0]
digest = re.findall(r'== (.*?)\n', rec)[0]
print(f"suffix: {suffix} \ndigest: {digest}")

print('Calculating hash...')
for i in product(s, repeat=4):
    prefix = ''.join(i)
    guess = prefix + suffix
    if sha256(guess.encode()).hexdigest() == digest:
        print(guess)
        break
r.sendafter(b'Give me XXXX: ', prefix.encode())

r.sendafter(b'The secret code?\n', b'I like playing Hgame')

r.interactive()
# hgame{It3Rt0O|S+I5_u$3fu1~Fo2_6rUtE-f0Rc3}

Remainder 150pt

Remainder_task.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from Crypto.Util import number
from secret import msg

assert 256 < len(msg) < 384

p, q, r = [number.getPrime(1024) for _ in range(3)]
# p = 94598296305713376652540411631949434301396235111673372738276754654188267010805522542068004453137678598891335408170277601381944584279339362056579262308427544671688614923839794522671378559276784734758727213070403838632286280473450086762286706863922968723202830398266220533885129175502142533600559292388005914561
# q = 150088216417404963893679242888992998793257903343994792697939121738029477790454833496600101388493792476973514786401036309378542808470513073408894727406158296404360452232777491992630316999043165374635001806841520490997788796152678742544032835808854339130676283497122770901196468323977265095016407164510827505883
# r = 145897736096689096151704740327665176308625097484116713780050311198775607465862066406830851710261868913835866335107146242979359964945125214420821146670919741118254402096944139483988745450480989706524191669371208210272907563936516990473246615375022630708213486725809819360033470468293100926616729742277729705727

m = number.bytes_to_long(msg)
e = 65537
for prime in [p, q, r]:
    print( pow(m, e, prime) )

# 78430786011650521224561924814843614294806974988599591058915520397518526296422791089692107488534157589856611229978068659970976374971658909987299759719533519358232180721480719635602515525942678988896727128884803638257227848176298172896155463813264206982505797613067215182849559356336015634543181806296355552543
# 49576356423474222188205187306884167620746479677590121213791093908977295803476203510001060180959190917276817541142411523867555147201992480220531431019627681572335103200586388519695931348304970651875582413052411224818844160945410884130575771617919149619341762325633301313732947264125576866033934018462843559419
# 48131077962649497833189292637861442767562147447040134411078884485513840553188185954383330236190253388937785530658279768620213062244053151614962893628946343595642513870766877810534480536737200302699539396810545420021054225204683428522820350356470883574463849146422150244304147618195613796399010492125383322922

关键在于assert 256 < len(msg) < 384,也就是说m在2048~3072bit之间。

但是p, q, r只有1024bit,显然只用其中一个是算不出来m的,需要把模数扩大下。


exp.sage

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# sage 8.9
e = 65537
p = 94598296305713376652540411631949434301396235111673372738276754654188267010805522542068004453137678598891335408170277601381944584279339362056579262308427544671688614923839794522671378559276784734758727213070403838632286280473450086762286706863922968723202830398266220533885129175502142533600559292388005914561
q = 150088216417404963893679242888992998793257903343994792697939121738029477790454833496600101388493792476973514786401036309378542808470513073408894727406158296404360452232777491992630316999043165374635001806841520490997788796152678742544032835808854339130676283497122770901196468323977265095016407164510827505883
r = 145897736096689096151704740327665176308625097484116713780050311198775607465862066406830851710261868913835866335107146242979359964945125214420821146670919741118254402096944139483988745450480989706524191669371208210272907563936516990473246615375022630708213486725809819360033470468293100926616729742277729705727

c1 = 78430786011650521224561924814843614294806974988599591058915520397518526296422791089692107488534157589856611229978068659970976374971658909987299759719533519358232180721480719635602515525942678988896727128884803638257227848176298172896155463813264206982505797613067215182849559356336015634543181806296355552543
c2 = 49576356423474222188205187306884167620746479677590121213791093908977295803476203510001060180959190917276817541142411523867555147201992480220531431019627681572335103200586388519695931348304970651875582413052411224818844160945410884130575771617919149619341762325633301313732947264125576866033934018462843559419
c3 = 48131077962649497833189292637861442767562147447040134411078884485513840553188185954383330236190253388937785530658279768620213062244053151614962893628946343595642513870766877810534480536737200302699539396810545420021054225204683428522820350356470883574463849146422150244304147618195613796399010492125383322922

c = crt([c1,c2,c3], [p,q,r])
m = pow(c, inverse_mod(e, (p-1)*(q-1)*(r-1)), p*q*r)

flag = ''
for line in ('0'+hex(Integer(m))).decode('hex').split('\n'):
    flag += line[:2]
print flag
# hgame{CrT_w0Nt+6Oth3R_mE!!!}

Inv 175pt

有点意思,Subs也能当作一个运算。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import gmpy2
from secret import Sbox, flag
assert set(range(256)) == set(Sbox)
assert flag.startswith(b'hgame{') and flag.endswith(b'}')

def Subs(S, X):
    return bytes([ S[x] for x in X ])

def Mul(A, B):
    assert len(A)==len(B)
    return Subs(A, B)

def Pow(X, E):
    Y = X
    E = bin(E)[3:]
    for e in E:
        Y = Mul(Y, Y)
        if e=='1':
            Y = Mul(X, Y)
    return Y

s = bytes(Sbox)

print( Pow(s, 595) )
print( Pow(s, 739) )
print( Subs(s, flag) )

# b'\xc8&\xc1E\xbe*\xc5\x84\xdb1\x05\x9b\xc0\xf2\xac/\x0b0\x8d\'\xc2b\x89\x93\xa6\xcd\xe1\x1b\xf4H\xffa\x90A\xf7,(\xea?\xa8\xa0\x8b\xf1\xf9"X\n\x86fj\x074\x7fBO\xd4F\xbd\xe6\xd9\xa7\xaf\x8a\x8c\xde\xab;!PT\x15)+w\xbc\x00>\xc6g\xc3\x85=9K\xb6<\xb7x\xaeUG\x83vk\xa9\xf6{\x03Y\x87\x14e\xfd\xed@i\xcc.\xd1\xebo\x106\xe2\xe7\xd7\xeeu\x9e\xfe\x95^R\xfb8\x04\xb4S\x16\xe0\xad\xd8\x98n\xca\xe4\xdd\xd2\xc7\x99l\xb3\\2L\xa3\x1d:_\x12\xb87\x17\x01\xb1#~q\x1c\t\xe8\xdar\xef\xcb\x0c\xe5\x80\xdf\xc9y\x0e`\xe9\x94\xd0\xcfW\x1f5\xf5h\xbf\xba\x91\xb9d\xfcM\x81\xec\x88\xb2c\x9f\xa4J|\xd3m\xd6s\xd5\x92\x9d\x9a3\xa2\xb5\xfa\x19N\xa1\x82][\xf8\x06\x13\xdcC\x1e\x1a\xaa\xc4tz\x08\x8f%$D\xbb\x97 \xce\x96V\xe3\x02I\x18\x11\x0f\r\xf3p\x8e\xa5Z-\xf0}\xb0Q\x9c'
# b'U\x17\x8aE\xa6\x19\xab\x7fd0\xd2)\xc0\xae\xcc/G_\xe3\'\r\xfb\xaf\x00\xb1hgi-\xc1\xffa\x8d\t&\x99k\x95\x93\xa8.\x07\xcd\x87\x01\xe8\x89\x86\xf6f48F\xdc\x96\xd4`P\xd6!\xfe\xc4B:\xd31C\x9f\x1dT{2c9\x0bY5#\xf7\xb8H\xe0Db\xb6wv\xe1\xbbI\x8f\x83l\x80\xa9\x04q\x03\xf0m\xf4\x1bp\x8e\xc6u\xfd\x16$\x06\xf9Z\xec\xa2\xcb\xd7V\xb9\xd1\xbdt^\xe7\xe2\xac\x18\xb4\x15=n\xad\xd8S+\xca\xeb\xdd\xd0;\x84\xe6\x08\x8c3\xb3\x90\x02\xc8}\xee\xea7K\x98\xde\x8b~\xcf\xfa\x11\n\xda\xa4L\xa3\x0cWQ\xdf\xc9yj\x9d\xe9\xfcJO\x1a\x1f\xdb\xf5M\xbf\x9e e\x1c*\x9b\x85\xe5\x88\xb2\xc7\xf2\x91\x10\x0e,\xd9<s\xd5\xef\xb0@|\xc3\xbc(\xb5"\xa1\x82\xa7[\xf8A\x13\x14\xc2\x1eN\xaao\xedr\xba\xcex]\x92\x05\x97\x12\xc5%\\\xb7>R\x9a\x94\x0fX\xf3\xbe?\xa5\xe4\xa0z\xf16\x81\x9c'
# b"\\-\xa5{\xb9\x85J @\xfa\x91\x0b\x88\r4I\x7f\xb9\xe5\r\xc0\x84\x8f\xa6\xc0i\xb0\xa4\x1b\x8fIw\x84'\xa2\xa4\x00\x91\x87\x10\r\\\x8c\x12"




# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# c1 = s^(595)
# c2 = s^(739)
def egcd(a, b):
    '''
    Extended Euclidean Algorithm.
    returns x, y, gcd(a,b) such that ax + by = gcd(a,b).
    '''
    u, u1 = 1, 0
    v, v1 = 0, 1
    while b:
        q, r = divmod(a, b)
        u, u1 = u1, u - q * u1
        v, v1 = v1, v - q * v1
        a, b = b, r
    return u, v, a

a, b, g = egcd(595, 739) # (-272, 219, 1)
# x1 := c1^272 = s^161840
# x2 := c2^219 = s^161841
# s * x1 = x2
# => Subs(s, x1) == x2

s = [0]*256
for i, x in enumerate(x1):
    s[x] = x2[i]

s = bytes(s)
for i in output:
    print( chr(s.index(i)), end='')
# hgame{U_kN0w~tH3+eXtEnD-EuC1iD34n^A1G0rIthM}

notRC4 200pt

notRC4_task.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from hashlib import md5
from secret import flag
assert flag.startswith(b'hgame) and flag.endswith(b')

class RC4:
    def __init__(self):
        self.l1 = [0] * 256
        self.a = 0
        self.l2 = [0] * 256
        for i in range(256):
            self.l1[i] = i
        self.b = 0

    def fun1(self, key):
        l = len(key) # l=8
        for i in range(256):
            self.l2[i] = key[i%l]
        for i in range(256):
            self.b = ( self.b + self.l1[i] + self.l2[i] ) % 256
            self.l1[i], self.l1[self.b] = self.l1[self.b], self.l1[i]
        self.a = self.b = 0

    def fun2(self, length):
        res = []
        for _ in range(length): # 50
            self.a = ( self.a + 1 ) % 256
            self.b = ( self.b + self.l1[self.a] ) % 256
            self.l1[self.a], self.l1[self.b] = self.l1[self.b], self.l1[self.a]
            t = ( self.l1[self.a] + self.l1[self.b] ) % 256
            res.append( self.l1[t] )
        print(self.l1)
        return res


def xor(s1, s2):
    return bytes(map( (lambda x: x[0]^x[1]), zip(s1, s2) ))

def enc(msg):
    Oo0oO = RC4()
    Oo0oO.fun1( md5(msg).digest()[:8] )
    O0O = Oo0oO.fun2( len(msg) )
    return xor(msg, O0O)

print( enc(flag) )

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

逆一下fun2

exp.py

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

t = 228
keys = [27]
a = 50
b = l1.index((t - l1[a]) % 256)
while a >1:
    l1[a], l1[b] = l1[b], l1[a]
    b = (b - l1[a]) % 256
    a -= 1
    t = (l1[a] + l1[b]) % 256
    keys.append(l1[t])
print(keys)
xor(c, keys[::-1])
# b'hgame{0o00Oo00_~reVeRSe_tHE~prGA_0F-Rc4-+0OoOOOOo}'

Week3

RSA? 150pt

RSA_task.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from Crypto.Util.number import *
import gmpy2
from flag import flag

p = getPrime(256)
q = getPrime(256)
r = getPrime(512)
e = 2
m = bytes_to_long(flag)

n = p * q + r
c = pow(m, e, n)

with open('task', 'w') as f:
    f.write("c =  {}\n".format(str(c)))
    f.write("e =  {}\n".format(str(e)))
    f.write("q =  {}\n".format(str(q)))
    f.write("n =  {}\n".format(str(n)))

# c =  2166906408965390116437761185603075699086315246646982132520792868301615462732707695943058783211233589602162482847874299962577512765886861573898075548033678
# e =  2
# q =  95476159140358852660572143425801843414366266400633576268276298731539374363607
# n =  7236834786093916009325417235344284284391050287273422058348241360770131423240807259717864686885012301293921328397391157761688776563780348734483657156421779

n = p*q + r迷惑行为。

看了一下,n是一个素数,GF(n)上开平方根。

exp.sage

1
2
3
4
5
6
7
# sage 8.9
c = ...
n = ...

m = sqrt(GF(n)(c))
print hex(Integer(m)).decode('hex')
# hgame{eaa5262c-4631-46ef-a97b-53277ab7e1d8}

ToyCipher_XorShift 175pt

task.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import binascii


from secret import FLAG


BLOCKSIZE = 8
BITSLENGTH = 8*BLOCKSIZE
MASK = (1 << BITSLENGTH) - 1

BLOCKS = lambda data: [ bytes(data[i*BLOCKSIZE:(i+1)*BLOCKSIZE]) for i in range(len(data)//BLOCKSIZE) ]
XOR = lambda s1, s2: bytes([x^y for x,y in zip(s1, s2)])


def pad(data):
    pad_len = BLOCKSIZE - (len(data) % BLOCKSIZE)
    return data + bytes( [pad_len] * pad_len )


def f(x, a, shr=True):
    x = x & MASK
    a = a % BITSLENGTH
    if shr:
        x ^= x >> a
    else:
        x ^= x << a
    return x & MASK


def enc(block):
    block = int.from_bytes(block, byteorder='big')
    block = f(block, 13, shr=False)
    block = f(block,  7, shr=True )
    block = f(block, 17, shr=False)
    return block.to_bytes(BLOCKSIZE, byteorder='big')


def encrypt(msg, iv):
    msg = pad(msg)
    mid = iv
    cipher = b''
    for block in BLOCKS(msg):
        mid = enc( XOR(mid, block) )
        cipher += mid
    return cipher


def decrypt(cipher, iv, unpad=False):
    assert len(cipher)%BLOCKSIZE == 0
    pass

IV = b'c8C~M0d3'
ciphertext = encrypt(FLAG, IV)
print( ciphertext.hex() )

# 15eb80358fe6f89b1802a5f3eb5a6ec6c33dc4f35822fb6e97e0b22be860a28602b35e2930a93ac5

就逆一个xor shift

exp.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
BLOCKSIZE = 8
BITSLENGTH = 8*BLOCKSIZE
MASK = (1 << BITSLENGTH) - 1

BLOCKS = lambda data: [ bytes(data[i*BLOCKSIZE:(i+1)*BLOCKSIZE]) for i in range(len(data)//BLOCKSIZE) ]
XOR = lambda s1, s2: bytes([x^y for x,y in zip(s1, s2)])


def UnshiftRightXor(x, a):
    # block-by-block
    mask = (1<<a) - 1
    i = BITSLENGTH - a
    while i>0:
        tmp = (x>>i) & mask
        i -= a
        if i >= 0:
            x ^= tmp << i
        else:
            x ^= tmp >> (-i)
    return x

def UnshiftLeftXor(x, a):
    # bit-by-bit
    i = a
    while i < BITSLENGTH:
        known_bit = x & (1 << (i-a))
        x ^= known_bit << a
        i += 1
    return x

def dec(block):
    block = int.from_bytes(block, byteorder='big')
    block = UnshiftLeftXor(block, 17)
    block = UnshiftRightXor(block, 7)
    block = UnshiftLeftXor(block, 13)
    return block.to_bytes(BLOCKSIZE, byteorder='big')


def decrypt(c, iv):
    mid = iv
    m = b''
    for block in BLOCKS(c):
        m += XOR( dec(block), mid )
        mid = block
    return m

IV = b'c8C~M0d3'
c = bytes.fromhex('15eb80358fe6f89b1802a5f3eb5a6ec6c33dc4f35822fb6e97e0b22be860a28602b35e2930a93ac5')

print(decrypt(c, IV))
# b'hgame{tHi$+4lgOr1thM_i5_3@sY-t0~b2EaK}\x02\x02'

Exchange 150pt

中间人攻击,就是写脚本费时间。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# Our admin hijacked a secret channel and it looks like there are two teams doing some unspeakable transactions.
# nc 47.98.192.231 25258
from hashlib import sha256
from itertools import product
from Crypto.Util.number import *
from pwn import *
import string
import re


s = string.ascii_letters + string.digits

r = remote('47.98.192.231', 25258)

context.log_level = 'debug'

# Proof of work
rec = r.recvline().decode()

suffix = re.findall(r'\(XXXX\+(.*?)\)', rec)[0]
digest = re.findall(r'== (.*?)\n', rec)[0]
print(f"suffix: {suffix} \ndigest: {digest}")

print('Calculating hash...')
for i in product(s, repeat=4):
    prefix = ''.join(i)
    guess = prefix + suffix
    if sha256(guess.encode()).hexdigest() == digest:
        print(guess)
        break
r.sendafter(b'Give me XXXX: ', prefix.encode())
print("Proof of work passed!")

# p, g
r.sendafter(b"Alice: Let's do Key Exchange first.\n", b'\n')
rec = b''.join(r.recvlines(6, keepends=True)).decode()
print(rec)
p = int(re.findall(r'p = ([0-9]*)\n', rec)[0])
g = int(re.findall(r'g = ([0-9]*)\n', rec)[0])
print(f"p = {p}\ng = {g}")

# A
r.send(b'\n')
r.recvlines(5)
r.send(b'\n')
rec = r.recvuntil(b'> ').decode()
A = int(re.findall(r'A = ([0-9]*)\n', rec)[0])
print(f"A = {A}")


# Impersonate Alice
my_a = 0x1337
my_A = pow(g, my_a, p)
print(f"my_A = {my_A}")
my_b = 0x7331
my_B = pow(g, my_b, p)
print(f"my_B = {my_B}")

r.sendline(b'yes')
r.sendafter(b"> ", str(my_A).encode())
r.recvlines(3)
r.send(b"\n")

# B
rec = r.recvuntil(b"> ").decode()
B = int(re.findall(r'B = ([0-9]*)\n', rec)[0])
print(f"B = {B}")

# Impersonate Bob
r.sendline(b'yes')
r.sendafter(b"> ", str(my_B).encode())
r.recvlines(3)
r.send(b"\n")

# two share keys
S_a = pow(A, my_b, p)
inv_a = inverse(S_a, p)
S_b = pow(B, my_a, p)
inv_b = inverse(S_b, p)
print(f"S_a = {S_a}\nS_b = {S_b}")


r.recvuntil(b"hint: does Alice and Bob share the same key?\n\n")
r.send(b"\n")
r.recvuntil(b"Bob: Right, let's exchange the encrypted flag!\n\n")
r.send(b"\n")
r.recvuntil(b"[INFO] : Bob does the same, `C_b = (m * S_b) % p`\n\n")
r.send(b"\n")

# Impersonate B to send message to A
rec = r.recvuntil(b"> ").decode()
C_b = int(re.findall(r'C_b = ([0-9]*)\n', rec)[0])
print(f"C_b = {C_b}")

m_b = (C_b * inv_b) % p
m2a = (m_b * S_a) % p
print(f"m_b = {m_b}\nm2a = {m2a}")

r.sendline(b"yes")
r.sendafter(b"> ", str(m2a).encode())

# Impersonate A to send message to B
rec = b"".join(r.recvlines(2, keepends=True)).decode()
C_a = int(re.findall(r'C_a = ([0-9]*)\n', rec)[0])
print(f"C_a = {C_a}")

m_a = (C_a * inv_a) % p
m2b = (m_b * S_b) % p
print(f"m_a = {m_a}\nm2b = {m2b}")

# r.sendline(b"yes")
# r.sendafter(b"> ", str(m2b).encode())

print(b''.join(map(long_to_bytes, [m_a, m_b])))

r.interactive()

Feedback 150pt

task.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os, random
import string, binascii
import signal
import socketserver
from hashlib import sha256
from Crypto.Cipher import AES

from secret import MESSAGE
assert len(MESSAGE) == 48


class Task(socketserver.BaseRequestHandler):
    def __init__(self, *args, **kargs):
        self.KEY = b""
        self.IV = b""
        super().__init__(*args, **kargs)

    def _recvall(self):
        BUFF_SIZE = 2048
        data = b''
        while True:
            part = self.request.recv(BUFF_SIZE)
            data += part
            if len(part) < BUFF_SIZE:
                break
        return data.strip()

    def send(self, msg, newline=True):
        try:
            if newline: msg += b'\n'
            self.request.sendall(msg)
        except:
            pass

    def recv(self, prompt=b'> '):
        self.send(prompt, newline=False)
        return self._recvall()

    def encrypt(self, data):
        assert len(data) % 16 == 0
        aes = AES.new(self.KEY, AES.MODE_CFB, self.IV, segment_size=128)
        return aes.encrypt(data)

    def decrypt(self, data):
        assert len(data) % 16 == 0
        aes = AES.new(self.KEY, AES.MODE_CFB, self.IV, segment_size=128)
        return aes.decrypt(data)

    def handle(self):
        signal.alarm(60)
        self.KEY = os.urandom(32)
        self.IV = os.urandom(16)

        self.send(b"You have only 3 times to decrypt sth, then I'll give u the FLAG.")
        try:
            for _ in range(3):
                self.send(b"Give me sth(hex) to decrypt")
                hex_input = self.recv()
                if not hex_input:
                    break
                ciphertext = binascii.unhexlify(hex_input)
                plaintext = self.decrypt(ciphertext)
                self.send( binascii.hexlify(plaintext) )
        except:
            self.send(b"Rua!!!")
            self.request.close()

        enc_msg = self.encrypt(MESSAGE)
        self.send(b"Here is your encrypted FLAG(hex): ", newline=False)
        self.send( binascii.hexlify(enc_msg) )
        self.request.close()


class ForkedServer(socketserver.ForkingMixIn, socketserver.TCPServer):
    pass

if __name__ == "__main__":
    HOST, PORT = '0.0.0.0', 1234
    server = ForkedServer((HOST, PORT), Task)
    server.allow_reuse_address = True
    server.serve_forever()

看图解题。

CFB encryption.svg

CFB decryption.svg

exp.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
from pwn import *
import re

XOR = lambda s1, s2: bytes([x^y for x,y in zip(s1, s2)])
m1 = b'FLAG is hgame{51'

r = remote('47.98.192.231', 25147)
context.log_level = 'debug'

def send(data):
    r.sendafter(b"> ", data)

def rec():
    return r.recvline().decode()

# c1 = b'0'*32
# send(c1)
# enc_iv = bytes.fromhex(rec())

# inp = XOR(m1, enc_iv)
# c2 = inp.hex().encode() + c1
# send(c2)
# enc_inp = bytes.fromhex(rec())[16:32]

# send(b"\n")
# enc_flag = bytes.fromhex(re.findall(r'FLAG\(hex\): ([0-9a-f]*)\n', rec())[0])

# m2 = XOR(enc_flag[16:32], enc_inp)
# print(m2)
m2 = b'b72d4cd23b2fe672'


c1 = b'0'*32
send(c1)
enc_iv = bytes.fromhex(rec())

inp = XOR(m1, enc_iv)
c2 = inp.hex().encode() + c1
send(c2)
enc_inp = bytes.fromhex(rec())[16:32]

inp2 = XOR(m2, enc_inp)
c3 = inp.hex().encode() + inp2.hex().encode() + c1
send(c3)
enc_inp2 = bytes.fromhex(rec())[32:48]

enc_flag = bytes.fromhex(re.findall(r'FLAG\(hex\): ([0-9a-f]*)\n', rec())[0])
m3 = XOR(enc_flag[32:48], enc_inp2)
print(m3)
m3 = b'a874cb44020868}.'
r.interactive()

# FLAG is hgame{51b72d4cd23b2fe672a874cb44020868}

Week4

CBCBC 150pt

task.py

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import socketserver
import os, sys, signal
import string, binascii
from hashlib import sha256

from secret import FLAG

from Crypto.Cipher import AES
from Crypto.Random import random, atfork

BLOCKS = lambda data: [data[16*i:16*(i+1)] for i in range(len(data)//16)]
XOR = lambda s1, s2: bytes([x^y for x,y in zip(s1, s2)])

class Task(socketserver.BaseRequestHandler):

    BLOCKSIZE = 16
    KEY = None
    IV = None

    def _recvall(self):
        BUFF_SIZE = 2048
        data = b''
        while True:
            part = self.request.recv(BUFF_SIZE)
            data += part
            if len(part) < BUFF_SIZE:
                break
        return data.strip()

    def send(self, msg, newline=True):
        try:
            if newline: msg += b'\n'
            self.request.sendall(msg)
        except:
            pass

    def recv(self, prompt=b'> '):
        self.send(prompt, newline=False)
        return self._recvall()

    def proof_of_work(self):
        proof = ''.join([ random.choice(string.ascii_letters+string.digits) for _ in range(20) ])
        _hexdigest = sha256(proof.encode()).hexdigest()
        self.send(str.encode( "sha256(XXXX+%s) == %s" % (proof[:],_hexdigest) ))
        x = proof[:4].encode()
        if len(x) != 4 or sha256(x+proof[4:].encode()).hexdigest() != _hexdigest:
            return False
        return True

    def pad(self, data):
        pad_len = self.BLOCKSIZE - (len(data) % self.BLOCKSIZE)
        return data + bytes( [pad_len] * pad_len )

    def unpad(self, data):
        pad_len = data[-1]
        _data = data[:-pad_len]
        if self.pad(_data) != data:
            raise ValueError('Padding is incorrect.')
        return _data

    def enc(self, block):
        aes = AES.new(self.KEY, AES.MODE_ECB)
        return aes.encrypt(block)

    def dec(self, block):
        aes = AES.new(self.KEY, AES.MODE_ECB)
        return aes.decrypt(block)

    def encrypt(self, data):
        assert len(data) > 2*self.BLOCKSIZE
        data = self.pad(data)
        iv_1, iv_2 = BLOCKS(data)[0], BLOCKS(data)[1]
        mid_1, mid_2 = iv_1, iv_2
        cipher = b''
        for block in BLOCKS(data)[2:]:
            block = XOR(block, mid_1)
            block = self.enc(block)
            mid_1 = block
            block = XOR(block, mid_2)
            block = self.enc(block)
            mid_2 = block
            cipher += block
        return iv_1 + iv_2 + cipher

    def decrypt(self, data):
        assert len(data) > 2*self.BLOCKSIZE
        assert len(data) % self.BLOCKSIZE == 0
        iv_1, iv_2 = BLOCKS(data)[0], BLOCKS(data)[1]
        mid_1, mid_2 = iv_1, iv_2
        plain = b''
        for block in BLOCKS(data)[2:]:
            mid_2_n = block
            block = self.dec(block)
            block = XOR(block, mid_2)
            mid_1_n = block
            block = self.dec(block)
            block = XOR(block, mid_1)
            mid_1, mid_2 = mid_1_n, mid_2_n
            plain += block
        return self.unpad(iv_1 + iv_2 + plain)

    def timeout_handler(self, signum, frame):
        self.send(b"\n\nSorry, time out.\n")
        raise TimeoutError

    def handle(self):
        atfork()

        self.KEY = os.urandom(32)
        self.IV = os.urandom(2*self.BLOCKSIZE)
        # self.send(b'key: ' + self.KEY.hex().encode())
        # self.send(b'IV: ' + self.IV.hex().encode())

        try:
            signal.signal(signal.SIGALRM, self.timeout_handler)
            signal.alarm(300)

            if not self.proof_of_work():
                return

            enc_flag = self.encrypt(self.IV + FLAG)
            self.send(b'Your encrypted FLAG (in hex) is ', newline=False)
            self.send( binascii.hexlify(enc_flag) )

            self.send(b'Now, I can decrypt sth for you.')
            while True:
                try:
                    self.send(b'Give me sth (in hex)')
                    hex_inp = self.recv()
                    if not hex_inp:
                        break
                    inp = binascii.unhexlify(hex_inp)
                    self.decrypt(inp)
                    self.send(b'decryption done')

                except TimeoutError:
                    exit(1)

                except:
                    self.send(b'sth must be wrong')

            signal.alarm(0)

            self.send(b'Bye~~')
            self.request.close()

        except:
            pass

class ForkedServer(socketserver.ForkingMixIn, socketserver.TCPServer):
    pass


if __name__ == "__main__":
    HOST, PORT = '0.0.0.0', 12345
    server = ForkedServer((HOST, PORT), Task)
    server.allow_reuse_address = True
    server.serve_forever()

没图?那就自己画一个。

Screen Shot 2020-02-13 at 1.41.28 PM

考虑第一段M1,即flag[:16]

解密完了会unpadunpad成功后会返回Decryption done,否则返回sth must be wrong

可以在这边操作下。

Screen Shot 2020-02-13 at 1.44.13 PM

mid_1(即IV1)异或上一些东西,来逐字节爆破。

例如,如果解密出来的m满足???????????????\x01,那么就会显示Decryption done,所以我们可以在mid_1的最后一字节异或上某个东西i(尝试所有256种可能),使它回显Decryption done,那么原来的m的最后一位就肯定是i ^ 1;如果解密出来的m满足``??????????????\x02\x02,也会显示Decryption done,可以把mid_1的最后一位设置成\x02(我们已经知道什么时候会是\x01了,所以再异或一下1^2就能把它给设置成\x02),在倒数第二位处尝试256种可能i,使它回显Decryption done,那么原来的m的倒数第二位肯定就是i ^ 2 。一直这样下去,可以通过平均256/2 * 16 = 2048 次尝试爆破出flag[:16]`。


考虑第二段M2,即flag[16:32]

Screen Shot 2020-02-13 at 1.53.32 PM

同样的思路,可以对mid_2(即IV_2)异或上一些东西,逐字节爆破M2

后面2段M3, M4也是一样,分别对C1, C2异或上一些东西,逐字节爆破M3, M4


exp.py

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
from hashlib import sha256
from itertools import product
from Crypto.Util.number import *
import re

from pwn import *

s = string.ascii_letters + string.digits
XOR = lambda s1, s2: bytes([x^y for x,y in zip(s1, s2)])


debug = False
if debug == False:
    r = remote('47.98.192.231', 25355)
    # context.log_level = 'debug'

    # Proof of work
    rec = r.recvline().decode()

    suffix = re.findall(r'\(XXXX\+(.*?)\)', rec)[0]
    digest = re.findall(r'== (.*?)\n', rec)[0]
    print(f"[+] suffix: {suffix} \n[+] digest: {digest}")

    print('[*] Calculating hash...')
    for i in product(s, repeat=4):
        prefix = ''.join(i)
        guess = prefix + suffix
        if sha256(guess.encode()).hexdigest() == digest:
            print(guess)
            break
    r.sendafter(b'Give me XXXX: ', prefix.encode())
    print("[!] Proof of work passed!")
else:
    r = remote('127.0.0.1', 12345)
    # context.log_level = 'debug'

rec = r.recvuntil(b'Give me sth (in hex)').decode()
enc = re.findall(r'is ([0-9a-f]*)\n', rec)[0]
enc = bytes.fromhex(enc)
IV1, IV2, enc_flag = enc[:16], enc[16:32], enc[32:]
print(f"[+] IV1: {IV1}\n[+] IV2: {IV2}\n[+] enc_flag: {enc_flag}")



enc1 = enc_flag[:16]
flag1 = ''
for j in range(16):
    for i in range(256):
        payload = XOR(IV1, b'\x00'*(15-j) + bytes([i]) + XOR(flag1.encode(), bytes([j+1])*j))
        payload += IV2 + enc1
        r.sendafter(b'> ', payload.hex().encode())
        if r.recvline() == b'decryption done\n':
            ch = chr(i^(j+1))
            flag1 = ch + flag1
            print(flag1)
            break
# hgame{I_like_Pad


enc2 = enc_flag[16:32]
flag2 = ''
for j in range(16):
    for i in range(256):
        payload = IV1
        payload += XOR(IV2, b'\x00'*(15-j) + bytes([i]) + XOR(flag2.encode(), bytes([j+1])*j))
        payload += enc1 + enc2
        r.sendafter(b'> ', payload.hex().encode())
        if r.recvline() == b'decryption done\n':
            ch = chr(i^(j+1))
            flag2 = ch + flag2
            print(flag2)
            break
# ding_oracle_atta


enc3 = enc_flag[32:48]
flag3 = ''
for j in range(16):
    for i in range(256):
        payload = IV1 + IV2
        payload += XOR(enc1, b'\x00'*(15-j) + bytes([i]) + XOR(flag3.encode(), bytes([j+1])*j))
        payload += enc2 + enc3
        r.sendafter(b'> ', payload.hex().encode())
        if r.recvline() == b'decryption done\n':
            ch = chr(i^(j+1))
            flag3 = ch + flag3
            print(flag3)
            break
# ck_6f64ab782042f

enc4 = enc_flag[48:]
flag4 = '\x04'
for j in range(1, 16):
    # print(f"j: {j}")
    for i in range(256):
        payload = IV1 + IV2 + enc1
        payload += XOR(enc2, b'\x00'*(15-j) + bytes([i]) + XOR(flag4.encode(), bytes([j+1])*j))
        payload += enc3 + enc4
        r.sendafter(b'> ', payload.hex().encode())
        if r.recvline() == b'decryption done\n':
            ch = chr(i^(j+1))
            # print(ord(ch))
            flag4 = ch + flag4
            print(flag4)
# 3f389f590a2}\x04\x04\x04\x04\x04\x04

flag = flag1 + flag2 + flag3 + flag4
print(flag)
# hgame{I_like_Padding_oracle_attack_6f64ab782042f3f389f590a2}

r.interactive()

ToyCipher_Linear 175pt

整个加密过程可以用一个$32 \times 224$的矩阵来表示,以前也出过一道基本一样的题目。

太费时间了,懒得写了。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os, binascii

# from secret import flag

def rotL(x, nbits, lbits):
    mask = 2**nbits - 1
    return (x << lbits%nbits) & mask | ( (x & mask) >> (-lbits % nbits) )


def rotR(x, nbits, rbits):
    return rotL(x, nbits, -rbits%nbits)


def keySchedule(masterkey):
    roundKeys = [ ( rotR(masterkey, 64, 16*i) % 2**16 ) for i in range(12) ]
    return roundKeys


def f(x, roundkey):
    return rotL(x, 16, 7) ^ rotL(x, 16, 2) ^ roundkey


def ToyCipher(block, mode='enc'):
    '''Feistel networks'''
    roundKeys_ = ROUNDKEYS
    if mode == 'dec':
        roundKeys_ = roundKeys_[::-1]

    L, R = (block >> 16), (block % 2**16)
    for i in range(12):
        _R = R
        R = L ^ f( R, roundKeys_[i] )
        L = _R

    return (R << 16) | L


def pad(data, blocksize):
    pad_len = blocksize - (len(data) % blocksize)
    return data + bytes( [pad_len] * pad_len )


def unpad(data, blocksize):
    pad_len = data[-1]
    _data = data[:-pad_len]
    assert pad(_data, blocksize)==data, "Invalid padding."
    return _data


def encrypt(plaintext):
    '''ECB mode'''
    plaintext = pad(plaintext, BLOCKSIZE)
    ciphertext = b''
    for i in range( len(plaintext) // BLOCKSIZE ):
        block = plaintext[i*BLOCKSIZE:(i+1)*BLOCKSIZE]
        block = int.from_bytes(block, byteorder='big')
        E_block = ToyCipher(block)
        ciphertext += E_block.to_bytes(BLOCKSIZE, byteorder='big')
    return ciphertext


def decrypt(ciphertext):
    '''ECB mode'''
    plaintext = b''
    for i in range( len(ciphertext) // BLOCKSIZE ):
        block = ciphertext[i*BLOCKSIZE:(i+1)*BLOCKSIZE]
        block = int.from_bytes(block, byteorder='big')
        D_block = ToyCipher(block, 'dec')
        plaintext += D_block.to_bytes(BLOCKSIZE, byteorder='big')
    plaintext = unpad(plaintext, BLOCKSIZE)
    return plaintext

ms, ks, cs = [], [], []
for i in range(224):
    masterkey = os.urandom(8)
    masterkey = int.from_bytes(masterkey, byteorder='big')
    ROUNDKEYS = keySchedule(masterkey)
    BLOCKSIZE = 4
    m = os.urandom(4)
    m = int.from_bytes(m, 'big')
    c = ToyCipher(m)
    ms.append(m)
    ks.append(ROUNDKEYS)
    cs.append(c)

with open('ms.txt', 'wb') as f:
    for m in ms:
        f.write(str(m).encode() + b'\n')

with open('cs.txt', 'wb') as f:
    for c in cs:
        f.write(str(c).encode() + b'\n')

with open('ks.txt', 'wb') as f:
    for k in ks:
        for kk in k:
            f.write(str(kk).encode()+b' ')
        f.write(b'\n')

Screen Shot 2020-02-13 at 2.02.58 PM

Screen Shot 2020-02-13 at 2.03.15 PM