变量覆盖
通常将可以用自定义的参数值替换原有变量值的情况称为变量覆盖漏洞
extract
具体可以查看手册上的内容php:extract
warning:不要对不能信任的数据使用 extract(),例如用户的输入($_GET, $_FILES,…)。如果这样做,举例说,要临时运行依赖于 register_globals 的老代码,要确保使用不会覆盖的 extract_type 值,例如 EXTR_SKIP,并且要留意应该按照 variables_order 在 php.ini 里 定义的顺序来提取。
下面看一个题目具体分析一下:
我们要使$gift=$flag,由于extract($_GET)
,我们可以直接传gift和flag的值即可,但有file_get_contents,我们就要找它为空的时候是什么
为空的时候是NULL,那么我们可以传?flag=&gift=
就行了
$$
由于php中可以使用$$声明变量,因此当在遍历数组时可能会覆盖原来的值
<?php
highlight_file(__FILE__);
$a="hello world";
echo "$a";
echo "<br>";
foreach ($_GET as $key => $value)
{
$$key = $value;
}
echo "$a";
?>
使用foreach来遍历数组中的值,然后再将获取到的数组键名作为变量,数组中的键值作为变量的值。我们传入?a=hello
造成变量覆盖
下面一个例题具体分析一下:
由于$$key = $$value
我们传入的flag会覆盖已有的flag,所以说我们应该用一个变量先覆盖之前的flag,由于最后有die($_200),会输出数据,我们就传入?_200=flag
,然后POST方式提交flag=a
即可
parse_str
如果没有array 参数,则由该函数设置的变量将覆盖已存在的同名变量
参数 | 描述 |
---|---|
string | 必须。规定要解析的字符串 |
array | 可选,规定储存变量的数组的名称。该参数指示变量将被存储到数组中 |
用GET方式提交id,我们可以用id覆盖a[0],然后传入与QNKCDZO
一样md5的值即可
注意:由于 PHP 的变量名不能带「点」和「空格」,所以它们会被转化成下划线。
参考文章:由一道ctf学习变量覆盖漏洞
全局变量$GLOBALS
$GLOBALS — 引用全局作用域中可用的全部变量
一个包含了全部变量的全局组合数组。变量的名字就是数组的键。
实验环境:bugku:变量1
只要运行 eval(“var_dump($$args);”)即可得到flag
由$$args我们可以猜想$args很有可能是一个数组,应该想到的就是超全局变量,传入即可的到flag
空白符
<?php
$info = "";
$req = [];
$flag="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
ini_set("display_error", false); //为一个配置选项设置值
error_reporting(0); //关闭所有PHP错误报告
if(!isset($_GET['number'])){
header("hint:26966dc52e85af40f59b4fe73d8c323a.txt"); //HTTP头显示hint 26966dc52e85af40f59b4fe73d8c323a.txt
die("have a fun!!"); //die — 等同于 exit()
}
foreach([$_GET, $_POST] as $global_var) { //foreach 语法结构提供了遍历数组的简单方式
foreach($global_var as $key => $value) {
$value = trim($value); //trim — 去除字符串首尾处的空白字符(或者其他字符)
is_string($value) && $req[$key] = addslashes($value); // is_string — 检测变量是否是字符串,addslashes — 使用反斜线引用字符串
}
}
function is_palindrome_number($number) {
$number = strval($number); //strval — 获取变量的字符串值
$i = 0;
$j = strlen($number) - 1; //strlen — 获取字符串长度
while($i < $j) {
if($number[$i] !== $number[$j]) {
return false;
}
$i++;
$j--;
}
return true;
}
if(is_numeric($_REQUEST['number'])) //is_numeric — 检测变量是否为数字或数字字符串
{
$info="sorry, you cann't input a number!";
}
elseif($req['number']!=strval(intval($req['number']))) //intval — 获取变量的整数值
{
$info = "number must be equal to it is integer!! ";
}
else
{
$value1 = intval($req["number"]);
$value2 = intval(strrev($req["number"]));
if($value1!=$value2){
$info="no, this is not a palindrome number!";
}
else
{
if(is_palindrome_number($req["number"])){
$info = "nice! {$value1} is a palindrome number!";
}
else
{
$info=$flag;
}
}
}
echo $info;
我先把题目代码放出来,学习一下相关函数再来解
intavl函数
成功时返回 var 的 integer 值,失败时返回 0。 空的 array 返回 0,非空的 array 返回 1。
最大的值取决于操作系统:
32 位系统最大带符号的 integer 范围是 -2147483648 到 2147483647
64 位系统上,最大带符号的 integer 值是 9223372036854775807。
浮点数精度
当1.01的小数位加到一定次数后,会认为和1一样大
is_numeric函数
is_numeric函数对于空字符%00,无论是%00放在前后都可以判断为非数值,而%20空格字符只能放在数值后。所以,查看函数发现该函数对于第一个空格字符会跳过空格字符判断,接着后面的判断!
并且会跳过一些符号:
这里有个题目顺便说一下
实验环境:bugku:矛盾
$num=$_GET['num'];
if(!is_numeric($num))
{
echo $num;
if($num==1)
echo 'flag{**********}';
}
不能传is_numeric类型,又要等于1,由于是等于==
,弱类型,我们可传:?num=1x
,x为非数字字符即可
trim函数
strrev函数
解题
- number不为空,且不能是一个数值型数字,包括小数。(由is_numeric函数判断)
- number不能是一个回文数。(is_palindrome_number判断)
- 该数的反转的整数值应该和它本身的整数值相等(strrev判断)
一:由于我的系统是64位的,导致intval没有溢出,但是32位可以通过溢出来写
二:采用科学计数法构造payload为number=0e-0%00
,成立
三:由于trim未过滤\f,我们可以传入\f的url编码%0c,绕过了回文数,number=%00%0c121
四:%2B解析后为+,‘+121’=='121’且intval(‘121+’)==121 ,可传?number=%00%2B121
具体可参考文章:绕过过滤的空白字符
最近发现一个练习php代码审计的地址:PHP代码审计分段讲解,需要可在师傅的github下载源码