0%

2024-TAMUCTF-wp-crypto

最近过的有些慌乱,很多事情突然堆在了一起。两个很麻烦的作业的ddl、几门选修的数学课的作业、还有我们专业的一些让人心烦的破事……来到大三下学期之后的一个月,压力似乎突然就变得好大。

但是又有很多开心的事情,比如和队友一起肝刚刚开赛的高校密码挑战赛、给一些需要的师傅出点密码工坊dlc题目、还有就是空下来的时候和小队队友一起打打ctf玩,又会感觉开心很多。

这里就记录一下周天下午和余师傅的小队一起打的TAMUCTF,题目都是一些简单又有意思的小trick,就当放松一下了哈哈哈。这里会把队友做出的题目也整理进来。

Truncated 1

题目描述:

1
Only part of the private key was able to be retrieved. Decrypt the flag.txt.enc file.

题目主要内容是要恢复下面的破损私钥文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
ZXPI0zfM5EJkeooRvNr3RKQEoQKBgQD0WrYbxhBveSRYvkOV0+omfutwS6wIoCme
CYCq5MboHdZn8NDCHy+Y66b+G/GMZJewqEKQSLwHcAjKHxouneFXp6AxV0rkBWtO
RNnjXfthsWXvOgBJzGm8CJQS+xVtUpYc4l1QnYaQpc0/SClSTPG775H5DnJ8t4rK
oNQur+/pcwKBgD1BU0AjW6x+GYPXUzA0/tXQpu5XaAMxkinhiiOJWT/AExzJU8Jt
eQULJ3EDENG6acSuwMhm0WMLhQ0JG6gIejRyOBZSIqjESWGHPmkU1XbUDz0iLb1h
HTqJMAWYKWJs4RnJbx6NGJAhd2Ni4CyOGmujYpqNnp1qfZNhmcj/VOeBAoGBAJgD
stU2c9UVlTIMM7mLG1kVjlzPBtha42ko2j32k3Ol1FPXcdfCVPcaa0ockjnX/rJt
CvP9+9PYs+8iSESF/cFtS/BGMRYH9Qi9NpwHRLMzDIo2GCXRIFpVL+FbCKp5PV/8
xza2uRdVvolG2EYWDjDvym0Zusmx2YtTYI0m8ObXAoGAZ6T8aF6GAZ0s814ZfEcy
zZGrZZQ/MJ7W8ZGdU1/y+204LzfGsW+d+sTPfQPYhn03/qU3SFhP095sYzELeOOZ
3yITOftHEdMP3XffnAudgn3tBHrtu0EsVFL44H7CWe4hx3M49M0lfERD60lPwUG1
8hY5qcthSko1f1WkTgN7Rrs=
-----END PRIVATE KEY-----

虽然私钥文件开头被隐藏掉了,但是其实从已有的部分可以提取出n的素因子,所以直接去解密题目的加密文件就好了。



Truncated 2

题目描述:

1
It seems even less was able to be retrieved this time. Decrypt the flag.txt.enc file.

题目和上一个区别不大,主要在于私钥文件给出的部分更少了:

1
2
3
4
5
6
7
8
9
10
WXH2tecCgYBIlOn6LCaw4cYxztL4a+AgeoJ1HXB7AYg5Vl6T9VHfWW6dFvBVmaK/
sLuzAAZBOfOD3oXHk+BY2izOQamgOY5AvgW7m4JwP+gEFk9f9NdmI9DkxyD9cFzm
76zpeUiaizor1mMAd2mcCqjaYlDB3ohA0+Wvw024ZeBlDOCPgotJrQKBgFTU0ZgY
cNeZM05a5RdFJtKXnhTG7MdNe1lgD799tMBgSBw9OMg6pASOTGrUg6QW1DrsxY23
/ouePRFBh1OMArIskZf+Ov0jqD9umsM/q1XIR3ax3iOmBX6RxH42qyrHYArbv+tB
WdiwnYGJj5oE5HtnnL5pDa9qYFUfK4InhjN3AoGAZ2q2zPPhW9v75hq8fwVvLGjP
yDT4gGIz168dnCBLLMHsNv8y0twKQMY8UnqKBBIIkaC+j6zdCM+9CU3SEGC/TwQc
5iTOHmknFfuvRYN6WKOXbTQZJIx2aDHaRz4MZlpHOVFeHrmY9/s+y24U2nOG9kAC
zBzyXKI5PxT40b/mIGs=
-----END PRIVATE KEY-----

这时候私钥文件不再能提取到素因子p、q了,但是可以得到dp、dq的值。题目还给了完整的公钥文件,也就是说我们其实也拥有公钥n、e的值。那么可以通过如下方式还原出p来:

而之所以能够这么做,是因为在模p下有:

所以能够求GCD还原p、q。之后就解密就好了。



Criminal

题目描述:

1
2
3
It would be a crime for me to just give you the flag, so I'll encrypt it first before sending it. I'll even compress it to make it faster to transmit!

Note: the flag matches the regex gigem{[a-z_]+} (the curly braces are not quantifiers).

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from base64 import b64encode
from Crypto.Random import get_random_bytes
from Crypto.Cipher import ChaCha20_Poly1305
from pathlib import Path
from zlib import compress

flag = Path("flag.txt").read_bytes()
key = get_random_bytes(32)

try:
while True:
append = input("Append whatever you want to the flag: ").encode()
# gotta save on bandwidth!
m = compress(flag + append)
cipher = ChaCha20_Poly1305.new(key=key)
cipher.update(m)
c, tag = cipher.encrypt_and_digest(m)
res = cipher.nonce + tag + c
print(b64encode(res).decode())
except (KeyboardInterrupt, EOFError):
pass

题目用上了不熟悉的ChaCha20_Poly1305加密,搜索了一下他对应的多项式实现,看上去并不是很容易。然而他的解数却是所有密码题目中第三多的,所以我想出题人想考的东西并不在这里。

那就先简单梳理一下题目任务,题目给了我们一个能交互无限次的oracle,利用oracle可以:

  • 输入一段消息append
  • 靶机会将flag拼接上append,然后进行compress压缩之后,用ChaCha20_Poly1305去加密他
  • 给出加密的nonce,以及加密得到的tag和密文c

