贷齐乐hpp+php特性注入

avatar
作者
筋斗云
阅读量:0

目录

一、武装到牙齿的贷齐乐

1.1 第一层WAF

1.2 第二层WAF

1.3 执行逻辑顺序

二、绕过思路

2.1 业务处理板块

2.2 双层WAF组合技

2.3 思路

三、php特性

四、测试

index.php:

waf2分析

五、总结


一、武装到牙齿的贷齐乐

贷齐乐这个系统,说起来也是安全问题比较严重的P2P金融类的CMS。由于连续出了多次安全漏洞,所以官方给贷齐乐系统中添加了严重影响正常业务进行的变态WAF,而且还是两层!

1.1 第一层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;     } 	 

GET/POST/REQUEST三个变量,都会经过这个正则:select|insert|update|delete|'|/*|*|../|./|union|into|load_file|outfile,这简直就是完全不顾正常业务运行了,查询语句直接Ban掉,一旦遇到select,包括单引号,包括注释符,就立即exit整个流程。

//aticle()防SQL注入函数//php教程     function sqlin() {         foreach ($_GET as $key => $value) {             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);             }         }     } }

将GET获取到的所有值来进行判断是否含还有password,如果没有,那么就执行第一层WAF来进行检测,GET传参不行,单引号也是闭合不了的。第二段则是将POST也进行了循环,然后进行过滤,执行第一层WAF,第三段则是REQUEST同样是进行循环然后过滤掉了。

1.2 第二层WAF

