Xi4or0uji's blog

2020 HGAME

字数统计: 976阅读时长: 4 min
2020/02/05 Share

近期开始复习准备考研,连CTF打的都比较少了,刚好昨天星盟开始了新一轮的出题,遂练下手,但是今天不知道为什么出着出着就来补题了(摊手

WEEK1

CRYPTO

InfantRSA

这道题挺简单的,直接就把pqe都给出来了

1
2
3
4
5
6
7
8
9
#!/usr/bin/env python3
from secret import flag
assert flag.startswith(b'hgame{') and flag.endswith(b'}')
m = int.from_bytes(flag, byteorder='big')
p = 681782737450022065655472455411
q = 675274897132088253519831953441
e = 13
c = pow(m, e, p*q)
assert c == 275698465082361070145173688411496311542172902608559859019841

exp如下

1
2
3
4
5
6
7
8
9
10
11
import gmpy2
from Crypto.Util.number import long_to_bytes
p = 681782737450022065655472455411
q = 675274897132088253519831953441
e = 13
phi = (p-1)*(q-1)
d = gmpy2.invert(e,phi)
n = p*q
c = 275698465082361070145173688411496311542172902608559859019841
print long_to_bytes(pow(c,d,n))
# hgame{t3Xt6O0k_R5A!!!}

Affine

一道简单的仿射密码题,因为范围不大,所以可以直接爆破求A和B,当然联立方程去求也是可以的,exp如下

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
import gmpy2

TABLE = 'zxcvbnmasdfghjklqwertyuiop1234567890QWERTYUIOPASDFGHJKLZXCVBNM'
MOD = len(TABLE)
#这一步求A和B
flag = "hgame"
for A in range(0xff):
for B in range(0xff):
cipher = ''
for b in flag:
i = TABLE.find(b)
ii = (A*i+B)%MOD
cipher += TABLE[ii]
if(cipher=='A8I5z'):
print A, B, cipher
break
A = 13
B = 14
c = 'zxcvbnmasdfghjklqwertyuiop1234567890QWERTYUIOPASDFGHJKLZXCVBNM'
flag = ''
cipher = "A8I5z{xr1A_J7ha_vG_TpH410}"
for b in cipher:
o = TABLE.find(b)
if o==-1:
flag += b
continue
for x in c:
i = TABLE.find(x)
ii = (A*i+B)%MOD
now = TABLE[ii]
if now==b:
flag+=x
break
print flag

not_One-time

题目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/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() )

nc一下能拿到这串东西

1
JTcLBwQzE3czQV8UPkoIYWtbVT4PYnB2CUFxUQEzVhw8IVYvR0cdZXUJTg==

看题目可以看到,首先随机生成8个字符的种子,然后再生成一串长度跟flag一样的字符串,把两个字符串异或一下再base64一下就是nc得到的值
可以看到,题目用的是一次一密的加密方法,但是题目给的明文空间明显大于密文空间,所以可以用多组密文去进行攻击
原理看这个:https://www.zhihu.com/question/26576521
在这道题中,我们可以理解成keystream是不同的明文,而flag是相同的秘钥,每次都用不同的明文进行异或加密得到不同的密文,总结一下出来就是

1
2
3
4
5
6
7
假设我们现在有两条明文m1、m2,秘钥是k,密文是c1、c2,那么就有
c1 = m1^k
c2 = m2^k
c1^c2 = (m1^k)^(m2^k) = m1^m2
可以看到,我们不知道明文是什么,但是可以通过这个关系反推出部分明文
而同样有一个需要注意的就是,如果得出来的c1^c2是英文字符的话,m1很大可能是个英文字符,m2为空格
所以,如果我们的秘钥数足够多的话,就可以一位位爆破出k值,进而得到flag了

最后exp如下

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
import base64,string
from pwn import *
context.log_level = 'debug'
cipher = []
for i in range(40):
p = remote("47.98.192.231","25001")
s = p.recv(500)
cipher.append(s)
p.interactive()

t = (string.ascii_letters + string.digits).encode()
res = (string.printable).encode()
for i in range(len(base64.b64decode(cipher[0]))):
l = set()
for c1 in cipher:
for c2 in cipher:
d1 = base64.b64decode(c1)
d2 = base64.b64decode(c2)
if d1==d2:
continue
re = set()
tmp1 = d1[i]^d2[i]
for j in range(len(t)):
for k in range(len(t)):
if j==k:
continue
tmp2 = t[j]^t[k]
#找到了c1 c2 m1 m2都是可见字符,把符合条件的k放进去
if tmp1==tmp2:
re1 = int(d2[i]^t[j]).to_bytes(1,'big')
re2 = int(d2[i]^t[k]).to_bytes(1,'big')
if res.find(re1)!=-1:
re.add(re1)
if res.find(re2)!=-1:
re.add(re2)
if(l==set()):
l = re
else:
tmp = l.intersection(re)
if tmp!=set():
l.intersection_update(re)
else:
l.update(re)
print i,''.encode().join(l)
#hgame{r3us1nG+M3$5age-&&~rEduC3d_k3Y-5P4Ce}

Recoder

nc过去以后输入字符,然后他给你一下加密后的数据,然后输入很多次以后给一个flag?
注意一个点的就是,打乱的顺序以八个为一组,所以我们拿到了flag以后对应8位一组找出对应关系就行了
最后flag:hgame{jU$t+5ImpL3_PeRmuTATi0n!!}

CATALOG
  1. 1. WEEK1
    1. 1.1. CRYPTO
      1. 1.1.1. InfantRSA
      2. 1.1.2. Affine
      3. 1.1.3. not_One-time
      4. 1.1.4. Recoder