结合题目提示,这里应该是compress的地方有蹊跷,搜索了一下zlib的compress:

https://www.euccas.me/zlib/

发现他对消息的压缩主要是做了如下两点处理:

  • 处理一:把相同的字符串处理成一个字符串加上多个指针的形式(LZ77 algorithm)
  • 处理二:对字符串进行huffman编码

而自己测试可以知道,ChaCha20_Poly1305的加密有一个比较重要的特点就是明文和密文字节流的长度相同。而这说明我们可以从返回消息的长度,去看出flag+append这个消息的压缩情况。就比如说,由于知道flag是gigem{开头的,那如果我们输入的append是gigem{,这两个字符串一定会被处理一给压缩到一起。

那么解题思路就有了,我们爆破字符表中所有可能字符x,并发送给靶机gigem{x,当且仅当gigem{x与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
from base64 import *
from pwn import *
from string import *

sh = remote("tamuctf.com", 443, ssl=True, sni="criminal")

msg = b"gigem{"
table = [i.encode() for i in ascii_lowercase+"_"]
while(1):
sh.recvuntil(b"Append whatever you want to the flag: ")
sh.sendline(msg)
res = b64decode(sh.recvline().strip().decode())
temp_len = len(res[12+16:])
for i in table:
append = msg + i
sh.recvuntil(b"Append whatever you want to the flag: ")
sh.sendline(append)
res = b64decode(sh.recvline().strip().decode())
length = len(res[12+16:])
if(length == temp_len):
msg += i
break
print(msg)

#gigem{foiled_again}



PCG

题目描述:

1
QCG seems a little too simple, so instead I rolled my own rng using polynomials.

题目:

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
from secrets import randbelow
from Crypto.Util.number import getPrime
import sys

SIZE = 256
class PCG: # Polynomial Congruential Generator
def __init__(self):
self.m = getPrime(256)
self.coeff = [randbelow(self.m-1) for _ in range(SIZE)]
self.x = randbelow(self.m-1)
def __call__(self):
newx = 0
for c in self.coeff:
newx *= self.x
newx += c
newx %= self.m
self.x = newx
return self.x
def printm(self):
print(self.m)
return
pcg = PCG()

print(pcg.m)
for i in range(SIZE*3):
print(pcg())

sys.stdout.flush()
correct = True
for i in range(SIZE // 2):
guess = int(input())
if guess != pcg():
correct = False

if correct:
print(open('flag.txt','r').read())
else:
print("you failed")
sys.stdout.flush()

这是一个Polynomial Congruential Generator,他先初始化一个度为255的多项式f(x)的系数列表,然后按如下方式去迭代更新伪随机数:

通过与靶机交互,我们能得到模数m以及连续的256*3个随机数,要求我们完全成功地预测接下来的128个随机数就可以得到flag。

而由于连续的两个随机数x1,x2有如下关系:

因此他们可以看作是函数f(x)上的一个点:

所以其实是拉格朗日插值问题,插值完成后依次计算下一个点的函数值即可。

exp:

交互拿数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pwn import *

sh = remote("tamuctf.com", 443, ssl=True, sni="pcg")

points = []
m = int(sh.recvline().strip().decode())
T = []
for i in range(256*3):
t = int(sh.recvline().strip().decode())
T.append(t)
for i in range(len(T)-1):
points.append((T[i],T[i+1]))

print("m =" , m)
print("points =" , points)
print("T =",T)

sh.interactive()


#gigem{p0lyn0m1al5_4r3_funny}

得到f(x)并计算接下来128个随机数:

1
2
3
4
5
6
7
PR = PolynomialRing(Zmod(m), 'x')
f = PR.lagrange_polynomial(points)
print(f)
for i in range(128):
tt = f(T[-1])
T.append(tt)
print(tt)



Jumbled

题目描述:

1
The RSA Public and Private keys are provided. However, the private key seems to be jumbled in a block size of 10 hex characters. Can you get the flag?

和前两个Truncated题目类似,这也是一个私钥文件的恢复,只是私钥文件变成了如下的形式:

1
49 45 4e 42 47 2d 2d 2d 2d 2d 20 54 4b 41 45 49 50 56 20 52 0a 2d 4d 2d 0d 2d 59 2d 45 2d 44 42 41 49 41 76 49 41 49 45 47 6b 39 68 69 6b 42 71 4e 67 41 46 53 45 41 41 30 51 77 42 69 67 41 67 53 59 42 77 43 4b 51 42 43 49 41 41 45 6f 67 41 34 50 30 68 76 69 5a 46 71 4e 38 75 6f 4f 78 0d 4e 0a 48 6b 74 75 78 32 30 72 6a 37 50 67 69 59 2b 70 64 35 74 56 6b 50 44 39 74 66 2b 6e 77 31 66 47 79 50 77 6b 6f 6d 59 58 4f 72 51 31 59 79 6f 74 7a 6e 58 32 70 48 0d 54 36 4c 6b 36 55 2f 43 6b 45 33 5a 34 53 37 0a 6f 50 66 56 43 51 63 5a 44 7a 4a 63 6d 62 4a 36 31 6b 70 4d 70 6c 76 76 64 36 78 71 44 54 6c 2f 6a 74 6e 63 68 59 69 6b 4e 44 49 59 64 4c 79 42 41 71 53 79 0a 7a 0d 38 31 55 54 34 4b 56 50 30 61 6e 43 63 4c 6e 54 69 36 6e 75 6f 77 2f 70 53 37 7a 4c 50 76 63 62 67 4d 59 34 62 4d 58 4e 69 56 69 4f 48 76 4c 36 79 56 6a 6c 4f 56 77 65 49 32 4b 56 63 5a 32 74 77 31 38 75 2b 6f 63 68 0d 6a 30 0a 61 36 74 58 4e 34 5a 6e 79 6f 6b 32 68 64 6c 30 43 4f 61 2f 73 33 71 4e 56 36 4d 6a 34 36 52 72 38 67 61 46 30 34 57 73 62 4f 35 5a 42 47 65 69 57 6a 0a 66 2b 75 0d 76 42 69 49 49 6e 6f 6b 54 4a 31 4f 7a 69 6f 75 48 45 49 4a 63 34 4d 76 71 44 62 52 4b 50 42 65 4f 62 79 51 66 57 62 6d 4c 79 6b 41 74 59 2f 63 76 78 63 61 7a 2f 58 71 4a 59 4a 6b 61 4a 6c 36 64 36 78 2f 4f 74 0d 72 0a 71 56 41 42 45 4d 41 41 75 67 36 58 67 55 43 41 38 45 67 41 43 67 57 4b 69 47 2b 55 71 77 4c 53 47 74 79 49 72 61 65 6a 6f 33 78 6b 56 73 44 37 71 65 73 4d 2b 2f 0d 52 36 4d 2b 77 6a 6d 45 77 49 35 6e 47 5a 61 0a 74 64 77 5a 39 37 59 46 70 6b 33 2b 6b 72 4f 38 6b 45 6d 4f 2f 52 6e 63 47 6f 54 53 6f 53 63 33 4f 51 75 53 42 6c 67 65 42 64 42 5a 37 33 57 6e 48 75 31 58 0a 42 0d 75 74 51 6f 78 42 33 52 59 74 6a 71 2b 69 4e 72 42 41 49 52 6e 6a 36 78 4a 56 73 6f 49 31 6a 34 57 61 30 42 70 6d 4e 68 78 7a 70 46 2f 34 78 44 42 2b 71 57 59 6b 71 2f 61 39 47 48 37 57 69 4d 70 4c 32 68 43 51 52 55 0d 63 38 0a 56 2f 38 4f 45 30 4c 39 74 50 68 43 45 4e 74 49 44 31 46 43 43 6b 73 76 57 58 52 39 30 59 68 45 78 51 74 4e 45 39 44 62 55 4a 4b 79 4b 67 38 51 71 6c 0a 71 76 34 0d 59 4b 6c 61 33 73 4b 41 50 67 6a 62 34 32 61 41 4b 59 39 4a 78 48 39 4a 74 74 6b 73 30 59 58 44 70 6b 34 75 45 5a 6a 44 54 4b 4f 57 30 4a 31 78 31 51 68 53 42 50 63 7a 47 2b 52 39 68 71 5a 69 75 65 55 45 54 34 0d 65 0a 67 6f 2b 51 39 37 33 71 50 6a 47 58 58 49 46 71 4d 6a 4b 49 64 48 43 54 58 4a 2b 46 30 4b 45 2f 42 51 67 35 4b 32 5a 33 6e 55 42 74 64 2b 6a 6d 44 63 51 46 53 63 0d 2f 6a 66 77 62 55 4c 46 64 4b 6f 30 51 4d 38 0a 33 55 64 6c 34 42 49 56 45 52 34 7a 46 55 68 4c 4e 52 6a 79 50 46 52 41 68 44 53 7a 63 76 75 66 4d 2b 37 41 55 63 7a 52 4e 39 50 70 4d 2f 4d 42 63 45 41 63 0a 58 0d 4b 79 4b 50 6a 58 42 45 68 4b 49 71 61 6d 43 53 73 42 61 2f 69 55 4e 52 6a 4e 38 42 43 78 75 6f 4e 6b 62 6c 2b 67 66 6c 58 45 73 39 6f 75 33 4b 46 63 44 70 6d 35 38 62 4b 51 6d 31 57 68 6a 38 6e 71 48 4e 56 67 64 4c 0d 74 36 0a 6d 49 49 32 67 6e 6a 7a 48 37 77 70 4b 67 67 58 32 63 61 68 45 4a 68 77 6e 44 67 63 42 51 46 49 63 37 55 72 65 69 69 71 32 4b 78 7a 36 70 66 6e 34 45 0a 79 68 31 0d 36 54 43 74 67 69 4c 53 42 4b 71 55 74 43 6f 6e 36 52 34 74 5a 45 49 65 5a 2f 37 59 59 35 42 45 78 67 5a 62 68 50 4d 50 77 2b 76 71 6e 37 45 57 47 61 48 58 52 73 37 30 72 68 64 34 59 56 39 79 63 69 4e 4e 54 54 0d 4b 0a 31 73 54 6e 6c 34 30 75 77 65 72 66 5a 69 70 2f 38 75 64 76 69 47 30 51 42 64 44 30 78 69 36 53 2b 76 4a 70 49 58 36 72 4c 58 70 7a 69 53 31 56 6f 4b 44 55 39 4e 0d 75 76 4a 6f 39 64 52 64 72 58 45 78 53 75 72 0a 50 33 41 72 78 59 67 4b 42 51 42 50 6e 6b 65 51 74 6e 56 79 70 74 63 62 47 75 31 6a 6f 74 4b 4c 71 6a 42 63 66 43 45 30 73 4e 76 53 42 65 2b 61 51 48 7a 42 0a 73 0d 34 68 4e 79 6c 74 57 35 36 74 37 51 59 38 47 61 7a 69 73 59 5a 69 6b 7a 4a 70 43 59 64 44 63 37 58 79 77 32 45 32 6d 30 4d 7a 33 32 2b 56 63 51 36 74 69 59 67 37 37 44 72 75 42 73 74 4e 78 76 4d 6b 6a 4c 64 42 41 36 0d 59 70 0a 77 4e 42 6b 36 48 50 55 50 77 76 66 47 65 4e 47 4f 50 62 4f 69 69 56 2b 4c 78 32 73 58 35 74 4f 68 53 7a 6d 70 46 61 48 31 6b 41 43 41 68 31 51 30 44 0a 31 45 62 0d 62 47 69 6f 41 4a 41 66 65 74 6c 6c 63 36 56 62 58 4a 4f 42 39 54 48 53 65 4b 71 41 7a 63 4d 30 47 66 6c 36 74 6d 64 67 55 34 4a 62 71 36 4d 57 48 76 50 31 6b 56 78 5a 2f 54 72 76 6f 32 38 67 70 49 72 54 56 65 0d 7a 0a 43 64 37 31 78 50 54 31 69 66 50 50 77 67 62 46 35 75 56 52 6e 2b 2b 56 4f 5a 65 71 6d 53 73 76 41 38 39 56 6b 79 44 35 51 38 56 52 32 39 70 5a 33 6c 32 63 71 62 0d 45 7a 67 6b 6f 54 57 70 72 56 54 35 61 65 75 0a 6e 39 57 37 2b 46 66 54 6d 6a 42 74 30 42 46 37 44 48 4a 58 4b 6b 55 6b 76 37 62 67 6d 7a 4a 62 46 42 2b 64 41 67 7a 43 59 32 50 4a 4b 74 6a 5a 39 63 45 4c 0a 37 0d 68 72 4f 6c 31 38 4a 70 53 69 31 55 36 75 4a 65 65 37 74 32 79 6c 4c 67 6b 63 77 4c 76 71 53 41 46 50 78 6c 2f 52 2b 52 36 67 47 64 35 54 6b 2b 6d 74 4a 69 54 6c 74 2f 33 35 62 49 70 41 50 62 59 54 67 77 59 62 6a 77 0d 46 44 0a 58 2f 49 4c 4b 2b 69 44 68 77 68 71 68 73 71 73 62 35 45 52 4d 7a 54 36 46 42 7a 2b 41 67 2b 79 50 74 77 79 52 50 4b 38 72 59 76 6e 56 37 36 43 43 57 0a 65 70 56 0d 33 32 65 4e 61 46 6a 61 6b 53 44 6c 54 61 49 4f 52 74 77 37 37 79 6f 64 6a 2d 2d 2d 2d 2d 0d 3d 0a 51 3d 41 49 54 52 56 20 4e 50 45 44 2d 2d 2d 2d 2d 45 20 59 45 4b

按照题目描述,这种乱序是每10个字符一组的,因此可以先逐字符打印出来看看是什么样:

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
161
162
163
164
165
166
167
168
169
170
171
172
173
IENBG-----
TKAEIPV R
*-M-*-Y-E-
DBAIAvIAIE
Gk9hikBqNg
AFSEAA0QwB
igAgSYBwCK
QBCIAAEogA
4P0hviZFqN
8uoOx*N*Hk
tux20rj7Pg
iY+pd5tVkP
D9tf+nw1fG
yPwkomYXOr
Q1YyotznX2
pH*T6Lk6U/
CkE3Z4S7*o
PfVCQcZDzJ
cmbJ61kpMp
lvvd6xqDTl
/jtnchYikN
DIYdLyBAqS
y*z*81UT4K
VP0anCcLnT
i6nuow/pS7
zLPvcbgMY4
bMXNiViOHv
L6yVjlOVwe
I2KVcZ2tw1
8u+och*j0*
a6tXN4Znyo
k2hdl0COa/
s3qNV6Mj46
Rr8gaF04Ws
bO5ZBGeiWj
*f+u*vBiII
nokTJ1Ozio
uHEIJc4Mvq
DbRKPBeOby
QfWbmLykAt
Y/cvxcaz/X
qJYJkaJl6d
6x/Ot*r*qV
ABEMAAug6X
gUCA8EgACg
WKiG+UqwLS
GtyIraejo3
xkVsD7qesM
+/*R6M+wjm
EwI5nGZa*t
dwZ97YFpk3
+krO8kEmO/
RncGoTSoSc
3OQuSBlgeB
dBZ73WnHu1
X*B*utQoxB
3RYtjq+iNr
BAIRnj6xJV
soI1j4Wa0B
pmNhxzpF/4
xDB+qWYkq/
a9GH7WiMpL
2hCQRU*c8*
V/8OE0L9tP
hCENtID1FC
CksvWXR90Y
hExQtNE9Db
UJKyKg8Qql
*qv4*YKla3
sKAPgjb42a
AKY9JxH9Jt
tks0YXDpk4
uEZjDTKOW0
J1x1QhSBPc
zG+R9hqZiu
eUET4*e*go
+Q973qPjGX
XIFqMjKIdH
CTXJ+F0KE/
BQg5K2Z3nU
Btd+jmDcQF
Sc*/jfwbUL
FdKo0QM8*3
Udl4BIVER4
zFUhLNRjyP
FRAhDSzcvu
fM+7AUczRN
9PpM/MBcEA
c*X*KyKPjX
BEhKIqamCS
sBa/iUNRjN
8BCxuoNkbl
+gflXEs9ou
3KFcDpm58b
KQm1Whj8nq
HNVgdL*t6*
mII2gnjzH7
wpKggX2cah
EJhwnDgcBQ
FIc7Ureiiq
2Kxz6pfn4E
*yh1*6TCtg
iLSBKqUtCo
n6R4tZEIeZ
/7YY5BExgZ
bhPMPw+vqn
7EWGaHXRs7
0rhd4YV9yc
iNNTT*K*1s
Tnl40uwerf
Zip/8udviG
0QBdD0xi6S
+vJpIX6rLX
pziS1VoKDU
9N*uvJo9dR
drXExSur*P
3ArxYgKBQB
PnkeQtnVyp
tcbGu1jotK
LqjBcfCE0s
NvSBe+aQHz
B*s*4hNylt
W56t7QY8Ga
zisYZikzJp
CYdDc7Xyw2
E2m0Mz32+V
cQ6tiYg77D
ruBstNxvMk
jLdBA6*Yp*
wNBk6HPUPw
vfGeNGOPbO
iiV+Lx2sX5
tOhSzmpFaH
1kACAh1Q0D
*1Eb*bGioA
JAfetllc6V
bXJOB9THSe
KqAzcM0Gfl
6tmdgU4Jbq
6MWHvP1kVx
Z/Trvo28gp
IrTVe*z*Cd
71xPT1ifPP
wgbF5uVRn+
+VOZeqmSsv
A89VkyD5Q8
VR29pZ3l2c
qb*EzgkoTW
prVT5aeu*n
9W7+FfTmjB
t0BF7DHJXK
kUkv7bgmzJ
bFB+dAgzCY
2PJKtjZ9cE
L*7*hrOl18
JpSi1U6uJe
e7t2ylLgkc
wLvqSAFPxl
/R+R6gGd5T
k+mtJiTlt/
35bIpAPbYT
gwYbjw*FD*
X/ILK+iDhw
hqhsqsb5ER
MzT6FBz+Ag
+yPtwyRPK8
rYvnV76CCW
*epV*32eNa
FjakSDlTaI
ORtw77yodj
-----*=*Q=
AITRV NPED
-----E YEK

对比一下几组已知的头部:

1
2
3
4
5
6
7
8
-----BEGIN
IENBG-----

PRIVATE K
TKAEIPV R

*-M-*-Y-E-
EY-----**M

可以看出这个打乱其实是一个特定的置换,因此按顺序置换回去就可以得到完整私钥文件并解密了:

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
from Crypto.Util.number import *


pri = "49 45 4e 42 47 2d 2d 2d 2d 2d 20 54 4b 41 45 49 50 56 20 52 0a 2d 4d 2d 0d 2d 59 2d 45 2d 44 42 41 49 41 76 49 41 49 45 47 6b 39 68 69 6b 42 71 4e 67 41 46 53 45 41 41 30 51 77 42 69 67 41 67 53 59 42 77 43 4b 51 42 43 49 41 41 45 6f 67 41 34 50 30 68 76 69 5a 46 71 4e 38 75 6f 4f 78 0d 4e 0a 48 6b 74 75 78 32 30 72 6a 37 50 67 69 59 2b 70 64 35 74 56 6b 50 44 39 74 66 2b 6e 77 31 66 47 79 50 77 6b 6f 6d 59 58 4f 72 51 31 59 79 6f 74 7a 6e 58 32 70 48 0d 54 36 4c 6b 36 55 2f 43 6b 45 33 5a 34 53 37 0a 6f 50 66 56 43 51 63 5a 44 7a 4a 63 6d 62 4a 36 31 6b 70 4d 70 6c 76 76 64 36 78 71 44 54 6c 2f 6a 74 6e 63 68 59 69 6b 4e 44 49 59 64 4c 79 42 41 71 53 79 0a 7a 0d 38 31 55 54 34 4b 56 50 30 61 6e 43 63 4c 6e 54 69 36 6e 75 6f 77 2f 70 53 37 7a 4c 50 76 63 62 67 4d 59 34 62 4d 58 4e 69 56 69 4f 48 76 4c 36 79 56 6a 6c 4f 56 77 65 49 32 4b 56 63 5a 32 74 77 31 38 75 2b 6f 63 68 0d 6a 30 0a 61 36 74 58 4e 34 5a 6e 79 6f 6b 32 68 64 6c 30 43 4f 61 2f 73 33 71 4e 56 36 4d 6a 34 36 52 72 38 67 61 46 30 34 57 73 62 4f 35 5a 42 47 65 69 57 6a 0a 66 2b 75 0d 76 42 69 49 49 6e 6f 6b 54 4a 31 4f 7a 69 6f 75 48 45 49 4a 63 34 4d 76 71 44 62 52 4b 50 42 65 4f 62 79 51 66 57 62 6d 4c 79 6b 41 74 59 2f 63 76 78 63 61 7a 2f 58 71 4a 59 4a 6b 61 4a 6c 36 64 36 78 2f 4f 74 0d 72 0a 71 56 41 42 45 4d 41 41 75 67 36 58 67 55 43 41 38 45 67 41 43 67 57 4b 69 47 2b 55 71 77 4c 53 47 74 79 49 72 61 65 6a 6f 33 78 6b 56 73 44 37 71 65 73 4d 2b 2f 0d 52 36 4d 2b 77 6a 6d 45 77 49 35 6e 47 5a 61 0a 74 64 77 5a 39 37 59 46 70 6b 33 2b 6b 72 4f 38 6b 45 6d 4f 2f 52 6e 63 47 6f 54 53 6f 53 63 33 4f 51 75 53 42 6c 67 65 42 64 42 5a 37 33 57 6e 48 75 31 58 0a 42 0d 75 74 51 6f 78 42 33 52 59 74 6a 71 2b 69 4e 72 42 41 49 52 6e 6a 36 78 4a 56 73 6f 49 31 6a 34 57 61 30 42 70 6d 4e 68 78 7a 70 46 2f 34 78 44 42 2b 71 57 59 6b 71 2f 61 39 47 48 37 57 69 4d 70 4c 32 68 43 51 52 55 0d 63 38 0a 56 2f 38 4f 45 30 4c 39 74 50 68 43 45 4e 74 49 44 31 46 43 43 6b 73 76 57 58 52 39 30 59 68 45 78 51 74 4e 45 39 44 62 55 4a 4b 79 4b 67 38 51 71 6c 0a 71 76 34 0d 59 4b 6c 61 33 73 4b 41 50 67 6a 62 34 32 61 41 4b 59 39 4a 78 48 39 4a 74 74 6b 73 30 59 58 44 70 6b 34 75 45 5a 6a 44 54 4b 4f 57 30 4a 31 78 31 51 68 53 42 50 63 7a 47 2b 52 39 68 71 5a 69 75 65 55 45 54 34 0d 65 0a 67 6f 2b 51 39 37 33 71 50 6a 47 58 58 49 46 71 4d 6a 4b 49 64 48 43 54 58 4a 2b 46 30 4b 45 2f 42 51 67 35 4b 32 5a 33 6e 55 42 74 64 2b 6a 6d 44 63 51 46 53 63 0d 2f 6a 66 77 62 55 4c 46 64 4b 6f 30 51 4d 38 0a 33 55 64 6c 34 42 49 56 45 52 34 7a 46 55 68 4c 4e 52 6a 79 50 46 52 41 68 44 53 7a 63 76 75 66 4d 2b 37 41 55 63 7a 52 4e 39 50 70 4d 2f 4d 42 63 45 41 63 0a 58 0d 4b 79 4b 50 6a 58 42 45 68 4b 49 71 61 6d 43 53 73 42 61 2f 69 55 4e 52 6a 4e 38 42 43 78 75 6f 4e 6b 62 6c 2b 67 66 6c 58 45 73 39 6f 75 33 4b 46 63 44 70 6d 35 38 62 4b 51 6d 31 57 68 6a 38 6e 71 48 4e 56 67 64 4c 0d 74 36 0a 6d 49 49 32 67 6e 6a 7a 48 37 77 70 4b 67 67 58 32 63 61 68 45 4a 68 77 6e 44 67 63 42 51 46 49 63 37 55 72 65 69 69 71 32 4b 78 7a 36 70 66 6e 34 45 0a 79 68 31 0d 36 54 43 74 67 69 4c 53 42 4b 71 55 74 43 6f 6e 36 52 34 74 5a 45 49 65 5a 2f 37 59 59 35 42 45 78 67 5a 62 68 50 4d 50 77 2b 76 71 6e 37 45 57 47 61 48 58 52 73 37 30 72 68 64 34 59 56 39 79 63 69 4e 4e 54 54 0d 4b 0a 31 73 54 6e 6c 34 30 75 77 65 72 66 5a 69 70 2f 38 75 64 76 69 47 30 51 42 64 44 30 78 69 36 53 2b 76 4a 70 49 58 36 72 4c 58 70 7a 69 53 31 56 6f 4b 44 55 39 4e 0d 75 76 4a 6f 39 64 52 64 72 58 45 78 53 75 72 0a 50 33 41 72 78 59 67 4b 42 51 42 50 6e 6b 65 51 74 6e 56 79 70 74 63 62 47 75 31 6a 6f 74 4b 4c 71 6a 42 63 66 43 45 30 73 4e 76 53 42 65 2b 61 51 48 7a 42 0a 73 0d 34 68 4e 79 6c 74 57 35 36 74 37 51 59 38 47 61 7a 69 73 59 5a 69 6b 7a 4a 70 43 59 64 44 63 37 58 79 77 32 45 32 6d 30 4d 7a 33 32 2b 56 63 51 36 74 69 59 67 37 37 44 72 75 42 73 74 4e 78 76 4d 6b 6a 4c 64 42 41 36 0d 59 70 0a 77 4e 42 6b 36 48 50 55 50 77 76 66 47 65 4e 47 4f 50 62 4f 69 69 56 2b 4c 78 32 73 58 35 74 4f 68 53 7a 6d 70 46 61 48 31 6b 41 43 41 68 31 51 30 44 0a 31 45 62 0d 62 47 69 6f 41 4a 41 66 65 74 6c 6c 63 36 56 62 58 4a 4f 42 39 54 48 53 65 4b 71 41 7a 63 4d 30 47 66 6c 36 74 6d 64 67 55 34 4a 62 71 36 4d 57 48 76 50 31 6b 56 78 5a 2f 54 72 76 6f 32 38 67 70 49 72 54 56 65 0d 7a 0a 43 64 37 31 78 50 54 31 69 66 50 50 77 67 62 46 35 75 56 52 6e 2b 2b 56 4f 5a 65 71 6d 53 73 76 41 38 39 56 6b 79 44 35 51 38 56 52 32 39 70 5a 33 6c 32 63 71 62 0d 45 7a 67 6b 6f 54 57 70 72 56 54 35 61 65 75 0a 6e 39 57 37 2b 46 66 54 6d 6a 42 74 30 42 46 37 44 48 4a 58 4b 6b 55 6b 76 37 62 67 6d 7a 4a 62 46 42 2b 64 41 67 7a 43 59 32 50 4a 4b 74 6a 5a 39 63 45 4c 0a 37 0d 68 72 4f 6c 31 38 4a 70 53 69 31 55 36 75 4a 65 65 37 74 32 79 6c 4c 67 6b 63 77 4c 76 71 53 41 46 50 78 6c 2f 52 2b 52 36 67 47 64 35 54 6b 2b 6d 74 4a 69 54 6c 74 2f 33 35 62 49 70 41 50 62 59 54 67 77 59 62 6a 77 0d 46 44 0a 58 2f 49 4c 4b 2b 69 44 68 77 68 71 68 73 71 73 62 35 45 52 4d 7a 54 36 46 42 7a 2b 41 67 2b 79 50 74 77 79 52 50 4b 38 72 59 76 6e 56 37 36 43 43 57 0a 65 70 56 0d 33 32 65 4e 61 46 6a 61 6b 53 44 6c 54 61 49 4f 52 74 77 37 37 79 6f 64 6a 2d 2d 2d 2d 2d 0d 3d 0a 51 3d 41 49 54 52 56 20 4e 50 45 44 2d 2d 2d 2d 2d 45 20 59 45 4b"
pub = "2d 2d 2d 2d 2d 42 45 47 49 4e 20 50 55 42 4c 49 43 20 4b 45 59 2d 2d 2d 2d 2d 0d 0a 4d 49 49 42 49 6a 41 4e 42 67 6b 71 68 6b 69 47 39 77 30 42 41 51 45 46 41 41 4f 43 41 51 38 41 4d 49 49 42 43 67 4b 43 41 51 45 41 71 6d 54 59 68 59 54 37 2b 4e 42 7a 5a 44 72 73 66 4b 44 34 0d 0a 34 4b 2b 39 72 74 4c 63 5a 4c 54 2b 56 61 57 48 59 76 6e 38 42 70 39 58 2f 66 67 37 54 6d 4b 35 6c 35 44 36 4d 73 46 38 39 72 5a 38 74 61 45 47 46 4a 50 79 2b 6b 78 2b 71 55 71 4f 4f 39 35 47 0d 0a 51 68 4d 32 53 58 41 77 6e 30 44 31 54 4a 4b 64 61 53 5a 75 6e 47 30 36 70 63 51 33 62 2b 70 62 35 47 44 59 59 70 34 33 50 37 61 67 55 73 67 48 53 43 77 32 4f 46 43 74 55 2f 4d 73 35 33 45 77 0d 0a 69 32 6a 35 31 64 45 76 2b 38 4b 62 75 71 49 70 32 49 4f 47 7a 4c 79 33 4d 7a 78 34 72 31 54 6a 54 49 6d 31 38 44 6e 70 56 56 65 6f 79 38 73 4e 74 57 62 56 64 6e 43 43 74 49 59 36 4c 6e 50 50 0d 0a 73 6d 61 4f 4a 31 2b 6a 57 72 57 67 76 39 44 6e 64 70 5a 49 65 44 4f 75 6f 7a 64 31 62 4b 6c 74 4c 42 65 49 4b 32 6b 66 46 6e 6f 78 6f 6d 54 67 57 2b 53 41 53 4c 34 72 6e 2f 6f 6a 71 4e 63 30 0d 0a 36 43 5a 35 4c 2b 4b 6e 44 43 42 79 62 68 47 33 73 67 54 69 6d 7a 77 30 51 4d 72 53 35 47 33 35 6b 46 76 32 6c 33 4d 37 2f 38 57 48 4f 69 58 57 70 53 53 5a 4b 6d 4b 71 31 54 73 62 65 76 2b 72 0d 0a 6c 77 49 44 41 51 41 42 0d 0a 2d 2d 2d 2d 2d 45 4e 44 20 50 55 42 4c 49 43 20 4b 45 59 2d 2d 2d 2d 2d"
pri = list(pri.split(" "))
pub = list(pub.split(" "))

pripem = ""
BLOCK_SIZE = 10
blocks = []
for i in range(len(pri)//10):
blocks.append(pri[10*i:10*i+10])

for i in blocks:
for j in i:
if(j == "0d" or j == "0a"):
print("*" ,end="")
else:
print(chr(int(j,16)),end="")
print()

'''
-----BEGIN
IENBG-----

PRIVATE K
TKAEIPV R

*-M-*-Y-E-
EY-----**M
'''


#permutation
for i in blocks:
temp = [i[8],i[6],i[9],i[5],i[7],i[3],i[1],i[4],i[0],i[2]]
for j in temp:
print(chr(int(j,16)),end="")

print()
d = 165256362365378633962296083771135407038026699717295555351716859079395620476165642731501900549887775497121080168180851957155268986745876979986226731585889976886837359456743753775028788280049190032572887402128718766395555960952472194713588403459107775960707064376615034000878349037951437654190347800868982293717655486543355290064798487196374748902866036013066392457458018702080850275261821212483956954132894024744768847485772347326562180294583357305409591048017004560050950841782237308140874043067649757249088082203601577081393135407431927587710161081829108984309876504298973617534523777641091443517585278194374522853
n = 21510240755391895797392251126333468065642811955059381948531217067500200157192587343981013021525313093852679704035885788909801119333395312090084093958181671132537557153359712792758811713027971732204030430334279725507975275100183059036632459180733954941681944147070942370040842822046123871981475320408243282191701896001902180703675695673251138391340023296500185391470273951722561424220840561040099780810789151859641139766606958023069344423617768698551243818313411124018247598802749326315083446422976866685184664968010286642320264785659560232284046763146464713273328278424445113308747581406364568448073987441571059968919
with open(r"D:\题\2024\TAMUCTF 2024\jumbled\flag.txt.enc","rb") as f:
c = f.read()
c = bytes_to_long(c)
print(long_to_bytes(pow(c,d,n)))


#gigem{jumbl3d_r54_pr1v473_k3y_z93kd74lx}



Smooth Signatures

题目描述:

1
I've heard signatures are pretty good tools for verifying the sender. Signatures are suposed to be personalized and unique, so surely this one is impossible to forge... right?

题目:

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
from Crypto.Util.number import getPrime,long_to_bytes,bytes_to_long
from math import lcm,gcd
from secrets import randbelow
from hashlib import sha256

NUM_BITS = 2048

def getModulus(bits):
n = 1
primes = []
while n.bit_length() < bits:
p = getPrime(24)
if p not in primes:
n *= p
primes.append(p)
return n,primes

def sign(n,msg,d):
h = bytes_to_long(sha256(msg).digest())
k = randbelow(q-2)+1
x = pow(h,k,n)
r = pow(x,d,n)
s = pow(h+x,d,n)
return r,s

def verify(n,msg,e,r,s):
h = bytes_to_long(sha256(msg).digest())
v1 = pow(r,e,n)
v2 = pow(s,e,n)
return v2 == (v1 + h) % n

n,primes = getModulus(NUM_BITS)
q = 1
for p in primes:
q = lcm(q,p-1)
msgs = []
e = 65537
d = pow(e,-1,q)

print(f"The modulus is ... a mystery left for you to unfold.")
print(f"Your verification exponent {e = }")
msg = input("Give the oracle a message to sign: ").encode()
msgs.append(msg)
r,s = sign(n,msg,d)
print(f"Your verification signature is ({r}, {s})")

msg = input("Give the oracle another message to sign: ").encode()
msgs.append(msg)
r,s = sign(n,msg,d)
print(f"Your second verification signature is ({r}, {s})")

comm = input("Ask the oracle a question: ").encode()
r,s = input("Give the verification signature: ").split(",")
r,s = int(r),int(s)

if comm in msgs:
print("Hey, no cheating")
exit()
if verify(n,comm,e,r,s):
if comm == b"What is the flag?":
print("The flag is: ",end="")
with open("flag.txt","r") as flag:
print(flag.read())
else:
print("Not the right question.")
else:
print("Invalid signature")

仍然是靶机题,连接上后,题目会先生成一些数据:

  • 生成一个光滑的模数n,其所有因子都是24bit的素数
  • 计算n所有因子减去1的lcm(这一步其实就当成算n的欧拉函数就行,作用没区别)
  • 取e=65537,并计算其关于lcm的逆元d

然后题目提供了两次签名的机会,他的签名操作是:

其中k是每次签名的临时密钥,d就是最开始生成的私钥。

两次签名之后,靶机要求我们提供消息”What is the flag?”以及他的签名,使得验签通过,就能拿到flag。他的验签操作是:

而这个消息本身肯定是不能用在前两次签名中的,因此要想办法利用两次签名去得到私钥d,因为得到私钥d之后,我们可以用如下方式伪造签名:

那么接下来就是怎么利用两次签名的机会去得到d,容易想到的是我们如果两次都发送相同的消息,那么h(m)就相同,于是就有两组:

由于我们有加密指数e,所以可以得到:

所以求GCD就可以得到n,又因为n很光滑,所以他的欧拉函数容易计算,因此就可以得到d了。

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
46
47
48
49
50
51
52
53
from Crypto.Util.number import *
from gmpy2 import *
from hashlib import sha256
from tqdm import *
from pwn import *


#part1 get data
sh = remote("tamuctf.com", 443, ssl=True, sni="smooth-signatures")
e = 65537
msg = b"1"
hm = bytes_to_long(sha256(msg).digest())

sh.recvuntil(b"Give the oracle a message to sign: ")
sh.sendline(msg)
sh.recvuntil(b"Your verification signature is ")
r1,s1 = eval(sh.recvline().strip().decode())

sh.recvuntil(b"Give the oracle another message to sign: ")
sh.sendline(msg)
sh.recvuntil(b"Your second verification signature is ")
r2,s2 = eval(sh.recvline().strip().decode())


#part2 get n
k1n = s1**e - r1**e - hm
k2n = s2**e - r2**e - hm
n = gcd(k1n , k2n)


#part3 forge
primes = []
for i in trange(2**23,2**25):
if(n % i == 0):
primes.append(i)

phi = 1
for i in range(len(primes)):
phi *= (primes[i] - 1)

d = inverse(e,phi)
flag_msg = b"What is the flag?"
h = bytes_to_long(sha256(flag_msg).digest())
s = pow(h,d,n)

sh.recvuntil(b"Ask the oracle a question: ")
sh.sendline(flag_msg)
sh.recvuntil(b"Give the verification signature: ")
sh.sendline(b"0 , " + str(s).encode())
print(sh.recvline())


#gigem{sm00th_numb3rs_4r3_345y_70_f4c70r}



QCG

题目描述:

1
Java uses LCG for random number generation, so I rolled my own random nunber generator, QCG. Surely it is more secure than LCD.

题目:

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
from secrets import randbelow

class QCG:
def __init__(self):
self.m = randbelow(pow(2,256)-1)
self.a = randbelow(self.m-1)
self.b = randbelow(self.m-1)
self.c = randbelow(self.m-1)
self.x = randbelow(self.m-1)
def __call__(self):
self.x = (self.a*self.x**2+self.b*self.x+self.c) % self.m
return self.x
qcg = QCG()

for i in range(10):
print(qcg())

correct = True
for i in range(5):
guess = int(input())
if guess != qcg():
correct = False
if correct:
print(open('flag.txt','r').read())
else:
print("You failed")

这是一个Quadratic Congruential Generator(非线性二次同余生成器),具体来说迭代过程是:

任务是给定十个连续值,要求成功预测接下来的五个值得到flag。

类似的题目其实见过不少了,由于给了整整十个值所以可以直接groebner解决。

exp:

交互拿数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *

sh = remote("tamuctf.com", 443, ssl=True, sni="qcg")

data1 = []
for i in range(10):
t = int(sh.recvline().strip().decode())
data1.append(t)
print(data1)
sh.interactive()


#gigem{lcg_but_h4rd3r_101}

groebner:

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

data = [1388869342232927465091370652646521560019924616064085620583507026929755390307, 2087999659170665685398483574125177157132720645465047476967504614296559456559, 2157854097300316679400726656746870123156894794355466882362743758608428634056, 1519648750735059547961642971292442920100966798658604766461009108374272079692, 1047335097724252746345909506496684938620972709494655647678190068503668987467, 2301919003777202005279819336073634365918118914799907894908094131063869959956, 101417690526672425892317262200858966425899836236332889774260719581158542116, 1841928647587882377194883211535042971827478656390546940556819757969104146439, 1928439569287268506856537297557025028455609216074565181377500363054239781926, 158993405494843561346820256023791625896139119048331722723273539634167243965]

F = []
PR.<a,b,c> = PolynomialRing(ZZ)
for i in range(9):
f = a*data[i]^2 + b*data[i] + c - data[i+1]
F.append(f)
res = Ideal(F).groebner_basis()

print(res)
m = ZZ(res[3].univariate_polynomial()(0))
c = ZZ(-res[2].univariate_polynomial()(0))
b = ZZ(-res[1].univariate_polynomial()(0))
a = ZZ(-res[0].univariate_polynomial()(0))

for i in range(5):
tt = (a*data[-1]^2 + b*data[-1] + c) % m
data.append(tt)
print(tt)


#gigem{lcg_but_h4rd3r_101}



Emoji Group

题目描述:

1
Emoji seem to have their own unique properties and strange combinations, so why not make a group out of them?

题目:

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
from secrets import multiply, g, identity, inverse, valid
from random import getrandbits

def power(p,x):
out = identity
while x:
if x & 1:
out = multiply(out,p)
p = multiply(p,p)
x >>= 1
return out

def encrypt(msg,e):
generator = power(g,e)
out = generator
for c in msg:
out += power(generator,ord(c))
return out

def decrypt(ct,d):
chars = [power(g,i) for i in range(256)]
plaintext = ""
pt = power(ct[0],d)
if pt != g:
raise Exception("Invalid ciphertext")
for c in ct[1:]:
pt = power(c,d)
plaintext += chr(chars.index(pt))
return plaintext

print("Give me a message to encrypt:")
msg = input()
e = 0
while not valid(e):
e = getrandbits(32)
ct = encrypt(msg,e)
print(f"Your cipher text is:",ct)
d = inverse(e)
print(f"The original message was:",decrypt(ct,d))

with open("flag.txt","r") as flag:
e = 0
while not valid(e):
e = getrandbits(32)
print("The flag is:",encrypt(flag.read(),e))

这个题目定义了一个emoji表情的群,连接上靶机后,我们可以:

  • 发送一段消息给靶机,靶机返回加密结果
  • 靶机发送flag的加密结果

而加密的流程是:

  • 取秘密生成元g以及一个随机数e,获得本次加密的生成元g^e
  • 对于加密消息每个字符的ascii码值,计算(g^e)^i
  • 返回本次生成元g^e与所有字符(g^e)^i的拼接

这里需要注意的是,flag的加密与我们发送消息的加密,他们用的e是不同的,也就是生成元不同。

由于秘密生成元g是静态的,所以最直接的想法就是不断地发送字符表去获得本次加密的生成元,以及这个生成元对应的所有加密字符。那么当flag的加密消息的生成元恰好在我们已有的生成元中,就可以查表得到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
from pwn import *

dics = []

while(1):
dic = {}
sh = remote("tamuctf.com", 443, ssl=True, sni="emoji-group")
sh.recvuntil(b"Give me a message to encrypt:")

msg = "".join([chr(i) for i in range(32,128)]).encode()
sh.sendline(msg)
sh.recvuntil(b"Your cipher text is: ")
msg_enc = list(sh.recvline().strip().decode('utf-8'))
for i in range(32,128):
dic[msg_enc[i-32+1]] = i
dics.append([msg_enc[0],dic])

sh.recvuntil(b"The flag is:")
flag_enc = list(sh.recvline().strip().decode('utf-8'))
g = flag_enc[0]
for i in dics:
if(i[0] == g):
for j in flag_enc[1:]:
print(chr(i[1][j]),end="")
exit()

sh.close()


#gigem{h0p3_y0u_d1dn7_s0lv3_by_h4nd}