趁着主办方环境还在 (11.28) 赛后将安洵杯三道题都复现了一遍,难度比较友好。赛后复现过程比较顺利于是水一篇 wp。

# cry1

一个猜数字的签到题,连进去之后硬猜就行了,多试几次就出了

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
from pwn import *
from Crypto.Util.number import *
import gmpy2
import string
import itertools
import hashlib
table = string.ascii_letters + string.digits
context(log_level = 'debug')
ip = '120.78.131.38'
port = 10001
r = remote(ip,port)
def proof():
r.recvuntil(b'SHA256(XXXX')
line = r.recvline()[:-1].decode()
print(line)
tmp = line[line.find(' + ') + 3:line.find(')')]
print(tmp)
aim = line[line.find(':') + 1:]
print(aim)
for i in itertools.product(table,repeat=4):
ans = ''.join(i)
if hashlib.sha256((ans + tmp).encode()).hexdigest() == aim:
print(ans)
r.recvuntil(b'Give Me XXXX:\n')
r.sendline(ans.encode())
return

proof()
r.recvuntil(b"If you guessed right, I'll give you the flag!, You only have 6 chances (1~20)\n")
for _ in range(6):
num = random.randint(1,20)
r.send(str(num).encode())
r.recvline()
r.interactive()

# cry2

附件

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
# -*- coding:utf-8 -*-
import os
import random
import string
import hashlib
import binascii
import socketserver
from Crypto.Cipher import AES

flag = 'D0g3{' + ''.join([random.choice(string.ascii_letters + string.digits) for _ in range(18)]) + '}'


class MyServer(socketserver.BaseRequestHandler):
def proof(self):
random.seed(os.urandom(8))
random_str = ''.join([random.choice(string.ascii_letters + string.digits) for _ in range(20)])
str_sha256 = hashlib.sha256(random_str.encode()).hexdigest()
self.request.sendall(('SHA256(XXXX + %s):%s\n' % (random_str[4:], str_sha256)).encode())
self.request.sendall('Give Me XXXX:\n'.encode())
XXXX = self.request.recv(2048).strip()

if hashlib.sha256((XXXX + random_str[4:].encode())).hexdigest() != str_sha256:
return False

return True

