PHP代码审计之变量覆盖攻击
0x01.$$导致的变量覆盖问题
$$介绍
$$这种写法称之为可变变量:一个可变变量获取了一个普通变量的值作为这个可变变量的变量名
demo
1 |
|
漏洞产生
此时我们来看这串代码;此时我们看出$key为COOKIE、POST、GET中的参数,$$request是一个间接引用,它会根据$request的值(即请求类型的名称)获取相应的全局变量数组。例如,如果$request的值是”COOKIE”,那么$$request就等同于$COOKIE;如果$request的值是”POST”,那么$$request就等同于$POST;如果$request的值是”GET”,那么$$request就等同于$GET。然后,
通过foreach循环遍历选定的全局变量数组,将每个元素的键存储在变量$key中,将每个元素的值存储在变量$value中。
那么此时我们继续使用一个demo来实验以下变量覆盖
1 |
|
此时我们提交a=2;那么在这个代码实施过程中会使得$key=a;$value=2;接着$$key=$a;那么此时又$$key=$value;将value的值赋给了$a;此时便造成了变量覆盖攻击;将原来有的$a=1通过覆盖成了$a=2
题目
1 |
|
此时我们就可以利用GET传入?yds=flag;然后经过第一次的foreach()后就会变成$x=yds;$y=flag;那么此时的$$x=$yds;然后$$y=$flag;即$yds=$flag;将原本的$yds = “dog”;进行覆盖成$yds=$flag;那么此时进入到第二个个if()中便会exit($flag)从而将flag显示出来
extract()函数
extract()
1 | extract(array[,flag][,prefix]) |
array
一个关联数组(必需)。此函数会将键名当作变量名,值作为变量的值。 对每个键/值对都会在当前的符号表中建立变量,并受到 flags 和 prefix 参数的影响。
flags
必须使用关联数组,数字索引的数组将不会产生结果,除非用了 EXTR_PREFIX_ALL
或者 EXTR_PREFIX_INVALID。
flags
对待非法/数字和冲突的键名的方法将根据取出标记 flags 参数决定。可以是以下值之一:
1 | EXTR_OVERWRITE |
prefix
注意 prefix 仅在 flags 的值是 EXTR_PREFIX_SAME,EXTR_PREFIX_ALL,EXTR_PREFIX_INVALID 或 EXTR_PREFIX_IF_EXISTS 时需要。 如果附加了前缀后的结果不是合法的变量名,将不会导入到符号表中。前缀和数组键名之间会自动加上一个下划线。
demo1
1 |
|
demo2
原本变量$b的值为3,经过extract()函数对变量$a处理后,变量$b的值被成功覆盖为’hello’。
安全的做法是确定register_globals=OFF后,在调用extract()时使用EXTR_SKIP保证已有变量不会被覆盖
1 |
|
但是此时若是变量未被定义或者未被初始化依然会出现变量覆盖的情况
题目
1 |
|
parse_str()函数
parse_str()函数的作用是解析字符串并且注册成变量,它在注册变量之前不会验证当前变量是否已经存在,所以会直接覆盖掉已有变量。
parse_str()
1 | void parse_str ( string $str [, array &$arr] ) |
其中$str是必须的,代表要解析成变量的字符串,形式为”a=1”,经过parse_str()函数之后会注册变量$a并且赋值为1。第二个参数$arr是一个数组,当第二个参数存在时,注册的变量会放到这个数组里面,但是如这个数组原来就存在相同的键(key),则会覆盖掉原有的键值。
ps:php.ini 文件中的 magic_quotes_gpc 设置影响该函数的输出。如果已启用,那么在 parse_str() 解析之前,变量会被 addslashes() 转换。
tips
GPC会自动把我们提交上去的单引号等敏感字符转义掉,这样我们的攻击代码就没法执行了,GPC是PHP天生自带的功能。GPC是用来过滤request中提交的数据,将特殊字符进行转义来防止攻击,在PHP5之后用$SERVER取到的header字段不受GPC影响,所以当GPC开启的时候,它里面的特殊字符如单引号也不会被转义掉,另外一点是普通程序员很少会考虑这些字段被修改。而在header注入里面最常见的是user-agent、referer以及client-ip/x-forward-for,
因为大多的Web应用都会记录访问者的IP以及referer等信息。同样的$FILES变量也一样不受GPC保护。
demo1
1 |
|
demo2
此时我们来测试一下
1 |
|
例题
1 |
|
payload
1 | ?id=a[0]=240610708 |