/* 检查和转义字符 */ 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('&amp;', '&quot;', '&lt;', '&gt;','(',')'), $string);         if(strpos($string, '&amp;#') !== false) {             $string = preg_replace('/&amp;((#(\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_magic_quotes_gpc,如果是数组,则通过safe_str循环过滤,不是数组则使用和addslashes函数过滤。

接下来我们再看定义的这个函数:

header("Content-type: text/html; charset=utf-8"); require 'db.inc.php';   function dhtmlspecialchars($string) {       if (is_array($string)) {           foreach ($string as $key => $val) {               $string[$key] = dhtmlspecialchars($val);           }       }       else {           $string = str_replace(array('&', '"', '<', '>', '(', ')'), array('&amp;', '&quot;', '&lt;', '&gt;', '(', ')'), $string);           if (strpos($string, '&amp;#') !== false) {               $string = preg_replace('/&amp;((#(\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/COOKIE都会经过这个替换str_replace(array('&', '"', '<', '>','(',')'), array('&', '"', '<', '>','(',')'), $string),这个替换最明显的效果,就是所有的英文括号都变成中文括号,导致user(),database()等无法执行,这里我们再注入已经几乎没办法进行注入了。

1.3 执行逻辑顺序

index.php -> config.inc.php -> sqlin.php -> safe.inc.php
  • sqlin.php是对select、union、’等关键字的拦截。一旦发现存在这些关键字,就exit出整个执行流程。
  • safe.inc.php是替换一些敏感字符,比如<、>、(、)等。
  • 两层WAF各司其职,第一个waf拦截关键字单双引号等,第二个waf转义括号。

二、绕过思路

2.1 业务处理板块

  // 业务处理   //?i_d&i.d=aaaaaaa   if (isset($_REQUEST['submit'])) {       $user_id = $_REQUEST['i_d'];       $sql = "select * from ctf.users where id=$user_id";       $result=mysql_query($sql);       while($row = mysql_fetch_array($result))       {           echo "<tr>";           echo "<td>" . $row['name'] . "</td>";           echo "</tr>";       }   } ?>

在业务处理板块下,它将id放在user_id,第三行这里不存在单引号也就不用闭合,但是是可以写入语句注入

2.2 双层WAF组合技

  // 经过第一个waf处理   //i_d=1&i.d=aaaaa&submit=1   foreach ($_REQUEST as $key => $value) {       $_REQUEST[$key] = dowith_sql($value);   }   // 经过第二个WAF处理   $request_uri = explode("?", $_SERVER['REQUEST_URI']);   //i_d=1&i.d=aaaaa&submit=1   if (isset($request_uri[1])) {       $rewrite_url = explode("&", $request_uri[1]);       //print_r($rewrite_url);exit;       foreach ($rewrite_url as $key => $value) {           $_value = explode("=", $value);           if (isset($_value[1])) {               //$_REQUEST[I_d]=-1 union select flag users               $_REQUEST[$_value[0]] = dhtmlspecialchars(addslashes($_value[1]));           }       }   }

分析代码我们可以知道,第一个waf接收参数是使用$_REQUEST接收的,第二个waf使用$_SERVER接收。

2.3 思路

分析index.php中业务处理模块,我们可以找到它就是注入点,而这个注入点是通过$_request接收的,而不是$_service接收的,也就意味着我们需要让第一次request提交的值没有恶意字符,而第二次提交的service有恶意字符(可以绕过dhtmlspecialchars函数)

     $_REQUEST[$_value[0]] = dhtmlspecialchars(addslashes($_value[1]));

最终提交的恶意字符会重新赋值给$_request,然后我们在$_request中接收,就可以将赋值语句接过来,$_REQUEST不可以有恶意字符,防止被第一层WAF过滤掉,$_SERVER可以有恶意字符,但必须要过dhtmlspecialchars这个函数,过完之后重新赋值给request,然后request在业务处理模块中直接接受。

三、php特性

有什么办法让第一层WAF认为请求中没有恶意字符呢?

在第一次WAF检测参数的时候,检测的是第二个参数,但后面覆盖request的时候,拿到的是第一个参数,相当于第一次检测的时候是正常的,第二次检测时候将恶意代码覆盖上去,那么不就可以造成WAF的绕过了。

php传入两个参数时,会执行第二个参数

php另一个特性,自身在解析请求的时候,如果参数名字中包含” “、”.”、”[“这几个字符,会将他们转换成下划线

如下:

php自动将点转换为了下划线

假设我发送的是这样一个请求: /t.php?user_id=11111&user.id=22222 ,php先将user.id转换成user_id,即为/t.php?user_id=11111&user_id=22222 ,再获取到的$_REQUEST['user_id']就是22222,可在$_SERVER['REQUEST_URI']中,user_id和user.id却是两个完全不同的参数名,那么切割覆盖后,获取的$_REQUEST['user_id']却是11111。

四、测试

数据库的信息

再来看这串代码,注入点在第三行,我们在第二行接收到i_d,然后放在第三行中。

写入i_d=aaa&i.d=aaa,i.d里面是正常的字符,点会变成下划线(i_d),第二行REQUEST接收到的是第二个参数i.d(无恶意字符的参数),会经过第一个waf过滤,由于无恶意字符,代码执行到第二个waf,在第二个waf中,就等于传入了两个参数,此时截取的1是i_d=aaa&i.d=aaa,然后使用&拆分开,拆分成i_d=aaa和i.d=aaa,然后将value再使用=进行拆分,第二个waf中接受的是i_d=aaa,而此时的恶意代码正好在第一个里面,这个恶意代码还要经过dhtmlspecialchars函数的过滤,重新赋值给 $_REQUEST[$_value[0]] = dhtmlspecialchars(addslashes($_value[1]));中的$_REQUEST,$key是i_d,value[0]是aaa(恶意代码),此时将恶意代码赋值给$_REQUEST,上述代码正好接到了恶意代码。

index.php:

此时可以看出,并未被过滤,实际上被过滤的是i.d=aaaaa,前面的恶意代码未被拦截。但是dhtmlspecialchars函数还是会过滤,所以不能用括号,使用联合查询代替。

对i_d=1&i.d=aaaaa&submit=1进行分析

waf2分析

  • 接下来我们要对这个数组进行循环,第一个key为 [0],value为i_d=1;第二个key为[1] ,value为i.d=aaaaa;第三个key为[2] ,value为submit=1。
  • 第一个为value为i_d=1,此时这个value使用等号分割为id和1,$_value[1]就是1,[0]就是i_d,这里的$_REQUEST[i_d]目前等于1。
  • 第二行结束后,结果为数组,(由于业务处理模块需要的是i_d),第一个元素['0']='i_d',第二个元素['1']='1',然后value[1]取出1,然后经过dhtmlspecialchars函数处理,赋值给&_REQUEST,value[0]是i_d,最终结果为$_REQUEST[i_d]=1,最终传入业务处理模块,去除了1也就是name。

五、总结

  1. hpp php 只接收同名参数的最后一个,这个也涉及hpp全局参数污染。
  2. php中会将get传参中的key中的.转为_
  3. $_REQUEST 遵循php接收方式,i_d&i.d中的最后一个参数的转换为下划线然后接收,所以我们的正常代码放在第二个参数,waf失效。

    广告一刻

    为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!