22. 你真的会PHP吗?
分析:
首先我们来到欢迎界面,看到了havefun!再无其他信息,测试了几下发现需要源码,在返回包找到了提示
或者burpsuite抓包:
我们可以看见 hint(提示),那就打开它看看吧
直接代码审计:
<?php
$info = "";
$req = [];
$flag="xxxxxxxxxx";
ini_set("display_error", false);
error_reporting(0);
if(!isset($_POST['number'])){ //注意这里post请求 1.不能为空
header("hint:6c525af4059b4fe7d8c33a.txt"); //文件头添加hint提示。
die("have a fun!!"); // 直接就die了
}
foreach([$_POST] as $global_var) { //遍历数组
foreach($global_var as $key => $value) {
$value = trim($value); //trim() 函数移除字符串两侧的空白字符或其他预定义字符。
is_string($value) && $req[$key] = addslashes($value);
}
}
//global $var是外部$var的同名引用或者指针。
//函数
function is_palindrome_number($number) {
$number = strval($number); //本函数可将数组及类之外的变量类型转换成字符串类型。
$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'])){
$info="sorry, you cann't input a number!";
}elseif($req['number']!=strval(intval($req['number']))){
$info = "number must be equal to it's integer!! ";
}else{
$value1 = intval($req["number"]);
$value2 = intval(strrev($req["number"])); //strrev() 函数反转字符串。
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;
解析:
is_numeric($_REQUEST['number']);
$req['number']!=strval(intval($req['number'])
函数判断是否为数字 这两句要求提交的数不能是数字包括小数
if(intval($req["number"])=intval(strrev($req["number"])))
这句的要求为该数的反序列的整数值应该等于它本身的整数值即是一个回文数
is_palindrome_number($req["number"])
这句要求提交的数不是一个回文数
php函数:
ini_set("display_error",false);这是设置php.ini,不报错
trim()函数移除字符串两侧的空白字符或其他预定义字符。
intval()获取变量的整数值,允许以使用特定的进制返回。不管什么类型如果是数字,就返回数字,如果不是数字就返回0intval()而言,如果参数是字符串,则返回字符串中第一个不是数字的字符之前的数字串所代表的整数值。如果字符串第一个是‘-',则从第二个开始算起。如果参数是符点数,则返回他取整之后的值。当然intval()返回的值在一个4字节所能表示的范围之内
(-2147483648~2147483647),对于超过这个范围的值将用边界值代替。
strval()把值变为字符串
strrev()函数反转字符串。
is_numeric检测变量是否为数字或数字字符串
$_REQUEST变量默认情况下包含了$_GET,$_POST和$_COOKIE的数组。
函数漏洞:
关于使用intval强制转换成数字的问题。数字大于2147483647会出现溢出出现负数。使用个方法来替代这个吧
获取flag条件
1:必须要有POST['number']这个数据
2:POST【‘number’】的值必须为字符串
3;POST【‘number’】的值和strval(intval($req['number'])的值要相同
4:POST【‘number’】的翻转字符串必须和原字符串相同
5:POST【‘number'】的值不能为回文字符串
思考:
第一条没什么,第二条貌似和第三条冲突,因为根据intval的功能,它把一个字符串转化为int型,因为它必定会截取掉原来字符串中的字符,必定会和原来不同。第四条的第五条也看似冲突,因为翻转字符要和原字符相同,但是它还不能是回文字符串。
根据查的资料,第三条的第四条很容易解决,根据intval()返回的值在一个4字节所能表示的范围之内(-2147483648~2147483647)(32位)(64位不一样),对于超过这个范围的值将用边界值代替。可以利用一个边界值来绕过应为边界值的翻转为7463847412,它大于2147483647,则它为2147483647,两值相等,且不是回文字符。
解决方法:
我们可以利用intval函数的限制即:Intval最大的值取决于操作系统。
32位系统最大带符号的 integer 范围是 -2147483648 到 2147483647。举例,在这样的系统上, intval(‘1000000000000’) 会返回 2147483647。构造payload:url?2147483647%00绕过,在浏览器上没有反应,拿burp post
is_numeric函数在开始判断前,会先跳过所有空白字符,可是题目获取$req[‘number’]的时候明明使用trim过滤了空白字符,这时候我们可以引入\f(也就是%0c)在数字前面,来绕过最后那个is_palindrome_number函数,而对于前面的数字判断,因为intval和is_numeric都会忽略这个字符
所以我们可以另构造url?number=%00%0c121绕过
我们还可以使用科学计数法绕过:
补充: