您的位置 » 首页 » 代码审计 » 代码审计:在magic_quotes_gpc=off下绕过addslashes和preg_replace两种函数

代码审计:在magic_quotes_gpc=off下绕过addslashes和preg_replace两种函数

发表于5年前 | 作者: seay | 分类: 代码审计 | 孵化于:2012年11月06日 | 文章热度:4,434 次 全屏阅读

显示不全请点击全屏阅读

在PHP6公布的一些资料上来看(现在主流PHP5和PHP4),register_globals、magic_quotes_gpc、safe_mode这三个选项将会不复存在,官方这样的做法考虑到PHP可移植性问题。去掉了GPC选项,很多程序都要改写。到底会改写什么呢?起码会增加一个模拟magic_quotes_gpc的函数来处理数据。那 么现在研究在magic_quotes_gpc=off下绕过函数将会很有前景。

 

 

addslashes函数

很多程序都会考虑到GPC影响功能会关闭掉GPC选项,而选择用PHP的一个内置函数addslashes来进行预定转义,并模拟一段代码:

 

define(‘MAGIC_QUOTES_GPC’, get_magic_quotes_gpc());

foreach(array(‘_COOKIE’, ‘_POST’, ‘_GET’) as $_request) {

foreach($$_request as $_key => $_value) {

$_key{0} != ‘_’ && $$_key = daddslashes($_value);

}

}

function daddslashes($string, $force = 0) { //这里daddslashes函数跟addslashes是不用的,注意名称。

!defined(‘MAGIC_QUOTES_GPC’) && define(‘MAGIC_QUOTES_GPC’, get_magic_quotes_gpc());

if(!MAGIC_QUOTES_GPC || $force) {

if(is_array($string)) {

foreach($string as $key => $val) {

$string[$key] = daddslashes($val, $force);

}

} else {

$string = addslashes($string);

}

}

return $string;

}

 

 

  define、defined是PHP内置函数,前者用于定义常量,后者则是查看常量是否存在。这里有两个foreach读者会比较难理解:首 先把’_COOKIE’、’_POST’、’_GET’这三个值以array函数传递给$_request,此时$_request就会变成数 组,$_request[0]的值是_COOKIE,$_request[1]的值是_POST,$_request[2]的值是_GET,并且用第一个foreach将它们循环输出;然后再到第二个foreach:$$_request这句其实可以代换成“$$_request[0]”、 “$$_request[1]”、“$$_request[2]”;由于在PHP中,在双引号中的变量仍然起作用并可代表在变量里的值,所以上面三个数组 变量再代换成大家很熟悉的“$_COOKIE”、“$_POST”、“$_GET”,并循环输出。

 

 

$_key{0} != ‘_’即是我们提交的第一个数据的名称不能是“_”,这个显然可以满足;然后到了daddslashes($val, $force),注意这里传入的参数是$val和$force。

最后就是daddslashes函数的定义:&&代表AND运算符,||代表OR运算符;不存在MAGIC_QUOTES_GPC常量和 并且用define函数定义常量做一个AND运算。继续,如果MAGIC_QUOTES_GPC不存在值,而$force不存在值、或者两者都存在值,则 判断$string是否为数组,是的话就用foreach循环输出每一个值,并用daddslashes函数循环预定反义每一个值。注意这里的参数还 是$val和$force。

 

 

这段代码需要用到运算符高低级,函数的默认值、递归调用等等的小知识。这段代码我们不难看出,addslashes函数的参数($string)就是daddslashes函数的参数$val。

大家可能认为这段代码找不到什么问题,但如果你注意到它的参数的调用,就不难发现:函数只过滤了$val,而$_key并没有过滤。相反,在magic_quotes_gpc=on时不仅仅处理变量值,还处理变量名。其实daddslashes函数只是模拟了GPC的一部分功能而已‘

 

实例

ECShop是最大的免费开源网店系统(用户量不在话下)。ECShop小于v2.6.2版本的includes/init.php(这个文件经常被包含)当GPC为OFF时使用addslashes_deep函数过滤

 

$_REQUEST、$_COOKIE:

if (!get_magic_quotes_gpc())

{

if (!empty($_GET))

{

$_GET = addslashes_deep($_GET);

}

if (!empty($_POST))

{

$_POST = addslashes_deep($_POST);

}

 

$_COOKIE = addslashes_deep($_COOKIE);

$_REQUEST = addslashes_deep($_REQUEST);

}

 

如果get_magic_quotes_gpc为off而且存在GET提交过来的数据,则用addslashes_deep函数来过滤$_GET这 个PHP内置变量(数组),下面类同。addslashes_deep函数在includes/lib_base.php里最后通过addslashes 函数处理:

 

function addslashes_deep($value)

{

if (empty($value))

{

return $value;

}

else

{

return is_array($value) ? array_map(‘addslashes_deep’, $value) : addslashes($value);

}

}

 

 

如果$value为空,则返回为空;否则用“A?B:C”运算符来判断,这个其实可以代换成:

 

if (is_array($value))

{

array_map(‘addslashes_deep’, $value)

}

else

{

addslashes($value)

}

如果$value是数组,就用PHP内置函数array_map来调用addslashes_deep函数重复过滤value数组的每一个值;否则用addslashes来预定反义。其中array_map函数的作用,用一个例子来解说:

