盘点PHP中的变量覆盖漏洞

admin 2024年4月28日07:54:02评论3 views字数 3326阅读11分5秒阅读模式

最近在学习PHP代码审计中的变量覆盖问题,所以写了一篇文章简单总结一下,欢迎师傅们指正

一、$$动态变量

1、基本语法

PHP动态变量是指一个变量名的变量名可以动态的设置和使用,一个变量获取另一个变量的值作为这个变量的变量名。

例如下面

$Hello = "World";
$a = "Hello";

echo $a; //hello
echo $$a; //World

# $a="hello",所以$$a=$hello=world

所以运行之后结果则为HelloWorld,其实就类似于C语言的指针

2、漏洞案例

$a = "ping 127.0.0.1";
$b = $_GET['1'];
$$b = $_GET['2'];
system($a);

分析题目,由于system函数执行的是$a,但现在$a的值是"ping 127.0.0.1",所以我们需要想办法覆盖$a的值为我们指定的命令,这样才能造成命令执行漏洞,答案如下

?1=a&2=whoami

这样传参了之后就变为

$b = a
$$b = $a = $_GET['2'] = 我们传入的whoami

从而利用变量覆盖漏洞让system函数执行任何我们传入的语句,也就是命令执行漏洞

二、extract()

1、基本语法

PHP extract()函数用于将数组中的变量导入到当前的符号表中。它需要一个关联数组数组,并将键作为变量名和值作为变量值。

例如下面

<?php
extract($_GET);  
echo$name.'<br>';
echo$age.'<br>';
echo$id.'<br>';
?>

当以GET方式传入?name=tacoking&age=21&id=123456时,输出的结果就是$name=tacoking;$age=22;$id=123456

也就是name、age、id作为变量名,传进来的值作为了它们的变量值

2、漏洞案例

$a = 'ping 127.0.0.1';
$arr1 = [$_GET['1'] => $_GET['2'],'b'=>2];
extract($arr1);
system($a);

同理,我们也是需要想法设法覆盖掉$a的值,可以看到这里$GET['1']和$GET['2']的值作为键值对会被引入符号表,所以我们只需要进行如下传参

?1=a&2=whoami

这样就可以得到$a=whoami,从而覆盖掉$a原来的参数值,也就是变量覆盖漏洞

三、list()

1、基本语法

list() 函数用于将数组中的值赋给一组变量

$info = ['John', 'Doe', 30];
list($firstName, $lastName, $age) = $info;

echo $firstName; // 输出 "John"
echo $lastName; // 输出 "Doe"
echo $age; // 输出 30

2、漏洞案例

$a = 'ping 127.0.0.1';
$arr2 = array($_GET['1'],2);
list($a ,$b) = $arr2;
system($a);

这里的意思也就时$a的值会等于$_GET['1'],所以此处我们直接传入$a的值为我们的payload就可以了,传参如下

?1=whoami

四、parse_str()

1、基本语法

parse_str() 函数用于解析 URL 查询字符串,并将其中的参数赋值给相应的变量

$queryString = 'name=John&age=30';
2 parse_str($queryString, $params);
3
4 echo $params['name']; // 输出 "John"
5 echo $params['age']; // 输出 "30"

2、漏洞案例

示例代码,下面附上一段存在变量覆盖导致RCE的漏洞代码案例

$queryString = $_SERVER['QUERY_STRING'];
# 用于获取查询字符串,例如'a=170&b=241'
$com = array('cmd'=>'ping 127.0.0.1');
# parse_str(str,array)
# str:要解析的查询字符串
# array:存储的数组位置(可选,如果不填就解析为单独的变量,填了就作为键值队存储到该数组中)
parse_str($queryString,$$_GET[1]);
echo $com['cmd'];
system($com['cmd']);

payload传参如下

?cmd=whoami&1=com

那这里的parse_str就变为了下面这样

parse_str("cmd=whoami&1=com",$com);

所以此时$com['cmd']的值就为whoami,自然也就造成了变量覆盖的任意命令执行

五、mb_parse_str()

mb_parse_str()PHP 中的函数用于解析 GET、POST 和 COOKIE 数据并设置全局变量。它解析 URL 编码的数据并检测编码。之后,它转换内部编码中的编码并设置全局变量的值。PHP 7 或更高版本支持此功能。

$str = '[email protected]&city=shanghai&job=diger';
mb_parse_str($str, $result);
print_r($result);

//结果
Array
(
      [email] => 49b1@0ms5vj6.site
       [city] => shanghai
       [job] => diger
)

mb_parse_str()函数和parse_str()类似,所以不再做累赘,感兴趣的可自行研究。

六、compact()

1、基本语法

compact() 函数用于将多个变量转换为关联数组,其中变量名将成为数组的键,变量的值将成为数组的值

$firstName = 'John';
$lastName = 'Doe';
$age = 30;

$info = compact('firstName', 'lastName', 'age');
print_r($info);

2、漏洞案例

示例代码

$com1 = 'ping 127.0.0.1';
$cmd2 = 'ping 127.0.0.1';
$cmd3 = 'whoami';
$info = compact($_GET['1'], 'cmd2');
# [cmd2] = > ping 127.0.0.1
foreach ($info as $a){
   echo system($a);
}

传参如下

?1=cmd3

如果外部变量和compact参数可控,就可以利用这种方法进行变量覆盖

七、register_globals配置

当register_globals全局变量设置开启时,传递过来的值会被直接注册为全局变量而使用,这会造成全局变量覆盖

在PHP5.3之前 默认开启 PHP5.3默认关闭  PHP5.6及5.7已经被移除

例如下列漏洞代码

<?php
if ($num){
  echo "flag{daylight-04-26}";
}
?>
//payload:http://127.0.0.1/test.php?num=1

这样传入的num=1就会被注册并使用,导致$num不为空,从而获得flag

八、import_request_variables()

(PHP 4 >= 4.1.0, PHP 5 < 5.4.0)

import_request_variables — 将 GET/POST/Cookie 变量导入到全局作用域中

将 GET/POST/Cookie 变量导入到全局作用域中。如果禁止了 register_globals,但又想用全局变量,就可以尝试该函数,简单来说就是用于手动注册传入的变量

例如下面这个漏洞代码案例

<?php
$num=0;
//include 'flag.php';
import_request_variables('gp'); //导入get和post中变量

if($num=="daylight"){
  echo 'flag{daylight-2020-3-28}';
  // echo $flag.php;
}else{
  echo "NO!";
}
?>

//payload:http://127.0.0.1/test.php?num=daylight
//flag{daylight-2020-3-28}

原文始发于微信公众号(Hacking庆尘):盘点PHP中的变量覆盖漏洞

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年4月28日07:54:02
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   盘点PHP中的变量覆盖漏洞https://cn-sec.com/archives/2691825.html

发表评论

匿名网友 填写信息