php中的一些需要注意点
1、global
global的作用是定义全局变量,但是这个全局变量不是应用于整个网站,而是应用于当前页面,包括include或require的所有文件。但是在函数体内定义的global变量,函数体内可以使用,在函数体外定义的global变量不能再函数体内使用。
2、$GLOBALS
在$GLOBALS数组中,每一个变量为一个元素,键名对应变量名,值对应变量的内容。$GLOBALS是一个超全局变量。注意$GLOBALS的写法,比如变量$a1,写法为$GLOBALS['a1'].
变量覆盖问题
register_globals
register_globals是php.ini里的一个配置,这个配置影响到php如何接收传递过来的参数。
register_globals为on的时候,传递过来的值会被直接注册为全局变量直接引用,而off的时候,我们需要到特定的数组里去得到它。
例如:
register_globals=off的时候,下一个程序接收的时候应该用$GET['user_name']和$_GET['user_pass']来接收传递过来的值。
register_globals=On的时候,下一个程序可以直接使用$user_name和$user_pass来接收值。
经常导致变量覆盖漏洞的场景有:$$使用不当,extract()函数使用不当,parse_str()函数使用不当,import_request_variables()使用不当,开启了全局变量注册等。
$$导致的变量覆盖问题
$$这种写法称为可变变量,一个可变变量获取了一个普通变量的值作为这个可变变量的变量名。
<?php
$a = "hello";
echo "$a"; //输出hello
$a="world";
echo "$a"; //输出hello
echo "$$a"; //输出word
echo "$a ${$a}"; //输出hello world
echo "$a $hello"; //输出hello world
?>
漏洞产生
使用foreach来遍历数组中的值,然后在将获取到的数组键名作为变量,数组中的键值作为变量的值,因此产生了变量覆盖漏洞。
<?php
foreach ($_GET as $key => $value) {
${$key} = $value;
}
echo $a;
?>
get得到的数据和value,关键第3行,用传进来的key做为新的变量,将get传进来的赋值给它。第行回解析为a=1。就造成了变量覆盖。
extract()函数使用不当
1、extract()函数介绍
extract()函数从数组中将变量导入到当前的符号表。
该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量。
该函数返回成功设置的变量数目。
2、语法
extract(array,extract_rules,prefix)
参数 描述
array必需。规定要使用的数组
extract_rules可选。extract()函数将检查每个键名是否为合法的变量名,同时也检查和符号表中已存在的变量名是否冲突。对不合法和冲突的键名的处理将根据参数决定。
可能的值: EXTR_OVERWRITE - 默认。如果有冲突,则覆盖已有的变量。 EXTR_SKIP - 如果有冲突,不覆盖已有的变量。 EXTR_PREFIX_SAME - 如果有冲突,在变量名前加上前缀 prefix。 EXTR_PREFIX_ALL - 给所有变量名加上前缀 prefix。 EXTR_PREFIX_INVALID -仅在不合法或数字变量名前加上前缀 prefix。 EXTR_IF_EXISTS - 仅在当前符号表中已有同名变量时,覆盖它们的值。其它的都不处理。 EXTR_PREFIX_IF_EXISTS - 仅在当前符号表中已有同名变量时,建立附加了前缀的变量名,其它的都不处理。 EXTR_REFS - 将变量作为引用提取。导入的变量仍然引用了数组参数的值。
prefix可选。 如果 extract_rules 参数的值是 EXTR_PREFIX_SAME、EXTR_PREFIX_ALL、 EXTR_PREFIX_INVALID 或 EXTR_PREFIX_IF_EXISTS,则 prefix 是必需的。 该参数规定了前缀。前缀和数组键名之间会自动加上一个下划线。
从以上说明我们可以看到第一个参数是必须的,会不会导致变量覆盖漏洞由第二个参数决定,该函数有三种情况会覆盖已有变量。
<?php
$a = 1; //原变量值为1
$b = array('a' => '3');
extract($b); //经过extract()函数对$b处理后
echo $a; //输出结果为3
?>
3、漏洞重现
"extract($_GET);
if(isset($bdctf))
{
$content=trim(file_get_contents($flag));//file_get_contents—将整个文件读入一个字符串
if($bdctf==$content) //trim—去除字符串首尾处的空白字符(或者其他字符)
{ echo'bdctf{**********}'; }
else
{ echo'这不是蓝盾的密码啊'; }
}"
题目分析 题目使用了extract($_GET)接收了GET请求中的数据,并将键名和键值转换为变量名和变量的值,然后再进行两个if 的条件判断,所以可以使用GET提交参数和值,利用extract()对变量进行覆盖,从而满足各个条件。
解题思路 if($bdctf==$content) 输出flag 利用extract($_GET)漏洞,使$bdctf与$content都为空或者不存在就满足 $bdctf==$content get ?flag=&bdctf= 得到flag
parse_str()函数使用不当
1、parse_str函数介绍
parse_str()函数把查询字符串解析到变量中。
注释:如果未设置array参数,由该函数设置的变量将覆盖已存在的同名变量。
注释:php.ini文件中的magic_quotes_gpc设置影响该函数的输出。如果已启用,那么在parse_str()解析之前,变量会被addslashes()转换。
parse_str函数的作用就是解析字符串并注册成变量,在注册变量之前不会验证当前变量是否存在,所以直接覆盖掉已有变量
2、语法
parse_str(string,array)**
参数描述
string必需。规定要解析的字符串。
array可选。规定存储变量的数组名称。该参数指示变量存储到数组中。
import_request_variables()使用不当
1、import_request_variables()函数介绍
import_request_variables 将GET/POST/Cookie变量导入到全局作用域中
import_request_varibales()函数就是把GET、POST、COOKIE的参数注册成变量,用在register_globals被禁止的时候
2、语法
bool import_request_variables(string$type,[string$prefix])
$type代表要注册的变量,G表示GET,P表示POST,C代表COOKIE,第二个参数为前缀
原因是PHP中变量是以C语言的结构体来存储的,空字符串和NULL,false都是以值为0存储的,其中这个结构体有个zen_uchartype;这样的成员变量,他是用来保存变量的类型的,而空字符串的类型是string,NULL的类型是NULL, false是boolean
所以空字符串'',false,NULL和0是值相同而类型不一样
注意:NULL是一种特殊的类型,两种情况下为NULL。
1、$var = NULL;
2、$var;
3、""、0、"0"、NULL、FALSE、array()、var $var; 以及没有任何属性的对象都将被认为是空的,如果var为空,则返回TRUE(这地方我是不太理解的)
$a = 0;
$b="0";
$c= '';
$d= null;
$e = false;
echo "5个变量-原始测试类型";
var_dump($a);//int 0
var_dump($b);//string '0'
var_dump($c);//string ''
var_dump($d);//null
var_dump($e);//boolean false
echo "<h4>empty测试</h4>";
var_dump(empty($a));//true
var_dump(empty($b));//true
var_dump(empty($c));//true
var_dump(empty($d));//true
var_dump(empty($e));//true
echo "<hr>";
var_dump(isset($a));//true
var_dump(isset($b));//true
var_dump(isset($c));//true
var_dump(isset($d));//【false】 见结论一
var_dump(isset($e));//true
echo "<h4>(==)双等式测试</h4>";
var_dump($a == $b);//true
var_dump($a == $c);//true
var_dump($a == $d);//true
var_dump($a == $e);//true !!
var_dump($b == $c);//【false】见结论二
var_dump($b == $d);//【false】见结论二
var_dump($b == $e);//true
var_dump($c == $d);//true
var_dump($c == $e);//true
echo "<h4>(===)三等式测试</h4>";
var_dump($a === $b);//false
var_dump($a === $c);//false
var_dump($a === $d);//false
var_dump($a === $e);//false
var_dump($b === $c);//false
var_dump($b === $d);//false
var_dump($b === $e);//false
var_dump($c === $d);//false
var_dump($c === $e);//false
总结
对于【0;'0';'';null;false】五种类型
empty操作以上五个变量,都返回false
结论一:
关于变量类型的理解
1.null为不存在之意:php底层的zval空间里(结构见下方)没有存其value值,只存储了一个type标志其 IS_NULL(所以解释了 empty(null)=true,isset(null)=false ,isset('')=true)
2.【0 ; '0' ; '' ; false 】:这四个为存在,php底层是开辟zval空间存储,有value,有type.
结论二:
1、string '0'与 string '' 不相等,(想一下就明白,同类型比较【1个长度】的字符串怎么可能 等于 【0个长度】 的字符串)
2、int 0 却和 string '' 空相等,(非同类形比较,php会做类型转换)
3、string '0' 与 null 不相等,int 0 与 null 相等
ereg()
ereg()函数用指定的模式搜索一个字符串中指定的字符串,如果匹配成功返回true否则返回false。搜索字母的字符是大小写敏感的。
ereg函数存在NULL截断漏洞,导致正则过滤被绕过,所以可以使用%00截断正则匹配。
ereg()只能处理字符串,遇到数组做参数返回NULL,判断用的是===,要求类型也相同,而NULL跟FALSE类型是不同的,strpos()参数同样不能为数组,否则返回NULL。
preg_replace()函数
preg_replace($pattern, $replacement, $subject)
函数作用:搜索subject中匹配pattern的部分,以replacement进行替换。
$pattern:要搜索的模式,可以是字符串或一个字符串数组
$replacement:用于替换的字符串或字符串数组。
$subject:要搜索替换的目标字符串或字符串数组
preg_replace函数的参数pattern输入/e模式的时候,参数replacement的代码当做PHP代码执行。
intval()函数绕过
intval函数参数填入科学计数法的字符串,会以e前面的数字作为返回值,而对于科学计数法+数字则会返回字符串类型。
<?php
$num='2e4';
echo(intval($num)); //输出2
echo('<br>');
echo(intval($num+1)); //输出20001
?>