0x01 代码审计中的信息收集

一个cms代码量确实不少,通读代码耗时长,效果也不一定好.而一个功能点如果之前出过漏洞,特别是多次出现漏洞的地方,证明开发者对这个漏洞的理解不充分,很容易再次绕过补丁.这样,一整个CMS的代码审计就可以降维到一道ctf题目.特别是对于经常参加ctf的各位大佬来说,这样的代码审计更加简单休闲.我记得之前也有机构统计过,出过漏洞的地方更容易再次出现漏洞,普通CMS的开发者通常不是专业的安全人员,也不一定有专业的安全专家协助修复,再次出现漏洞的可能性就更大了.

我以github上的一个百星icms为例.

icms github链接: https://github.com/idreamsoft/iCMS \

在issue中搜索SSRF https://github.com/idreamsoft/iCMS/issues?utf8=%E2%9C%93&q=is%3Aissue+ssrf

在cve列表中查找,应该对应的就是这三个cve了

可以看到这个功能点已经出现了三次的绕过与过滤.

大致了解下这个功能点,是一个自动更新文章的爬虫,多处都可以控制url参数.

点开issue查看具体信息,我们从最早出现漏洞的版本看起.

通过查看具体的commits,可以找到开发者修复漏洞的思路.这给我们代码审计带来很大的便利.

CVE-2018-14514 漏洞分析

commit: https://github.com/idreamsoft/iCMS/issues/29

提交者详细描述了漏洞信息,只指出了一个点,但根据作者修复的commit,有两处都存在SSRF漏洞.

SSRF:

public static function postUrl($url, $data) {
is_array($data) && $data = http_build_query($data);
$options = array(
CURLOPT_URL => $url,
...
); $ch = curl_init();
curl_setopt_array($ch,$options);
$responses = curl_exec($ch);
curl_close ($ch);
return $responses;
}

icms7.0.9\app\spider\spider_tools.class.php 604行,关键代码:

public static function remote($url, $_count = 0) {
$url = str_replace('&', '&', $url);
if(empty(spider::$referer)){
$uri = parse_url($url);
spider::$referer = $uri['scheme'] . '://' . $uri['host'];
}
self::$curl_info = array();
$options = array(
CURLOPT_URL => $url,
...
);
spider::$cookie && $options[CURLOPT_COOKIE] = spider::$cookie;
if(spider::$curl_proxy){
$proxy = self::proxy_test();
$proxy && $options = iHttp::proxy($options,$proxy);
}
if(spider::$PROXY_URL){
$options[CURLOPT_URL] = spider::$PROXY_URL.urlencode($url);
}
$ch = curl_init();
curl_setopt_array($ch,$options);
$responses = curl_exec($ch);
...
}

两处都是因为使用了curl,且无安全措施,只需要url参数可控即可进行SSRF攻击.

可以看到icms7.0.9版本没有做任何的验证,并且可以使用任意协议访问任意ip与端口.因此如果有redis或无密码的mysql或者一些其他容易被攻击的服务,可以getshell.因为这里重点不是通过SSRF如何getshell ,因此不做getshell的验证.

我们找一处漏洞点测试,有很多处都调用了remote函数,全局搜索即可,我们找一个能即时回显的点测试.

payload:http://ip/admincp.php?app=spider&do=testdata&url=dict://127.0.0.1:8000&rid=2&pid=0&title=m09ic

监听端口观察是否有数据过来.

很明显收到了.

CVE-2018-14514 补丁分析

我们再来看看作者是如何修复的,commit: https://github.com/idreamsoft/iCMS/commit/64bb0bdf77febbd6ac0ccb6658ee1ddc71530bb1

public static function remote($url, $_count = 0) {
if(!iHttp::is_url($url,true)){
if (spider::$dataTest || spider::$ruleTest) {
echo "<b>{$url} 请求错误:非正常URL格式,因安全问题只允许抓取 http:// 或 https:// 开头的链接</b>";
}
return false;
}

作者添加了一个判断函数

public static function is_url($url,$strict=false) {
$url = trim($url);
if($strict){
return (stripos($url, 'http://') === 0 || stripos($url, 'https://') === 0);
} if (stripos($url, 'http://') === false && stripos($url, 'https://') === false) {
return false;
} else {
return true;
}
}

先判断url是否以http://开头,才开始解析,这样就限制了危险的协议,减轻了危害程度,大部分情况很难getshell.但是SSRF漏洞依然存在.

可以发现,作者对SSRF漏洞的认识并不到位,认为不能getshell就可以了.但是实际上,用HTTP协议也并非完全不可能getshell,内网有可能存在一些可以被GET请求getshell的服务,比如thinkphp的几个RCE,就算不能RCE,SSRF也可以直接被用来进行内网信息收集,同样是不可忽视的漏洞.

CVE-2018-14858 补丁分析

显然只允许http与https开头的url访问,SSRF依然存在,于是在icms7.0.11版本,又有人提交了SSRF漏洞,并获得了一个CVE编号.漏洞成因与上一个漏洞一致,作者的过滤措施虽然缓解了该漏洞的危害,但是漏洞依然存在.下面是作者在issue中的回复:

提交者除了提交漏洞,还简单说明了几种常见的ssrf绕过手法,比如不同格式的ip地址.

这是作者在issue33下的回复.

I know this question, but if the IP format is banned, the website using the IP format will not be collected. Although it is not used a lot, it will still be encountered. There is no better way to think about it now.

然后过了几天,作者意识到了这样并不算修复了SSRF漏洞,再次commit了一个补丁.

具体更新内容: https://github.com/idreamsoft/iCMS/commit/62de04e57a67f2690dbf88b7d381af61a0969ef3

添加了过滤代码,关键代码如下:

public static function remote($url, $_count = 0) {
if(!iHttp::is_url($url,true)){
$parsed = parse_url($url);//解析url
$validate_ip = true;
preg_match('/\d+/', $parsed['host']) && $parsed['host'] = long2ip($parsed['host']);//获取host部分,如果是十进制或其他进制的ip地址,转化成标准的ip地址
if(preg_match('/\d+\.\d+\.\d+\.\d+/', $parsed['host'])){
$validate_ip = filter_var($parsed['host'], FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);//匹配正确的ip格式,过滤非法ip地址字符与内外地址
}
if(!in_array($parsed['scheme'],array('http','https')) || !$validate_ip|| strtolower($parsed['host'])=='localhost'){
if (spider::$dataTest || spider::$ruleTest) {
echo "<b>{$url} 请求错误:非正常URL格式,因安全问题只允许抓取 http:// 或 https:// 开头的链接</b>";
echo "<b>{$url} 请求错误:非正常URL格式,因安全问题只允许抓取 http:// 或 https:// 开头的链接或私有IP地址</b>";
}
return false;
}
}
... }

可以看到,这次添加了检查ip地址的格式,以及是否是内网ip.

以普通开发者的角度思考,很多情况都是哪里出了问题就修哪里,什么东西能绕过就过滤什么.也很难要求他们完全了解安全漏洞,因此也导致了修复再次被绕过.

CVE-2018-15895补丁分析

与上个漏洞提交者是同一个人. https://github.com/idreamsoft/iCMS/issues/40

然而,普通开发人员通常是哪里有问题就去解决哪里的问题,并不一定能对某个漏洞有深入的认识,更不用说了解全部攻击与绕过手段,但是只要漏了一种,修复补丁就等于完全没有.

我们都知道,SSRF的常用绕过手法还有302重定向与DNS重绑定.漏洞提交者也演示了这两种方式.具体POC可以看issue内容.

关键代码如下:

public static function safe_url($url) {
$parsed = parse_url($url);
$validate_ip = true; if($parsed['port'] && is_array(self::$safe_port) && !in_array($parsed['port'],self::$safe_port)){
if (spider::$dataTest || spider::$ruleTest) {
echo "<b>请求错误:非正常端口,因安全问题只允许抓取80,443端口的链接,如有特殊需求请自行修改程序</b>".PHP_EOL;
}
return false;
}else{
preg_match('/^\d+$/', $parsed['host']) && $parsed['host'] = long2ip($parsed['host']);
$long = ip2long($parsed['host']);
if($long===false){
$ip = null;
if(self::$safe_url){
@putenv('RES_OPTIONS=retrans:1 retry:1 timeout:1 attempts:1');
$ip = gethostbyname($parsed['host']);
$long = ip2long($ip);
$long===false && $ip = null;
@putenv('RES_OPTIONS');
}
}else{
$ip = $parsed['host'];
}
$ip && $validate_ip = filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
} if(!in_array($parsed['scheme'],array('http','https')) || !$validate_ip){
if (spider::$dataTest || spider::$ruleTest) {
echo "<b>{$url} 请求错误:非正常URL格式,因安全问题只允许抓取 http:// 或 https:// 开头的链接或公有IP地址</b>".PHP_EOL;
}
return false;
}else{
return $url;
}
}

可以看到,使用了第17行使用了gethostbyname 确定parse_url解析后的host部分,来防护DNS rebinding 攻击.

并且在curl的options中,注释了// CURLOPT_FOLLOWLOCATION => 1,// 使用自动跳转,来防护302重定向绕过.

经常打ctf的小伙伴可能就会注意到了,攻击的思路可以针对parse_url的解析问题.历代parse_url存在不少方式缺陷,比如scheme,host,port,path等均有过绕过的记录.而这些细节,是开发者很难注意到的.如果想要再次绕过,这里就是个很好的突破点.

新的绕过

光黑名单和检查host真实ip来说,基本上是万无一失.但是作者万万没想到,来自php自身的背后一刀.在2017年blackhat上orange师傅演讲的A New Era of SSRF中,有一个新的攻击方式,利用php中的parse_url函数和libcurl对url的解析差异,导致了对host的过滤失效,成功绕过.

从orange师傅的ppt中偷一张图来解释.

php-curl拓展解析url的host在第二个@之后,而parse_url则是最后一个@之后.

因此我们可以使用如下payload绕过:

http://ip/admincp.php?app=spider_project&do=test&url=http://m09ic@127.0.0.1:81@baidu.com/&rid=2&pid=1&title=

可以看到,同时绕过了port和host的限制,访问到了只对本地开放的81端口的phpinfo内容.成功绕过过滤实现SSRF.

这里有一个小坑,在较新版本的php-curl中,已经修复了多个@的解析问题,使用多个@会报错,不知道为啥不是调整到与parse_url一致,这种修复显然影响了可用性.

该漏洞也不单是cms的问题,也有curl的问题.不管所使用的所有开源组件是不是安全的,在常见漏洞上cms中再加一层过滤是必要的.

大多数linux发行版并没有使用最新版本的curl.可以在 https://curl.haxx.se/download.html 这里查询linux发行版与curl版本的对应关系,应该少有公司会实时更新操作系统版本,只要不是最新版本的操作系统,基本都存在该漏洞.

我只测试了ubuntu,在ubuntu16.04及以下均可以使用该方式绕过.而在ubuntu18.04中,已经不再可以.exec_curl函数执行会直接返回false.

ubuntu16.04的curl版本是:

# curl -V
curl 7.47.0 (x86_64-pc-linux-gnu) libcurl/7.47.0 GnuTLS/3.4.10 zlib/1.2.8 libidn/1.32 librtmp/2.3
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IDN IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz TLS-SRP UnixSockets

已经提交了issue,坐等作者的修复,期待是否还有被绕过的可能:D

最后

(面向github代码审计)

一个开发人员很难有精力去了解一个攻击方式的方方面面,也很难让开发者紧跟攻击手法的趋势.在刚才的例子看到,虽然开发者积极的解决漏洞,但是并不能有效缓解漏洞,总有普通开发者不知道的方式再次绕过.

总得来说,初尝代码审计时,可以多翻翻issue中多次出现漏洞的点,这种地方再次出现漏洞的几率相对来说较高.

另外,这个漏洞在利用要进入后台,又过滤了各种敏感协议,实际上危害并不大,仅仅用来学习代码审计的思路以及常见SSRF的绕过与防护方式.

代码审计变成CTF的更多相关文章

  1. CTF 文件包含与伪协议

    正巧在写代码审计的文章,无意间看到了一篇CTF的代码审计,CTF题目很好,用的姿势正如标题,文件包含和伪协议. 先放出原文链接(http://www.freebuf.com/column/150028 ...

  2. [代码审计]PHP_Bugs题目总结(1)

    0x00 简介 最近这几天看到了许多关于代码审计的ctf题,在电脑里也翻出来好长时间没看过的php_bugs,干脆最近把这个好好看看! 下载地址:https://github.com/bowu678/ ...

  3. PHP代码安全杂谈

    虽然PHP是世界上最好的语言,但是也有一些因为弱类型语言的安全性问题出现.WordPress历史上就出现过由于PHP本身的缺陷而造成的一些安全性问题,如CVE-2014-0166 中的cookie伪造 ...

  4. CTF-WeChall-第一天

    2020.09.09 今天来了一个新平台,WeChall,从简单的开始做,才能找到自信--i春秋的题做自闭了

  5. web网络漏洞扫描器编写

    这两天看了很多web漏洞扫描器编写的文章,比如W12scan以及其前身W8scan,还有猪猪侠的自动化攻击背景下的过去.现在与未来,以及网上很多优秀的扫描器和博客,除了之前写了一部分的静湖ABC段扫描 ...

  6. 某CTF代码审计题

    记一次参加CTF比赛翻车记!   开始还是挺有信心的,毕竟也是经常打一些CTF锻炼,然而比赛发现大佬们平时不显山不漏水的一比赛全出来了!赛后看了一下各题的writeup发现自己的确技不如人啊!借鉴一个 ...

  7. 安鸾CTF Writeup PHP代码审计01

    PHP代码审计 01 题目URL:http://www.whalwl.xyz:8017 提示:源代码有泄露 既然提示有源代码泄露,我们就先扫描一遍. 精选CTF专用字典: https://github ...

  8. 某CTF平台一道PHP代码审计

    这道题不是说太难,但是思路一定要灵活,灵活的利用源码中给的东西.先看一下源码. 首先要理解大意. 这段源码的大致的意思就是,先将flag的值读取放在$flag里面. 后面再接受你输入的值进行判断(黑名 ...

  9. 实验吧(你真的会PHP吗)CTF之代码审计最终版---解析是错的 我的才是对的

    0X01代码的详细讲解 0X02代码分析完了 我们来理一下 思路 条件 A POST提交一个number参数的值不能为纯数字 C number取整数后是回文数 D number经过strval转译后的 ...

随机推荐

  1. PHP转换oracle数据库的date类型

    今天圣诞节啊,圣诞节快乐啊! 最近遇到一个很纠结的事,就是我在plsql里面查的是这样的,很正常, 但是我用程序查出来就是这样的,啊啊啊,真是崩溃啊 但是我传数据需要上面那种格式,而且我对oracle ...

  2. 关于 checkbox 的一些操作

    获取checkbox选中的状态 $("#checkbox").is(":checked"); 设置 checkbox 的状态 $("#checkbox ...

  3. 探讨 Git 代码托管平台的若干问题

    关于 Git 版本控制软件种类繁多,维基百科收录的最早的版本控制系统是 1972 年贝尔实验室开发的 Source Code Control System.1986 年 Concurrent Vers ...

  4. java实现文字转语音功能(仅Windows)

    一.pom.xml引入jar包依赖 <!-- https://mvnrepository.com/artifact/com.jacob/jacob 文字转语音 --> <depend ...

  5. 802.11r mixed mode

    * 802.11r mixed mode support – Untill this code(8.0), if you enable 802.11r fast secure roaming, onl ...

  6. Jedis实现频道的订阅,取消订阅

     第一步:创建一个发布者 package work; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; i ...

  7. The Captain 题解

    20200216题目题解 这是一篇题解祭题解记,但一共就一道题目.(ROS菜大了) 题目如下: The Captain 给定平面上的n个点,定义(x1,y1)到(x2,y2)的费用为min(|x1-x ...

  8. OS(操作系统)结构

    1.整体式: 模块设计(独立的) 调用自由 用全局变量来通信 缺点:信息不安全,维护更新比较难 2.层次结构(典型的如TCP/IP协议): 所有的模块排成若干层,相邻的互相依赖调用 按调用次序来安排 ...

  9. 「JSOI2015」最大公约数

    「JSOI2015」最大公约数 传送门 考虑先枚举区间左端点, 然后我们会发现所有可能的区间虽然有 \(O(n)\) 个,但是本质不同的区间 \(\gcd\) 只有 \(\log n\) 级别,而且是 ...

  10. 2.1 MySQL基础使用

    本文是课上资料的总结非原创没有转载地址 目录 引言 为什么需要数据库? 数据库和应用程序的关系 MySQL基础使用 一.数据库简介 1.1 简介 1.2 常见数据库管理系统 1.3 MySQL卸载 1 ...