function cube($n) {

return $n*$n*$n; //这个函数用参数$n,并以$n的三次方的值返回

}

 

$a = array(1, 2, 3, 4, 5); //$a是数组

$b = array_map(“cube”, $a); //用cube函数在array_map下重复执行在$a数组中每一个值的三次方运算

print_r($b); //$b是数组,做输出作用。

?>

 

 

 

我们在看完函数后再来看ECShop的漏洞文件pick_out.php:

 

if (!empty($_GET[‘attr’]))

{

foreach($_GET[‘attr’] as $key => $value)

{

$key = intval($key);

$_GET[‘attr’][$key] = htmlspecialchars($value);

 

}

}

省略部分代码……

$attr_table = ”;

$attr_where = ”;

$attr_url = ”;

$i = 0;

$goods_result = ”;

foreach ($_GET[‘attr’] AS $key => $value)

{

$attr_url .= ‘&attr[‘ . $key . ‘]=’ . $value;

 

$attr_picks[] = $key;

if ($i > 0)

{

if (empty($goods_result))

{

break;

}

$goods_result = $db->getCol(“SELECT goods_id FROM ” . $ecs->table(“goods_attr”) . ” WHERE goods_id IN (” . implode(‘,’ , $goods_result) . “) AND attr_id=’$key’ AND attr_value=’$value’”);

}

else

{

$goods_result = $db->getCol(“SELECT goods_id FROM ” . $ecs->table(“goods_attr”) . ” WHERE attr_id=’$key’ AND attr_value=’$value’”);

}

$i++;

}

 

这里读者可能感到迷惑,PHP内置函数intval强制转换key为数字型,还有什么问题?仔细地看到上面的第一个foreach的$_GET[‘attr’] as $key => $value了吗?它的代码可以代换成:

 

if (!empty($_GET[‘attr’]))

{

foreach($_GET[‘attr’] as $key => $value)

{

foreach($value[$key]=$_GET[‘attr’]) //我这样写是不规范的,只是为了让读者更容易明白。

 

 

 

{

 

$key = intval($key);

 

$_GET[‘attr’][$key] = htmlspecialchars($value);

 

}

 

 

 

 

这里我们看到了,as关键字的作用是在foreach中将$_GET[‘attr’]数组的每一个键和每一个值循环复制给$key和$value,但它并不影响我们提交在$_GET[‘attr’]中的数据(它只是一个拷贝)。尽管这样,但是这个文件包含了我们上面所说的init.php,也就是说$_GET[‘attr’]的每一个值我们都不能控制,但每一个键我们还是可以利用的!

 

再就是第二个foreach,$key变量先经过了两个赋值,后if判断,程序已经在上面定义了$i=0,那么我们将会执行下面else那个SQL查询。 这个循环虽然也是拷贝$_GET[‘attr’],但是这个和上面那个不同,这个foreach是直接带入数据库查询的。不过这里并没有对$key做任何 过滤,从而导致SQL注入。构造如下SQL:

 

 

 

pick_out.php?cat_id=123456&attr[‘ UNION SELECT CONCAT(user_name,0x3a,password) AS goods_id FROM ecs_admin_user WHERE action_list=’all’ LIMIT 1#]=evilrapper

 

 

 

我们看到上面的“SELECT goods_id FROM ”. $ecs->table(“goods_attr”) . ”WHERE attr_id=’$key’AND attr_value=’$value’”只有一个列,如果我们要用联合查询就需要用到CONCAT函数帮我们连接字符串。其中0×3a是“:”的十六进 制形式。这个语句让前面attr_id为空,那么前面的WHERE子句就会为FALSE,从而进入我们的联合查询。

 

 

 

 

 

 

 

preg_replace函数

 

如果用这个函数过滤数据,应该会用来处理XSS。由于我还没找到关于这个函数的实例,下面的代码(用于处理注入)是在理论的状态下

 

 

 

$test=0; extract($_GET); echo $test=preg_replace(“/^(.*)(\’)/”, “\\1\\\\’”, $test); ?>

 

 

 

 

 

这个属于正则表达式过滤,我们提交“1′and 1=1#,返回如图51所示。这个正则对单引号反义处理了。那么想绕过这种函数只有一个原则:让它匹配不了。

 

“.*”可以匹配任意数量的字符,但它并不匹配回车符。我们将回车符进行URL编码得到%0D%0A,并提交%0D%0A1′and 1=1#,那么正则表达式匹配不了,函数就会自然地返回原值,如图52所示。

\" width=

 

 

图51

\" width=

 

图52

Tags:

代码审计, 函数绕过,

如果您喜欢我的博客,欢迎点击图片定订阅到邮箱填写您的邮件地址,订阅我们的精彩内容: 也可以点击链接【订阅到鲜果】

如果我的想法或工具帮助到了你,也可微信扫下方二维码打赏本人一杯咖啡


来自 Seay互联网安全博客
本文地址:http://www.cnseay.com/1595/
文章版权说明请看置顶文章,尊重作者,转载请以链接形式标明原文地址

马上分享给你的朋友吧~

发表评论

你的大名(必填)

你的邮箱(必填)

评论内容(必填)