贷乐高注入复现
Jun 11, 2018 09:54 · 489 words · 3 minute read
这篇文章主要介绍了一种绕过waf的思路技巧。在于拓展思维。由于没有找到源码,所以就记录一下这个思路技巧。
部分代码
/core/sqlin.inc.php,包含在config.inc.php中,所有请求都会经由此类过滤:
class sqlin {
function dowith_sql($str) {
$check= eregi('select|insert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile', $str);
if($check)
{
echo "非法字符!";
exit();
}
$newstr="";
while($newstr!=$str){
$newstr=$str;
$str = str_replace("script", "", $str);
$str = str_replace("execute", "", $str);
$str = str_replace("update", "", $str);
//$str = str_replace("count", "", $str);
//注释掉对count的过滤,不然account这样的参数会被截断
$str = str_replace("master", "", $str);
$str = str_replace("truncate", "", $str);
$str = str_replace("declare", "", $str);
$str = str_replace("select", "", $str);
$str = str_replace("create", "", $str);
$str = str_replace("delete", "", $str);
$str = str_replace("insert", "", $str);
$str = str_replace("\'", "", $str);
}
return $str;
}
//aticle()防SQL注入函数//php教程
function sqlin() {
foreach ($_GET as $key => $value) {
//echo $key."|".(strpos($key, "password") == false);
if ($key != "content"&&strstr($key, "password") == false) {
$_GET[$key] = $this->dowith_sql($value);
}
}
foreach ($_POST as $key => $value) {
//echo $key."|".(strpos($key, "password") == false);
[email protected]_put_contents('wxy123123.txt', date('Ymd His') . '提交url拼接 '.$key."|".(strstr($key, "password") == false), FILE_APPEND);
if ($key != "content"&&strstr($key, "password") == false) {
$_POST[$key] = $this->dowith_sql($value);
}
}
foreach ($_REQUEST as $key => $value) {
//echo $key."|".(strpos($key, "password") == false);
if ($key != "content"&&strstr($key, "password") == false) {
$_REQUEST[$key] = $this->dowith_sql($value);
}
}
}
}
/core/safe.inc.php部分代码
/* 检查和转义字符 */
function safe_str($str){
if(!get_magic_quotes_gpc()) {
if( is_array($str) ) {
foreach($str as $key => $value) {
$str[$key] = safe_str($value);
}
}else{
$str = addslashes($str);
}
}
return $str;
}
function dhtmlspecialchars($string) {
if(is_array($string)) {
foreach($string as $key => $val) {
$string[$key] = dhtmlspecialchars($val);
}
} else {
$string = str_replace(array('&', '"', '<', '>','(',')'), array('&', '"', '<', '>','(',')'), $string);
if(strpos($string, '&#') !== false) {
$string = preg_replace('/&((#(\d{3,5}|x[a-fA-F0-9]{4}));)/', '&\\1', $string);
}
}
return $string;
}
foreach ($_GET as $key => $value) {
$_GET[$key] = safe_str($value);
$_GET[$key] = dhtmlspecialchars($value);
}
foreach ($_POST as $key => $value) {
$_POST[$key] = safe_str($value);
$_GET[$key] = dhtmlspecialchars($value);
}
foreach ($_REQUEST as $key => $value) {
$_REQUEST[$key] = safe_str($value);
$_REQUEST[$key] = dhtmlspecialchars($value);
}
foreach ($_COOKIE as $key => $value) {
$_COOKIE[$key] = safe_str($value);
$_GET[$key] = dhtmlspecialchars($value);
}
两段代码的作用
第一段代码对\$_GET,\$POST,\$_REQUEST都进行了过滤,过滤正则是select|insert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile
这个正则只是一部分,只要被这个正则匹配到,程序就会exit; 继续往下,还有针对关键字的替换。(delete,update等等)
第二段代码是对\$_GET,\$POST,\$_REQUEST,\$_COOKIE中的一些特殊字符做转义和替换。还会将英文的()
替换为中文的()
,很鸡儿骚。
梳理逻辑
贷乐高cms对于参数的处理的流程大致过程如下:
index.php->config.inc.php->sqlin.php->safe.inc.php
sqlin.php是对关键字的拦截,safe.inc.php是替换一些敏感字符。
在safe.inc.php中还有这样一段代码
$request_uri = explode("?", $_SERVER['REQUEST_URI']);
if (isset($request_uri[1])) {
$rewrite_url = explode("&", $request_uri[1]);
foreach ($rewrite_url as $key => $value) {
$_value = explode("=", $value);
if (isset($_value[1])) {
$_REQUEST[$_value[0]] = dhtmlspecialchars(addslashes($_value[1]));
//$_REQUEST[$_value[0]] = addslashes($_value[1]);
//$_REQUEST[$_value[0]] = dhtmlspecialchars($_value[1]);
}
}
}
这段代码是将\$_SERVER[‘REQUEST_URI’]中的内容做了一次过滤,然后赋值给\$_REQUEST[]数组。
这段代码是绕过waf的关键所在。这段代码会重新对\$_REQUEST[]数组赋值,\$_REQUEST[]数组是包含了\$_GET和\$_POST数组的。
绕过
绕过思路
假设第一道waf在检测参数时没有检测到参数,然后在第二段waf处利用处理\$_SERVER[‘REQUEST_URI’]的代码对\$_REQUEST[]数组的值进行了覆盖。那么就可以绕过waf了。
那么怎么才能在第一道waf不检测到恶意字符呢? 这个问题很严肃,因为waf对数组的每个值进行检测,只要有一个出现问题,程序就会终止。
php的两个个特性
两个参数取后一个
实际测试一下,就知道这个特性了。
<?
$a = $_REQUEST['yy'];
echo $a;
?>
测试
http://127.0.0.1/45.php?yy=1111
输出 1111
http://127.0.0.1/45.php?yy=1111&yy=2222
输出2222
当url里的参数重复时,php会采用后一个参数。这是HTTP Parameter Pollution在php中的表现。
有了这个特性,那么是不是就尝试让waf检测的是第二个参数,而到覆盖REQUEST的时候使用第一个参数。
如果直接使用相同的参数是不可行的,两道waf取的参数都是第二个参数。
解析特性,转换.为_
这里可以利用php的另一个特性:php自身在解析请求时,如果参数中包含. [
这几个字符,会将他们转换为下划线。
<?php
$url = explode("?",$_SERVER['REQUEST_URI']);
echo "<pre>";
print_r($url);
echo "</pre>";
echo "<pre>";
print_r($_REQUEST);
echo "</pre>";
输出
url: http://127.0.0.1/45.php?yy=1111&yy.id=2222
Array
(
[0] => /45.php
[1] => yy=1111&yy.id=2222
)
Array
(
[yy] => 1111
[yy_id] => 2222
)
可以看到输出的为yy_id,而url中为yy.id。
### 结合 构造一个两个特性结合在一起的url
http://127.0.0.1/45.php?yy_id=1111&yy.id=2222
这样的url在第一道waf时检测时,\$_REQUEST获取到的是2222,在第二段waf处理\$_SERVER[‘REQUEST_URI’]时,因为yy_id和yy.id是两个不同的字符串,所以最后的\$_REQUEST[yy_id]就为1111.
测试
<?php
echo "第一段waf:";
echo $_REQUEST["yy_id"];
echo "<br>";
$request_uri = explode("?", $_SERVER['REQUEST_URI']);
if (isset($request_uri[1])) {
$rewrite_url = explode("&", $request_uri[1]);
foreach ($rewrite_url as $key => $value) {
$_value = explode("=", $value);
if (isset($_value[1])) {
$_REQUEST[$_value[0]] = addslashes($_value[1]);
}
}
}
echo "第二段waf:";
echo $_REQUEST["yy_id"];
?>
输出结果
http://127.0.0.1/45.php?yy_id=1111&yy.id=2222
第一段waf:2222
第二段waf:1111
这样就绕过了waf的检测。
利用
从上面的内容来看,这个漏洞的利用需要满足以下几个条件 - 有注入点 - 注入点由$REQUEST中获取。 - 变量的名字包含下划线 - 不使用特殊字符(第二段waf会对特殊字符转义,可以union select注入完全不需要括号什么的)
这几个条件在这个贷齐乐cms里很好找,因为该cms过分相信于waf,内部存在太多注入。