NKCTF2024 复现+wp
NKCTF:
Crypto:
1.eZ_Math:
这道题经过分析做了出来:
题目:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24from Crypto.Util.number import *
from secret import flag
m1, m2 = bytes_to_long(flag[:len(flag)//2]), bytes_to_long(flag[len(flag)//2:])
p, q, r, s = [getStrongPrime(512) for _ in range(4)]
e = 0x10001
n = p * q * r * s
x = pow(q + r, p, n)
y = pow(p * q + r, p, n)
z = pow(s + 1, m1, s ** 3)
c = pow(m2, e * (s - 1), n)
print(f'{n = }')
print(f'{x = }')
print(f'{y = }')
print(f'{z = }')
print(f'{c = }')
# n = 16063619267258988011034805988633616492558472337115259037200126862563048933118401979462064790962157697989038876156970157178132518189429914950166878537819575544418107719419007799951815657212334175336430766777427972314839713871744747439745897638084891777417411340564312381163685003204182743581513722530953822420925665928135283753941119399766754107671729392716849464530701015719632309411962242638805053491529098780122555818774774959577492378249768503656934696409965037843388835948033129997732058133842695370074265039977902884020467413323500218577769082193651281154702147769044514475692164145099161948955990463002411473013
# x = 3021730035236300354492366560252387204933590210661279960796549263827016146230329262559940840168033978439210301546282150367717272453598367244078695402717500358042032604007007155898199149948267938948641512214616076878271433754986480186150178487625316601499002827958344941689933374158456614113935145081427421623647242719093642478556263121508238995676370877385638074444859047640771188280945186355013165130171802867101829647797879344213688981448535289683363612035513789240264618036062440178755665951650666056478493289870170026121826588708849844053588998886259091357236645819074078054595561158630194224419831088510266212458
# y = 8995787142441643101775260550632842535051686960331455373408888374295557050896156890779515089927839904014859222004906681231525326673182671984194300730575609496770604394218160422560576866112460837985407931067753009696969997384839637927957848613356269534870170452152926447601781637641134982178028922559652443398183848786034348994249923007092159192374765197460466878587635412657807328348343062302127490267456095927890461140420639805398464266081441243108883599713672104446500850203779995739675784794478089863001309614674686652597236324659979849324914804032046113978246674538411441434320732570934185579553749616238819583998
# z = 1283646988194723153191718393109711130382429329041718186548715246082834666179475883560020086589684603980734305610989683434078096863563033623169666389076830792095374856743015929373461198718962686411467443788047511292138922700655772772117855226419561159782734009961921473456332468653898105909729309377890721920937410781006337057478451806364879679045839945032594716202888196404203782734864187890231653321470085251
# c = 4988583141177813116287729619098477713529507701428689219486720439476625736884177254107631282807612305211904876847916760967188201601494592359879509876201418493870112712105543214178376471651715703062382025712952561985261461883133695993952914519494709871429166239968478488380137336776740647671348901626710334330855078254188539448122493675463406596681080368929986034772169421577420193671300532508625180845417164660544286332963072804192276425664877337357353975758574262657585309762422727680851018467657523970318042829660721433987195369353660020476598195375492128671951807024027929490113371463210453342974983253996717176870
分析一下源码,加密逻辑是这样的
p , q , r , s 都是 512 位的素数
分析一下:
于是
我当时没想到模 pq 可以(我只想到了模 p ),但是算出来是 1024 位,回去又算了一下发现了。
然后:
根据二项式定理:
但 $m_2$ 想了一会儿,发现整个模 p 不行(不知道为啥),最后才知道模 pq 可以
之后取 s-1 除以最大公因数计算模逆元得出 $m^{4}$ ,再使用 iroot 开根得:$m_2$1
flag:nkctf{cb5b7392-cca4-4ce2-87e7-930cf6b29959}
2.GGH
1 | from sage.all import * |
一道典型的格上问题,我的格子构造对了,但不知道要简化,所以一直 runtime error
错误示范:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16from sage.all import *
import re
s2n=lambda x: [int(x) for x in re.findall(r"\-?\d+\.?\d*",x)]
f=open("./pubkey.txt","r").readlines()
W = [s2n(f[i]) for i in range(260)]
c=[14697, -103356, 74779, 95622, -11650, 524678, -258697, 388940, -399559, -437273, 267702, -978292, 686059, 835272, 809650, 759647, -573204, 989361, -696040, -694133, -831908, -477981, 724090, -1228907, -120150, -774399, -591584, -1291399, 254792, -134668, -109064, 198801, -749156, -816935, -1015728, -899139, 957539, 797980, 622496, 636293, 1795867, 688081, 1327891, 94650, -1563715, 789969, -2006496, 1400404, -1134347, -736944, 148919, -665150, -1081234, -973028, 881767, 3307694, -1584719, -805716, 1360628, -896122, -862741, -1014915, 848040, 8791, 1010424, -627001, -1915331, 1734060, -578582, -1250807, 1002361, -2555983, 382933, 876467, -517111, -890007, 1120942, 328836, 1070931, 3060471, 1282311, 900451, -1368524, 2383556, -100407, 1472198, -636598, 1481797, 1250775, 279604, -880140, 940241, 474972, 854830, -4072298, 440151, -622028, 2260879, -2738469, 974549, 103401, -2590853, -12923, -1612830, 1275543, -693632, -2681181, 536356, -199335, -638932, -3363587, 3897884, 1328506, -1114079, -553394, -2175335, -56629, 1842637, 48649, 1561646, 1874518, 514713, -1927387, -20432, -915037, -840193, 870019, 4671103, 1119753, 837031, 420138, 2875929, 1527979, 363294, -1591329, -2499404, -1504185, -161931, -1799591, 245185, 2222818, -3921583, -1357181, 453979, 1872321, -297401, 2490480, 2786635, -2838351, -737681, -2545091, -1696116, 425623, 147974, -1739229, -3945245, 1255070, -1820074, -2190869, -1254018, 2211542, 3565405, -1063183, 1179506, 1707422, 3324989, -105580, 4502495, 332793, -1505723, -493912, 756291, 2609637, -396470, 3318630, 1424448, -871428, -2341694, -1474359, 78429, 4140695, -1223022, 1726067, -1830797, 2252423, 634142, -1162911, 657946, 1161669, 314917, 637551, 128154, 1944407, 467548, -2980735, -1097568, -2143663, -251256, 2520771, -1815714, -1814059, 305958, -2599700, 1076361, -2794868, 2798862, -3126794, 2466492, 2951889, 2541846, -2737433, 2364461, 519766, 522438, -728868, -1180942, 1205432, 2089627, -844456, -427481, 1117668, 1503704, 4061528, -889185, 1678865, 4025442, 871542, -3081410, 2014316, 667040, -3925491, 1776813, 1304258, 2248592, 2204897, 1250071, 883217, -1259669, -2071139, -3292080, -1706324, 626620, 1202724, -361433, 1343175, 1486927, -2365569, 4589072, -2439656, -1727340, 2108002, -1245002, -1392031, -2058070, -814170, -116156, -878803, -1503880, 3505220, -891921]
L=Matrix.zero(261,260)
for i in range(260):
for j in range(260):
L[i,j]=W[i][j]
for j in range(260):
L[-1,j]=c[j]
M=L.BKZ()
with open('2.txt','w')as f:
f.write(str(M))
因为 r 足够小,所以经过 BKZ 求出来的最短向量很可能就是 r (但不知道可以通过前11位明文简化)
实际:
因此再用:
求出 r ,带回原式计算得:
以下是我看了 wp 后自己改的脚本: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
56from sage.all import *
import re
from tqdm import tqdm
s2n=lambda x: [int(x) for x in re.findall(r"\-?\d+\.?\d*",x)]
f=open("./pubkey.txt","r").readlines()
B1 = [s2n(f[i]) for i in range(11)]
B2 = [s2n(f[i]) for i in range(11,260)]
B1=Matrix(ZZ,B1)
c=[14697, -103356, 74779, 95622, -11650, 524678, -258697, 388940, -399559, -437273, 267702, -978292, 686059, 835272, 809650, 759647, -573204, 989361, -696040, -694133, -831908, -477981, 724090, -1228907, -120150, -774399, -591584, -1291399, 254792, -134668, -109064, 198801, -749156, -816935, -1015728, -899139, 957539, 797980, 622496, 636293, 1795867, 688081, 1327891, 94650, -1563715, 789969, -2006496, 1400404, -1134347, -736944, 148919, -665150, -1081234, -973028, 881767, 3307694, -1584719, -805716, 1360628, -896122, -862741, -1014915, 848040, 8791, 1010424, -627001, -1915331, 1734060, -578582, -1250807, 1002361, -2555983, 382933, 876467, -517111, -890007, 1120942, 328836, 1070931, 3060471, 1282311, 900451, -1368524, 2383556, -100407, 1472198, -636598, 1481797, 1250775, 279604, -880140, 940241, 474972, 854830, -4072298, 440151, -622028, 2260879, -2738469, 974549, 103401, -2590853, -12923, -1612830, 1275543, -693632, -2681181, 536356, -199335, -638932, -3363587, 3897884, 1328506, -1114079, -553394, -2175335, -56629, 1842637, 48649, 1561646, 1874518, 514713, -1927387, -20432, -915037, -840193, 870019, 4671103, 1119753, 837031, 420138, 2875929, 1527979, 363294, -1591329, -2499404, -1504185, -161931, -1799591, 245185, 2222818, -3921583, -1357181, 453979, 1872321, -297401, 2490480, 2786635, -2838351, -737681, -2545091, -1696116, 425623, 147974, -1739229, -3945245, 1255070, -1820074, -2190869, -1254018, 2211542, 3565405, -1063183, 1179506, 1707422, 3324989, -105580, 4502495, 332793, -1505723, -493912, 756291, 2609637, -396470, 3318630, 1424448, -871428, -2341694, -1474359, 78429, 4140695, -1223022, 1726067, -1830797, 2252423, 634142, -1162911, 657946, 1161669, 314917, 637551, 128154, 1944407, 467548, -2980735, -1097568, -2143663, -251256, 2520771, -1815714, -1814059, 305958, -2599700, 1076361, -2794868, 2798862, -3126794, 2466492, 2951889, 2541846, -2737433, 2364461, 519766, 522438, -728868, -1180942, 1205432, 2089627, -844456, -427481, 1117668, 1503704, 4061528, -889185, 1678865, 4025442, 871542, -3081410, 2014316, 667040, -3925491, 1776813, 1304258, 2248592, 2204897, 1250071, 883217, -1259669, -2071139, -3292080, -1706324, 626620, 1202724, -361433, 1343175, 1486927, -2365569, 4589072, -2439656, -1727340, 2108002, -1245002, -1392031, -2058070, -814170, -116156, -878803, -1503880, 3505220, -891921]
c=vector(ZZ,c)
for i in tqdm(range(70,80)):
gift = str(hex(i)).zfill(5).encode('utf-8')
leak_m = gift + b'nkctf{'
leak = [i for i in leak_m]
m1=vector(ZZ,leak)
c1=c-m1*B1
L=Matrix.zero(250,260)
for i in range(249):
for j in range(260):
L[i,j]=B2[i][j]
for j in range(260):
L[-1,j]=c1[j]
try:
M=L.BKZ()
except RuntimeError:continue
r=M[0]
if r[0]==r[1]:
B2=Matrix(ZZ,B2)
B=block_matrix([[B1],[B2]])
m=B.solve_left(c-r)
flag=''.join(chr(abs(i))for i in m[:49])
if 'nkctf{' in flag:
print(flag)
break
else:
continue
'''
for i in range(133,261):
m=M[i]
for x in m:
print(chr(abs(x)),end="")
'''
'''
with open('5.txt','w')as f:
f.write(str(M))
flag=""
'''
'''
for i in m:
print(chr(i),end="")
'''
另一方法:爆破 delta1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24import re
from sage.all import *
from tqdm import tqdm
s2n=lambda x: [int(x) for x in re.findall(r"\-?\d+\.?\d*",x)]
f=open("./pubkey.txt","r").readlines()
B=[s2n(f[i])for i in range(260)]
c=[14697, -103356, 74779, 95622, -11650, 524678, -258697, 388940, -399559, -437273, 267702, -978292, 686059, 835272, 809650, 759647, -573204, 989361, -696040, -694133, -831908, -477981, 724090, -1228907, -120150, -774399, -591584, -1291399, 254792, -134668, -109064, 198801, -749156, -816935, -1015728, -899139, 957539, 797980, 622496, 636293, 1795867, 688081, 1327891, 94650, -1563715, 789969, -2006496, 1400404, -1134347, -736944, 148919, -665150, -1081234, -973028, 881767, 3307694, -1584719, -805716, 1360628, -896122, -862741, -1014915, 848040, 8791, 1010424, -627001, -1915331, 1734060, -578582, -1250807, 1002361, -2555983, 382933, 876467, -517111, -890007, 1120942, 328836, 1070931, 3060471, 1282311, 900451, -1368524, 2383556, -100407, 1472198, -636598, 1481797, 1250775, 279604, -880140, 940241, 474972, 854830, -4072298, 440151, -622028, 2260879, -2738469, 974549, 103401, -2590853, -12923, -1612830, 1275543, -693632, -2681181, 536356, -199335, -638932, -3363587, 3897884, 1328506, -1114079, -553394, -2175335, -56629, 1842637, 48649, 1561646, 1874518, 514713, -1927387, -20432, -915037, -840193, 870019, 4671103, 1119753, 837031, 420138, 2875929, 1527979, 363294, -1591329, -2499404, -1504185, -161931, -1799591, 245185, 2222818, -3921583, -1357181, 453979, 1872321, -297401, 2490480, 2786635, -2838351, -737681, -2545091, -1696116, 425623, 147974, -1739229, -3945245, 1255070, -1820074, -2190869, -1254018, 2211542, 3565405, -1063183, 1179506, 1707422, 3324989, -105580, 4502495, 332793, -1505723, -493912, 756291, 2609637, -396470, 3318630, 1424448, -871428, -2341694, -1474359, 78429, 4140695, -1223022, 1726067, -1830797, 2252423, 634142, -1162911, 657946, 1161669, 314917, 637551, 128154, 1944407, 467548, -2980735, -1097568, -2143663, -251256, 2520771, -1815714, -1814059, 305958, -2599700, 1076361, -2794868, 2798862, -3126794, 2466492, 2951889, 2541846, -2737433, 2364461, 519766, 522438, -728868, -1180942, 1205432, 2089627, -844456, -427481, 1117668, 1503704, 4061528, -889185, 1678865, 4025442, 871542, -3081410, 2014316, 667040, -3925491, 1776813, 1304258, 2248592, 2204897, 1250071, 883217, -1259669, -2071139, -3292080, -1706324, 626620, 1202724, -361433, 1343175, 1486927, -2365569, 4589072, -2439656, -1727340, 2108002, -1245002, -1392031, -2058070, -814170, -116156, -878803, -1503880, 3505220, -891921]
B=Matrix(ZZ,B)
c=vector(ZZ,c)
for i in tqdm([1,3,7]):
r=[]
k=4*i-2
r+=[i-2]*(260//k)
r+=[i-1]*( ((k-2)*260) // (2*k))
r+=[i]*(260//k)
r+=[i+1]*( ((k-2)*260) // (2*k))
r=vector(ZZ,r)
m=B.solve_left(c-r)
try:
flag=''.join(chr(abs(i)) for i in m[:49])
if "nkctf{" in flag:
print(flag)
break
except TypeError:continue
flag:1
flag:nkctf{G0od_Y0u_Ar3_Kn0W_Ggh_Crypt0syst3m!!!}
3.EZ_random
1 | from random import randint |
用了一个十分巧妙的降维:
我们来解决一个子问题:
恢复 key 的一个 bit
定义一个 GF(2) 上的 m 维向量 $\mathbf{b}=(b_0,\cdots ,b_{m-1})$ ,知道 $l=\sum_{i=0}^{m-1}b_i$ ,则对于一个 GF(2) 上的 $(m-1)\times m$ 维的矩阵 M,
若已知$\mathbf{c}=M\mathbf{b}$, 则可恢复出向量b
关系1.
$M^{\prime}$ 是一个 $m\times (m-1)$ 维的矩阵
此时 $\mathbf{b}=(b_0,\cdots,b_{m-1})=(l,0,\cdots,0)+M^{\prime} \cdot (b_1,\cdots,b_{m-1})$
关系2:
$M=[\mathbf{m_0},\mathbf{m_1},\cdots,\mathbf{m_{m-1}}],A=MM^{\prime}=[\mathbf{m_0}+\mathbf{m_1},\cdots,\mathbf{m_0}+\mathbf{m_{m-1}}]$
因此,构造出 $M^{\prime}$ 即可求得 A ,只需知道 l 即可还原 key
根据 key 的生成代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class MyBytesGen:
def __init__(self, seed):
self.state = seed
def next_byte(self):
offset = randint(0, 8)
self.state = (self.state >> offset | self.state << (8 - offset)) & 0b11111111
self.state = self.state ^ 0b11000001
return bytes([self.state])
def get_key(self, num):
key = b""
for _ in range(num):
key += self.next_byte()
return key
可知经一次 next_byte ,1 的奇偶个数会发生交替变化, 因此在 GF(2) 上,每个字节的 l 分别为 1 或 0
因此 l 便已知:
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
47from sage.all import *
from Crypto.Util.number import *
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from hashlib import sha256
import re
l=vector(GF(2),[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]*32)
s2n=lambda x: [int(x) for x in re.findall(r"\-?\d+\.?\d*",x)]
f=open("output.txt","r").readlines()
c="a9ebc27bdc700ff2fb68489dc63196fa35ec4ceecb9e7981e8bf8420d3fb3117bbc8fe290f163ccc31f9365bf795b728"
T=s2n(f[1])
print(len(T))
R=int(f[2])
R=vector(GF(2),[int(j) for j in bin(R)[2:].zfill(7*64)])
M=Matrix(GF(2),7*64,8*64)
for i in range(7*64):
temp=[int(j) for j in bin(T[i])[2:].zfill(8*64)]
M[i]=temp
MM=Matrix(GF(2),7*64,8*64)
for i in range(64):
for j in range(7):
Temp=[0]*64*8
Temp[8*i]=1
Temp[8*i+j+1]=1
MM[7*i+j]=vector(GF(2),Temp)
MM=MM.T
A=M*MM
'''
N=[0]*64*7
for i in range(0,7*64,16):
for j in range(8):
N[i+j]=M.T[0,i+j]
for k in range(8,16):
N[i+k]=0
N=vector(GF(2),N)
print(len(N))
print(len(R))
'''
C=R+M*l
b=A.solve_right(C)
B=l+MM*b
key="".join([str(j) for j in B])
print(key)
key=long_to_bytes(int(key,2))
cipher = AES.new(sha256(key).digest()[:16], AES.MODE_ECB)
dec = cipher.decrypt(bytes.fromhex(c))
print(dec)
flag:1
flag:NKCTF{1ab1e94f-a699-431b-8f17-8aabdea55ec6}
4.drsn
main.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
75import os
import signal
import random
import string
import hashlib
from utils import RSN
flag = os.getenv('FLAG')
LIMIT = 256
ROUND = 30
chaos = 1337 * 777
spirit = 3
def proof_of_work():
ss = ''.join(random.choices(string.ascii_letters + string.digits, k=20))
sh = hashlib.sha256(ss.encode()).hexdigest()
print(f"| sha256(XXXX + {ss[4:]}) == {sh}")
prefix = input("| XXXX>")
return prefix == ss[:4]
def task_bit(bit: int, n: int):
key, seed = [os.urandom(16) for _ in range(2)]
enc = b''.join(
RSN(key=key, seed=seed).encrypt(
''.join(random.choices(string.printable, k=1)).encode()
) for _ in range(n)
)
if bit:
enc = os.urandom(16*n)
return enc
def challenge(bit: int, n: int):
global chaos
for i in range(LIMIT):
print('| Menu:\n| [H]it\n| [S]tand\n| [Q]uit')
inp = input('| >').lower()
if inp == 'h':
if chaos < n:
raise Exception('| out of chaos')
chaos -= n
print(f'| CARD #{i + 1}\n| CHAOS: {hex(chaos)}')
print(task_bit(bit, n))
elif inp == 's':
break
else:
raise Exception('| quit')
b = int(input('| b>'))
return b == bit
if __name__ == "__main__":
try:
assert proof_of_work()
signal.alarm(1337 + 133+7 + 13+37 + 1+337)
print("🏁 Good luck, good luck.")
for r in range(ROUND):
print(f'| ROUND #{r + 1}\n| CHAOS: {hex(chaos)}\n| SPIRIT: {spirit}')
n = min(abs(int(input('| n>'))) + 1, LIMIT)
if challenge(random.randint(0, 1), n):
print('| 😎Accepted')
continue
print('| 💀Rejected')
spirit -= 1
assert spirit
print(f'🏁 {flag}')
except:
print('| FAIL')
exit()
utils.py1
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
42import os, random
from Crypto.Util.number import long_to_bytes as l2b, bytes_to_long as b2l
class RSN:
ROUNDS = 7
BLOCK_SIZE = 16
def __init__(self, key: bytes, seed: bytes) -> None:
assert len(key) == self.BLOCK_SIZE
self.key = key
self.rng = random.Random()
self.rng.seed(seed)
def pad(self, msg: bytes) -> bytes:
return msg + os.urandom(self.BLOCK_SIZE - len(msg) % self.BLOCK_SIZE)
def xor(self, a: bytes, b: bytes) -> bytes:
return bytes([a[i] ^ b[i] for i in range(self.BLOCK_SIZE)])
def split(self, b: bytes) -> list[int]:
return list(map(int, bin(b2l(b))[2:].zfill(self.BLOCK_SIZE * 8)))
def unite(self, l: list[int]) -> bytes:
return l2b(int(''.join(map(str, l)), 2), self.BLOCK_SIZE)
def encrypt_block(self, block: bytes) -> bytes:
for _ in range(self.ROUNDS - 1):
block = self.split(block); self.rng.shuffle(block); block = self.unite(block)
block = self.xor(block, self.key)
#
block = self.split(block); random.shuffle(block); block = self.unite(block)
return block
def encrypt(self, msg: bytes) -> bytes:
pt = self.pad(msg)
ct = b''
for i in range(0, len(pt), self.BLOCK_SIZE):
block = pt[i: i + self.BLOCK_SIZE]
ct += self.encrypt_block(block)
return ct
1.爆破proof_of_work:
1 | import string |
2.代码审计:
关键在这个函数:1
2
3
4
5
6
7
8
9
10def task_bit(bit: int, n: int):
key, seed = [os.urandom(16) for _ in range(2)]
enc = b''.join(
RSN(key=key, seed=seed).encrypt(
''.join(random.choices(string.printable, k=1)).encode()
) for _ in range(n)
)
if bit:
enc = os.urandom(16*n)
return enc
如果 bit 是 0,那么第一个字符是一个 printable ,意味着首位一定是 0
即使经过 RSN 过程随机打乱,其是 0 的概率也会大于 bit=1 的情况
(据学长说后面还有需要考虑的地方,但是感觉有点复杂,回头再看)
这里直接粘 NKCTF 第一名的 WP
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
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
119from pwn import remote, context, log, process
# import mbruteforce from pwnlib
from pwnlib.util.iters import mbruteforce
from Crypto.Util.number import long_to_bytes, bytes_to_long
import string
import hashlib
# node.nkctf.yuzhian.com.cn:37755
io = remote('node.nkctf.yuzhian.com.cn', 32291)
# node.nkctf.yuzhian.com.cn:32291
# io = process(["python3", "main.py"])
# context.log_level = 'DEBUG'
def solve_pow(io:remote):
io.recvuntil(b"sha256(XXXX + ")
suffix = io.recvuntil(b") == ", drop=True)
target = io.recvuntil(b"\n", drop=True).decode()
log.info(f"suffix: {suffix}, target: {target}")
sol = mbruteforce(lambda x: hashlib.sha256(x.encode() + suffix).hexdigest() == target,
string.ascii_letters + string.digits,
4)
io.sendlineafter(b"XXXX>", sol.encode())
log.info(f"solved pow: {sol}")
return True
def challenge_func(option):
io.recvuntil(b"[Q]uit\n")
io.sendlineafter(b">", option.encode())
if option == "s":
return None
io.recvline()
io.recvline()
return io.recvuntil(b"\n", drop=True)
def to_bits(s):
return ''.join(format(x, '08b') for x in s)
def compute_diff(res):
diff = 0
table01 = {}
table01["1"] = 0
table01["0"] = 0
for block in range(0, len(res), 16):
rblock = res[block:block+16]
bres = to_bits(rblock)
cnt1 = bytes_to_long(rblock).bit_count()
cnt0 = 128 - cnt1
table01["1"] += cnt1
table01["0"] += cnt0
diff += abs(table01["1"] - table01["0"])
return diff
def check_fix_bit(res:bytes):
table = {}
bit = 0
n = 1
for block in range(0, len(res), 16):
rblock = res[block:block+16]
bres = to_bits(rblock)
for i, c in enumerate(bres):
if i not in table:
table[i] = [0,0]
else:
table[i][int(c)] += 1
for k, v in table.items():
if 0 in v:
print(k, v)
print(f"fixed bit: {k = }")
return True
print(k, v)
return False
def rounds_func(n, times=1, bound=10000):
io.recvuntil(b"ROUND #")
round_num = int(io.recvuntil(b"\n", drop=True))
log.info(f"round_num: {round_num}")
io.recvuntil(b"CHAOS: ")
chaos = int(io.recvuntil(b"\n", drop=True), 16)
log.info(f"chaos: {chaos}")
io.recvuntil(b" SPIRIT: ")
spirit = int(io.recvuntil(b"\n", drop=True))
log.info(f"spirit: {spirit}")
diff = 0
io.sendlineafter(b"n>", str(n).encode())
for i in range(times):
tmp = challenge_func("h")
bytes_res = eval(tmp.decode())
diff += compute_diff(bytes_res)
if diff > bound:
guess_bit = 0
else:
guess_bit = 1
log.info(f"bytes_res: {guess_bit = } with {diff = }")
challenge_func("s")
io.sendlineafter(b"b>", str(guess_bit).encode())
response = io.recvuntil(b"\n", drop=True).decode("utf-8")
if "Accept" in response:
log.info(f"round {round_num} accepted")
return True
elif "Reject" in response:
log.info(f"round {round_num} rejected")
else:
log.info(f"round {round_num} error")
return False
solve_pow(io)
n = 256
t = 50
lb = 10000
ROUND = 30
spirit = 3
for i in range(ROUND):
rounds_func(n, t, lb)
print(io.recvline())
io.interactive()
5.fly to the hyper
1 | from Crypto.Util.number import * |
可以根据论文求解亏格为 2 且基域为 GF(2) 的超椭圆曲线的阶:
https://www.math.uwaterloo.ca/~ajmeneze/publications/hyperelliptic.pdf1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20from sage.all import *
def Hyper_Jacobian_order(n,q=2):
F=GF(q)
u=var('u')
PP=PolynomialRing(F,'u')
h=PP(u**2+u)
f=PP(u**5+u**3+1)
H=HyperellipticCurve(f,h)
M1,M2=H.count_points_exhaustive(2)
a1=M1-1-q
a2=(M2-1-q**2+a1**2)//2
X=var('X')
y1,y2=solve([X**2+a1*X+(a2-2*2)==0],X)
y1,y2=y1.rhs(),y2.rhs()
alpha1=solve([X**2-y1*X+q==0],X)[0].rhs()
alpha2=solve([X**2-y2*X+q==0],X)[0].rhs()
Nn=int(abs(1-alpha1**n)**2*abs(1-alpha2**n)**2)
return Nn
n=180
print(Hyper_Jacobian_order(n))
(后续的再学一学再更)
Misc:
1.signin
签到得(没抢到一血)
base64 解一下:d3jlhbPms6jlhazkvJflj7fvvJrnjI7liIPlrp7pqozlrqTvvIzlj5HpgIHlj6Pku6QgTktDVEYyMDI0X3NpZ25Jbg==
发公众号就行1
flag:nkctf{hell0_w41coMe_2_NKCTF_2024}
2.Webshell_pro
队友看了每一个返回包并获取了信息,我负责通过 RSA 过程解决返回包中的加密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//1 whoami
root
//2 应该是ifconfig?
/bin/sh: 1: ipconfig: not found
//3 ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1460
inet 172.22.161.159 netmask 255.255.240.0 broadcast 172.22.175.255
inet6 fe80::215:5dff:fe18:b845 prefixlen 64 scopeid 0x20<link>
ether 00:15:5d:18:b8:45 txqueuelen 1000 (Ethernet)
RX packets 26778 bytes 10199358 (10.1 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 1240 bytes 175322 (175.3 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
//4 ls /
Docker
bin
boot
dev
etc
home
init
lib
lib32
lib64
libx32
lost+found
media
mnt
mysql_data
opt
proc
root
run
sbin
snap
srv
sys
tmp
usr
var
wslOHicoG
wslbmJCJF
wslgCJNfE
wslhaGDbD
//5 ls /root
Compressed
Desktop
Documents
Downloads
FLAG
Music
Pictures
Public
Templates
Videos
WSL
//6 ls /root/FLAG
hint.py
小明的日记.txt
//7 cd /root/FLAG
null
//8 cat 小明的日记.txt'
cat: 小明的日记.txt: No such file or directory
//9 cd /root/FLAG && base64 小明的日记.txt'
FLAG is NOT HERE!!!!!!!!!!!
PASSWORD:
Password-based-encryption
//10 cd /root/FLAG && base64 hint.py
import base64
import libnum
from Crypto.PublicKey import RSA
pubkey = """-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCK/qv5P8ixWjoFI2rzF62tm6sDFnRsKsGhVSCuxQIxuehMWQLmv6TPxyTQPefIKufzfUFaca/YHkIVIC19ohmE5X738TtxGbOgiGef4bvd9sU6M42k8vMlCPJp1woDFDOFoBQpr4YzH4ZTR6Ps+HP8VEIJMG5uiLQOLxdKdxi41QIDAQAB
-----END PUBLIC KEY-----
"""
prikey = """-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIr+q/k/yLFaOgUjavMXra2bqwMWdGwqwaFVIK7FAjG56ExZAua/pM/HJNA958gq5/N9QVpxr9geQhUgLX2iGYTlfvfxO3EZs6CIZ5/hu932xTozjaTy8yUI8mnXCgMUM4WgFCmvhjMfhlNHo+z4c/xUQgkwbm6ItA4vF0p3GLjVAgMBAAECgYBDsqawT5DAUOHRft6oZ+//jsJMTrOFu41ztrKkbPAUqCesh+4R1WXAjY4wnvY1WDCBN5CNLLIo4RPuli2R81HZ4OpZuiHv81sNMccauhrJrioDdbxhxbM7/jQ6M9YajwdNisL5zClXCOs1/y01+9vDiMDk0kX8hiIYlpPKDwjqQQJBAL6Y0fuoJng57GGhdwvN2c656tLDPj9GRi0sfeeMqavRTMz6/qea1LdAuzDhRoS2Wb8ArhOkYns0GMazzc1q428CQQC6sM9OiVR4EV/ewGnBnF+0p3alcYr//Gp1wZ6fKIrFJQpbHTzf27AhKgOJ1qB6A7P/mQS6JvYDPsgrVkPLRnX7AkEAr/xpfyXfB4nsUqWFR3f2UiRmx98RfdlEePeo9YFzNTvX3zkuo9GZ8e8qKNMJiwbYzT0yft59NGeBLQ/eynqUrwJAE6Nxy0Mq/Y5mVVpMRa+babeMBY9SHeeBk22QsBFlt6NT2Y3Tz4CeoH547NEFBJDLKIICO0rJ6kF6cQScERASbQJAZy088sVY6DJtGRLPuysv3NiyfEvikmczCEkDPex4shvFLddwNUlmhzml5pscIie44mBOJ0uX37y+co3q6UoRQg==
-----END PRIVATE KEY-----
"""
pubkey = RSA.import_key(pubkey)
prikey = RSA.import_key(prikey)
n = pubkey.n
def enc_replace(base64_str: str):
base64_str = base64_str.replace("/", "e5Lg^FM5EQYe5!yF&62%V$UG*B*RfQeM")
base64_str = base64_str.replace("+", "n6&B8G6nE@2tt4UR6h3QBt*5&C&pVu8W")
return base64_str.replace("=", "JXWUDuLUgwRLKD9fD6&VY2aFeE&r@Ff2")
def encrypt(plain_text):
# 小明的日记
cipher_text = b""
for i in range(0, len(plain_text), 128):
part = plain_text[i:i+128]
enc = libnum.n2s(pow(libnum.s2n(part), prikey.d, n))
cipher_text += enc
return enc_replace(base64.b64encode(cipher_text).decode())
if __name__ == '__main__':
m = b"cmd"
print(f"小明的日记: {m}")
c = encrypt(m)
print(f"小明的日记: {c}")
//11 echo U2FsdGVkX1+SslS2BbHfe3c4/t/KxLaM6ZFlOdbtfMHnG8lepnhMnde40tNOYjSvoErLzy0csL7c5d4TlMntBQ== > /root/FLAG/flag.txt
null
//12 ls /root/FLAG
flag.txt
hint.py
小明的日记.txt
//13 echo Good Luck! ByeBye~
Good Luck! ByeBye~
解密脚本: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
51import base64
from Crypto.Util.number import *
import libnum
from Crypto.PublicKey import RSA
pubkey = """-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCK/qv5P8ixWjoFI2rzF62tm6sDFnRsKsGhVSCuxQIxuehMWQLmv6TPxyTQPefIKufzfUFaca/YHkIVIC19ohmE5X738TtxGbOgiGef4bvd9sU6M42k8vMlCPJp1woDFDOFoBQpr4YzH4ZTR6Ps+HP8VEIJMG5uiLQOLxdKdxi41QIDAQAB
-----END PUBLIC KEY-----
"""
prikey = """-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIr+q/k/yLFaOgUjavMXra2bqwMWdGwqwaFVIK7FAjG56ExZAua/pM/HJNA958gq5/N9QVpxr9geQhUgLX2iGYTlfvfxO3EZs6CIZ5/hu932xTozjaTy8yUI8mnXCgMUM4WgFCmvhjMfhlNHo+z4c/xUQgkwbm6ItA4vF0p3GLjVAgMBAAECgYBDsqawT5DAUOHRft6oZ+//jsJMTrOFu41ztrKkbPAUqCesh+4R1WXAjY4wnvY1WDCBN5CNLLIo4RPuli2R81HZ4OpZuiHv81sNMccauhrJrioDdbxhxbM7/jQ6M9YajwdNisL5zClXCOs1/y01+9vDiMDk0kX8hiIYlpPKDwjqQQJBAL6Y0fuoJng57GGhdwvN2c656tLDPj9GRi0sfeeMqavRTMz6/qea1LdAuzDhRoS2Wb8ArhOkYns0GMazzc1q428CQQC6sM9OiVR4EV/ewGnBnF+0p3alcYr//Gp1wZ6fKIrFJQpbHTzf27AhKgOJ1qB6A7P/mQS6JvYDPsgrVkPLRnX7AkEAr/xpfyXfB4nsUqWFR3f2UiRmx98RfdlEePeo9YFzNTvX3zkuo9GZ8e8qKNMJiwbYzT0yft59NGeBLQ/eynqUrwJAE6Nxy0Mq/Y5mVVpMRa+babeMBY9SHeeBk22QsBFlt6NT2Y3Tz4CeoH547NEFBJDLKIICO0rJ6kF6cQScERASbQJAZy088sVY6DJtGRLPuysv3NiyfEvikmczCEkDPex4shvFLddwNUlmhzml5pscIie44mBOJ0uX37y+co3q6UoRQg==
-----END PRIVATE KEY-----
"""
pubkey = RSA.import_key(pubkey)
prikey = RSA.import_key(prikey)
n = pubkey.n
def enc_replace(base64_str: str):
base64_str = base64_str.replace("/", "e5Lg^FM5EQYe5!yF&62%V$UG*B*RfQeM")
base64_str = base64_str.replace("+", "n6&B8G6nE@2tt4UR6h3QBt*5&C&pVu8W")
return base64_str.replace("=", "JXWUDuLUgwRLKD9fD6&VY2aFeE&r@Ff2")
def encrypt(plain_text):
cipher_text = b""
for i in range(0, len(plain_text), 128):
part = plain_text[i:i+128]
enc = libnum.n2s(pow(libnum.s2n(part), prikey.d, n))
cipher_text += enc
return enc_replace(base64.b64encode(cipher_text).decode())
def dec_replace(base64_str):
base64_str=base64_str.replace(b"e5Lg^FM5EQYe5!yF&62%V$UG*B*RfQeM",b"/")
base64_str = base64_str.replace(b"n6&B8G6nE@2tt4UR6h3QBt*5&C&pVu8W",b"+" )
return base64_str.replace(b"JXWUDuLUgwRLKD9fD6&VY2aFeE&r@Ff2",b"=")
def decrypt(cipher_text):
plain_text=b""
#cipher_text=dec_replace(cipher_text)
cipher_text=bytes(base64.b64decode(cipher_text))
for i in range(0,len(cipher_text),128):
part=cipher_text[i:i+128]
dec=libnum.n2s(pow(libnum.s2n(part),pubkey.e,n))
plain_text+=dec
return plain_text
if __name__ == '__main__':
c=b""
m=decrypt(c)
print(long_to_bytes(m))
解得1
2
3
4
5
6
7
8
9
10
11
12
13b'whoami'
b'ipconfig'
b'ifconfig'
b'ls /'
b'ls /root'
b'ls /root/FLAG'
b'cd /root/FLAG'
b'cat 小明的日记.txt'
b'cd /root/FLAG && base64 小明的日记.txt'
b'cd /root/FLAG && base64 hint.py'
b'echo U2FsdGVkX1+SslS2BbHfe3c4/t/KxLaM6ZFlOdbtfMHnG8lepnhMnde40tNOYjSvoErLzy0csL7c5d4TlMntBQ== > /root/FLAG/flag.txt'
b'ls /root/FLAG'
b'echo Good Luck! ByeBye~'
发现一串: U2FsdGVkX1+SslS2BbHfe3c4/t/KxLaM6ZFlOdbtfMHnG8lepnhMnde40tNOYjSvoErLzy0csL7c5d4TlMntBQ
密钥为Password-based-encryption
AES解密得flag1
flag:NKCTF{d0e1183c-07c3-49ea-b048-addbe6cc1b20}
3.Minecraft:SEED
题目过程:打mod:
seedcrackerX
然后就是跑图
…1
flag:NKCTF{07efe5eb-318e-486a-8c1c-6fc9304c6b16}
4.world.execute.me
构造payload:1
echo $HEART;echo ;
即可1
NKCTF2024{Then_1_c4n_b3_y0ur_only_EXECUTION}
5.ctf80
想办法使五种语言都可运行,这个题我们队最后一个小时开始冲,只过了两关,没时间了。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
echo -e "这天在NKCTF2024命题组的群里,发起了一场有趣的讨论:什么是世界上最好的语言?\n"
echo -e "Cain从高中开始学习算法竞赛,喜欢C++,它优雅而高效\n"
echo -e "crazyman觉得古老的C语言才是万物之本\n"
echo -e "探姬觉得,哦不,是认同PHP是世界上最好的语言\n"
echo -e "天天直播HTB的巨魔,当然力荐构建了MSF的ruby\n"
echo -e "喜欢密码学的rec自然离不开python\n"
echo -e "“那我们来出一道题目让选手提交文件!让大家决定到底什么是最好的语言!”\n"
echo -e "可是flag的发放要经过大家全票同意,从他们“永不服输”的脸上,可以预料到,只要你不能让大家都满意,那就没办法得到flag\n"
echo "请输入 Base64 编码后的源文件:"
read -p "" input_string
decoded_code = $(echo "$input_string" | base64 -d)
echo "$decoded_code" > srccode
#Initiating SandBox
#--------------------------------
#---------不给你看!-----------
#--------------------------------
#Run in SandBox
#--------------------------------
#---------不给你看!-----------
#--------------------------------
if [ "$output" != "Cain" ]; then
echo "Cain生气了!他拒绝给你flag!"
exit 1
fi
#Run in SandBox
#--------------------------------
#---------不给你看!-----------
#--------------------------------
if [ "$output" != "crazyman" ]; then
echo "crazyman似乎不是很高兴,他否决了你的flag申请。"
exit 1
fi
#Run in SandBox
#--------------------------------
#---------不给你看!-----------
#--------------------------------
if [ "$output" != "#/*tanji" ]; then
echo "探姬失望的摇了摇头,回房间脱掉了刚刚穿好的女装。"
exit 1
fi
#Run in SandBox
#--------------------------------
#---------不给你看!-----------
#--------------------------------
if [ "$output" != "Randark_JMT" ]; then
echo "巨魔没有关掉他的直播,他还在学习!"
exit 1
fi
#Run in SandBox
#--------------------------------
#---------不给你看!-----------
#--------------------------------
if [ "$output" != "rec" ]; then
echo "rec似乎是不小心的按下了否决按钮,“呀!真不好意思”"
exit 1
fi
flag_value=$FLAG
echo "真是一个职场圣体啊!不去考公务员可惜了(bushi"
echo "FLAG在这里: $flag_value1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 #/*<?php echo "tanji"; __halt_compiler();?> */
#include <stdio.h> /*
print (0 and "Randark_JMT" or "rec"));
__DATA__ = 1
"""""
__END__
===== . ===== */
#elif __cplusplus
char msg[5] = {'C','a','i','n','\n'};
#else
char msg[9] = {'c','r','a','z','y','m','a','n','\n'};
#endif
int main() { int i; for(i = 0; i < 9; ++i) putchar(msg[i]); return 0;}
/*
"""
#*/
wp 的 payload 构造的非常巧妙
第一关:通过其中的 C++ 代码输出 “Cain”
第二关:使用C语言输出 “crazyman”
第三关:(当时想了好久都没想出来)可以利用不同语言的注释符的不同,嵌入PHP语言
第四、五关:思路同上
(先就这样吧)