def pad(self, message):
if len(message) % 16 != 0:
return message.ljust((len(message) // 16 + 1) * 16, '0').encode()
else:
return message.encode()

def encrypt(self, key, message):
aes = AES.new(key, AES.MODE_ECB)
return aes.encrypt(message)

def decrypt(self, key, message):
aes = AES.new(key, AES.MODE_ECB)
return aes.decrypt(message)

def handle(self):
if not self.proof():
self.request.sendall(b'Error Hash!')
return

flag1, flag2 = flag[:8], flag[8:]
while True:
self.request.sendall('You can input anything:\n'.encode())
iMessage = self.request.recv(2048).strip().decode()
message = self.pad(flag1 + iMessage + flag2)
cipher = binascii.hexlify(self.encrypt(flag2.encode(), message))
self.request.sendall(('Here is your cipher: %s\n' % cipher).encode())


if __name__ == '__main__':
sever = socketserver.ThreadingTCPServer(('0.0.0.0', 10086), MyServer)
sever.serve_forever()

主要逻辑在 handle 中。主要逻辑是我们输入 iMessage 变量,之后和 flag1flag2 一起 pad,pad 函数用的不是库里面的函数,是题目自己写的,每次将输入的信息用 0 补齐为 16 长度的整数倍。注意 flag 被分成了两部分,第一部分 8 长度,第二部分 16 长度。

测试 pad 函数的作用 (flag 为自行生成的)

1
2
3
4
5
6
7
8
9
10
11
12
13
def pad(message):
if len(message) % 16 != 0:
return message.ljust((len(message) // 16 + 1) * 16, '0').encode()
else:
return message.encode()
flag = 'D0g3{ikq6HOskfLMKqJk825}'
flag1, flag2 = flag[:8], flag[8:]
iMessage = '0000000'
tmp = flag1 + iMessage + flag2
p = pad(tmp)
out = [p[i:i + 16] for i in range(0,len(p),16)]
print(out)
#[b'D0g3{ikq00000006', b'HOskfLMKqJk825}0']

交互次数是无限,需要依靠我们构造 iMessage 变量进而得到 flag。 flag2 作为 AES 的密钥,置于 iMessage 的后半部分,其位置是可以根据我们输入而改变的,而 flag1 相对固定在前面。

第一次尝试,首先观察到 flag 头为 D0g3{ ,这就占据了 5 个长度,后面 flag1 剩下的三个长度可以尝试爆破。

1
2
iMessage = '00000000D0g3{xxx00000000'
#out = [b'D0g3{ikq00000000', b'D0g3{xxx00000000', b'6HOskfLMKqJk825}']

其中 xxx 为爆破范围,这样可以得到 flag1 。但由于题目是交互的爆破的速度很慢,可以尝试多线程进行爆破,但是在比赛中并没有想到。

第二次尝试,从 flag2 入手,前面说了位置可变也就是说 flag2 可以单字节爆破。

1
2
iMessage = '00000000' + '000000000000000' + 'x' + '000000000000000'
#out = [b'D0g3{ikq00000000', b'000000000000000x', b'0000000000000006', b'HOskfLMKqJk825}0']

可以看出分块进行 AES 之后,第二块和第三块是可以进行爆破的,只需不断地改变第二块的的内容即可爆破出 flag2 的内容。

得到 flag2 之后将 flag2 作为密钥即可解得 flag1

# 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
54
from pwn import *
from Crypto.Util.number import *
import gmpy2
import string
import itertools
from tqdm import tqdm
import hashlib
from tqdm import tqdm
from Crypto.Cipher import AES
table = string.ascii_letters + string.digits
# context(log_level = 'debug')
ip = '120.78.131.38'
port = 10086
r = remote(ip,port)
def proof():
r.recvuntil(b'SHA256(XXXX')
line = r.recvline()[:-1].decode()
print(line)
tmp = line[line.find(' + ') + 3:line.find(')')]
print(tmp)
aim = line[line.find(':') + 1:]
print(aim)
for i in itertools.product(table,repeat=4):
ans = ''.join(i)
if hashlib.sha256((ans + tmp).encode()).hexdigest() == aim:
print(ans)
r.recvuntil(b'Give Me XXXX:\n')
r.sendline(ans.encode())
return

proof()
fleg = ''
for i in tqdm(range(1,16)):
for t in table:
payload = '0' * 8 + '0' * (16 - i) + fleg + t + '0' * (16 - i)
# print(payload)
r.recvuntil(b'You can input anything:\n')
r.send(payload.encode())
r.recvuntil(b"Here is your cipher: b'")
cipher = r.recvuntil(b"'")[:-1]
# print(cipher)
p = [cipher[i : i + 32] for i in range(0,len(cipher),32)]
if p[1] == p[2]:
fleg += t
print(fleg)
break
fleg += '}'
key = fleg.encode()
cipher = bytes.fromhex('ee05ba9cf6a3fe5d0681741d0206e9ce7b287f063c78b2d1775af6b4d4768cca')#输入全0第一部分
aes = AES.new(key=key,mode=AES.MODE_ECB)
flag1 = aes.decrypt(cipher)[:8]
flag = flag1 + key
print(flag)
r.interactive()

# cry3

附件

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
import binascii
import hashlib
import os
import random
import socketserver
import string
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.Util.number import getPrime, bytes_to_long

hint = ''
flag = b''
key = os.urandom(16)

GOD = [b'Whitfield__Diffie']


def encrypt(message):
message = pad(message, 16)
aes = AES.new(key, AES.MODE_CBC, iv=b'\x00' * AES.block_size)
return binascii.hexlify(aes.encrypt(message)[-16:])


Authentication = encrypt(b'Whitfield__Diffie')


class CryptoPalace(socketserver.StreamRequestHandler):
def proof1(self):
random.seed(os.urandom(8))
random_str = ''.join([random.choice(string.ascii_letters + string.digits) for _ in range(20)])
str_sha256 = hashlib.sha256(random_str.encode()).hexdigest()
self.request.sendall(('SHA256(XXXX + %s):%s\n' % (random_str[4:], str_sha256)).encode())
self.request.sendall('Give Me XXXX:\n'.encode())
XXXX = self.request.recv(2048).strip()
if hashlib.sha256((XXXX + random_str[4:].encode())).hexdigest() != str_sha256:
return False
return True

def proof2(self):
self.request.sendall(b'You must prove your identity to enter the palace ' + Authentication + b'\n--> ')
name = self.request.recv(1024)
if name in GOD:
self.request.sendall(b'You are not him!\n')
return False
if encrypt(name) != Authentication:
self.request.sendall(b'Wrong, try again......\n')
return False
else:
self.request.sendall(hint.encode() + b"\n")
return True

def DiffieEncrypt(self):
p, q = getPrime(1024), getPrime(1024)
E = [getPrime(512) for _ in range(3)]
e1, e2, e3 = E[0] * E[1], E[0] * E[2], E[1] * E[2]
n = p * q
m = bytes_to_long(flag)
c1, c2, c3 = pow(m, e1, p * q), pow(m, e2, p * q), pow(m, e3, p * q)
return n, e1, e2, e3, c1, c2, c3

def handle(self):
if not self.proof1():
self.request.sendall(b'Error Hash!')
return

if not self.proof2():
self.request.sendall(b'You did not enter the palace\n')
return

self.request.sendall(b'Flag has been encrypted by Diffie\n')
cipher = self.DiffieEncrypt()
self.request.sendall(str(cipher).encode())
return


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


if __name__ == '__main__':
host, port = '127.0.0.1', 10010
server = ThreadedTCPServer((host, port), CryptoPalace)
ThreadedTCPServer.allow_reuse_address = True
ThreadedTCPServer.allow_reuse_port = True
server.serve_forever()

也是 AES 构造题,名字的长度为 17,刚好比一组多一点点,由于是 CBC 模式,多了一个异或。下图是 CBC 模式基本的逻辑。

观察 encrypt 函数,他只输出最后一组密文,也就是 16 长度,给出的 Authentication 是明文为 Whitfield__Diffie 的值,也就是第二块密文的值,两块明文分别为 [b'Whitfield__Diffi',b'e\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f'] ,只需构造一组 payload 让最后一组密文与 Authentication 相等即可。

由于是异或,于是想到再构造两组明文,使得在有 iv 的情况下与直接输入 Whitfield__Diffie 的值相同,即可得到 payload

1
2
tmp = bytes.fromhex('f95651580444fa622eacbccf9770b3ff')
payload = b'Whitfield__Diffie' + b'\x0f' * 15 + xor(b'Whitfield__Diffi',tmp) + b'e'

过了 proof2,之后的共模就没什么问题了,缝合题实锤了

# exp1(pass proof2)

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
import string
import itertools
from tqdm import tqdm
import hashlib
from tqdm import tqdm
from Crypto.Cipher import AES
from pwn import *
from Crypto.Util.number import *
import gmpy2
table = string.ascii_letters + string.digits
# context(log_level = 'debug')
ip = '120.78.131.38'
port = 10010
r = remote(ip,port)
def proof():
r.recvuntil(b'SHA256(XXXX')
line = r.recvline()[:-1].decode()
print(line)
tmp = line[line.find(' + ') + 3:line.find(')')]
print(tmp)
aim = line[line.find(':') + 1:]
print(aim)
for i in itertools.product(table,repeat=4):
ans = ''.join(i)
if hashlib.sha256((ans + tmp).encode()).hexdigest() == aim:
print(ans)
r.recvuntil(b'Give Me XXXX:\n')
r.sendline(ans.encode())
return

proof()
r.recvuntil(b'--> ')
tmp = bytes.fromhex('f95651580444fa622eacbccf9770b3ff')
payload = b'Whitfield__Diffie' + b'\x0f' * 15 + xor(b'Whitfield__Diffi',tmp) + b'e'
print([payload[i:i + 16] for i in range(0,len(payload),16)])
r.send(payload)
r.interactive()

# exp2(common attack)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from Crypto.Util.number import *
import gmpy2
def commonattack(n,e1,e2,c1,c2):
ee,r,s = gmpy2.gcdext(e1,e2)
c = pow(c1,r,n) * pow(c2,s,n) % n
return c,ee

n,e1,e2,e3,c1,c2,c3 = (16486440901534735026079416493978454741894450403370110556952100122086322920105571130678250641915372185243929998120476723068695888574253004920214978656633731730934749677776991026227603288006133099065288831437504027746249576607479919237472214622575391394869006854656815062954603003816034710663602456213441098153480223142198387502026080500041270898650584914586278372694499229218974168098064855994816922143612998429807879165810732197097573046444802021890672546454283234079813058136359660986111588178104564665665995304715176338309871165836308611716088141650281105068996898382923846380193870192761814739770139653510966104751, 65802495601435430884014164157983149653203234623955505267125287361071963315770434981892489974384879593873475142501241145601723614323897723390501569002238572356829910984628103822201481335709487416159863618946979440659041547154114671899486525434770880129425262757246368430750708258408326309187727652234815585419, 84052869410714090640770403216113230839277321067057519586163704644665358725739224991736973759295254145044042178560300574832616468703956602526618738222660187695293011922181988315448711453435542605573956962547119211022419616008368704660669748535610493674183565028329838917447904176113207220156826317340699437593, 100371728547256384949982888634783641299739616870044358688489836785922608820763172440149617632810996373515955734547335005130521600356592560907659378731935960310234428511482927378131465278097987481799823820647036963781201324775423352332305006477403812596207497116554664505759556095955969342792848402226512368627, 15261004370119214706959515968030854191676456800616973538461959351992471464540836687005984690248717559047758489987075986638520702850349454321186989410905549534938155294092893987905452866242184643209787138089818105935430742879317741198594417901200437260065347672412023361258620469658844098562410561877333794386845203425148899503389833324831538953801957720002237277374463904872142541483848759043201174370604819350967351480308608451984840477429244165300686159356916664991123401159451171030927965396910313264155646628985987191742297420283292174316356246349668749837737731953899269189198217168274383101947026425204456838160, 71541617104238844958487883021039355278444019837455251172339485579078558869398357227856788593394214298852960639376075294395532407694214339231233954736459666326541522892558895015959786142034356758961868446449789420201526410878223759959717267639969116877402196009127044915222666560782895827404862951164041265622628489520482847258656360871516832377682501732333658245587098059235130566229239937380443671627999879470372170836594286189086444701729878229745439484364124684303687046151033508502467956620647993774042769260686510115761978384809134836689414925273265255556855227957345350020071650407106382453330165304050197474, 4766757679370684627017583733506550957309188382850543696266351427239582260983573264265386195971548917594018584776176071576579345048652554955857423115095296425367939489411211052506084195876524626560037042894405386200124478821492919219428968603842929228479615125568877310704339398411026343270768242395465278977326077353312529870220454930150817570343099012869314391994452805895801588178295809919103392518876039767473895127244168371888478145986283201565121940213119882431394835910683821456518678860431760302804169923498220501378863302643422731675387021209640484519081168169948208503539321061060436845781388861085417706035)

cc1,ee1 = commonattack(n,e1,e2,c1,c2)
cc2,ee2 = commonattack(n,e1,e3,c1,c3)
m,_ = commonattack(n,ee1,ee2,cc1,cc2)
print(long_to_bytes(m))
#b'D0g3{New_3ra_@f_PK_Crypt0graphy_1976}'
編集日 閲覧数

*~( ̄▽ ̄)~[お茶]を一杯ください

tsuppari Alipay

Alipay

tsuppari PayPal

PayPal