PHP代码审计:变量覆盖
0x00 前提
本文是给作为给小伙伴分享教学的一篇文章,给讲一下,所以写的比较简陋。。。见谅而且都比较基础,只是简单讲课提纲吧
0x01 变量覆盖审计
0x00 简介
变量覆盖,顾名思义就是可以覆盖已有变量值,导致变量覆盖的漏洞
常见的造成的代码审计的情景是代码中出现以下关键词:
- register_globals=on
- extract()函数
- parse_str()函数
- import_request_variables()函数
- $$
0x01 变量覆盖演示
extract()
extract(array,extract_rules,prefix)
函数
https://www.runoob.com/php/func-array-extract.html
该函数可以从数组中将变量导入到当前的符号表,即将数组中的键值对注册成函数,使用数组键名作为变量名,使用数组键值作为变量值。
这里我们要注意一下该函数的第二个参数
- EXTR_OVERWRITE - 默认。如果有冲突,则覆盖已有的变量。
- EXTR_SKIP - 如果有冲突,不覆盖已有的变量。
这就为我们提供了覆盖的可能。
1 |
|
可以看到我们初始变量值为a但是覆盖之后就变成了我们输入的值。
1 | http://127.0.0.1/test/extract.php?a=123 |
修复:
在使用extract()函数时,可以指定将第二个参数设置为EXTRS_KIP
parse_str()
parse_str()
函数用于把查询字符串解析到变量中,如果没有array参数,则由该函数设置的变量将覆盖已存在的同名变量。
在没有array参数的情况下使用此函数,
并且在PHP 7.2中将废弃不设置参数的行为,此函数没有返回值。
1 |
|
1 | http://127.0.0.1/test/parse_str.php?b=a=zeo |
$$
典型的例子就是foreach来遍历数组中的值作为变量。
$$是一种可变变量的写法,它可以使一个普通变量的值作为可变变量的名字,这种类型常常会使用遍历的方式来释放变量的代码
1 |
|
import_request_variables()
import_request_variables ( string $types , string $prefix )
https://www.runoob.com/php/php-import\_request\_variables-function.html
import_request_variables() 函数将 GET/POST/Cookie 变量导入到全局作用域中。该函数在最新版本的 PHP 中已经不支持。
import_request_variables() 函数将 GET/POST/Cookie 变量导入到全局作用域中。如果你禁止了 register_globals,但又想用到一些全局变量,那么此函数就很有用。
版本要求:PHP 4 >= 4.1.0, PHP 5 < 5.4.0
第二个参数$types:指定需要导入的变量,可以用字母 G、P 和 C 分别表示 GET、POST 和 Cookie,这些字母不区分大小写,所以你可以使用 g 、 p 和 c 的任何组合。POST 包含了通过 POST 方法上传的文件信息。注意这些字母的顺序,当使用 gp 时,POST 变量将使用相同的名字覆盖 GET 变量。任何 GPC 以外的字母都将被忽略。
全局变量
当你在升级PHP到PHP5.4及之后的版本的时候,是否发现register_global配置指令不再生效了呢
因为从PHP5.4开始register_global配置指令被移除了。
1 |
|
0x02 深x服edr实例
简化后的代码
1 |
|
变量匿名函数 $show_form
具有一个形式参数 $params
在这里也就是array(“strip_slashes”=>“system”,“host”=>“id”);
接下来执行**extract($params);**,后进入如下代码:
1 | $host = isset($host) ? $strip_slashes($host) : "127.0.0.1"; |
在这个过程中就产生了漏洞,想要了解具体原因:
首先函数传入参数值为array("strip_slashes"=>"system","host"=>"id");
经过extract()
函数后,赋值了2个变量:
1 | $strip_slashes = 'system'; |
变量$host
利用三元运算重新赋值$strip_slashes($host)
而实际上其赋值内容是函数system('id')
的返回结果,这也就造成了命令执行漏洞。
0x03MetInfo实例
/include/common.inc.php
传入的cookie、get、post参数进行变量赋值
1 | foreach(array('_COOKIE', '_POST', '_GET') as $_request) { |
daddslashes()
防注入,不过并不影响
随便来到一个子文件看看他的加载方式\about\index.php
1 |
|
这里使用了require_once函数包含了/include/module.php文件,可以发现这个文件又包含了common.inc.php文件
出现了两个未知变量:$module
,$fmodule
。我们可以用$fmodule
变量通过两次文件包含,使用$_request
来获取GET传递的新$fmodule
值实现变量覆盖。