<?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's 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;
审计代码可以发现,number必须符合下面3个条件才可以输出flag
1.不为空,且不能是一个数值型数字,包括小数。(由is_numeric函数判断)
2.不能是一个回文数。(is_palindrome_number判断)
3.该数的反转的整数值应该和它本身的整数值相等;即
intval($req["number"])=intval(strrev($req["number"]))
但是2和3又冲突,这该怎么办
方法一
我们构造的payload为:
?number=%002147483647
解释下为什么可以这么构造;
1、is_numeric函数对于空字符%00判断为非数值,绕过第一个条件。
函数判断为非数值,但又不影响它值的构造,理所当然想到空格字符%20和空字符%00。
2、2147483647不是一个回文数绕过第二个条件。
3、这里利用了intval函数的溢出问题。
Intval最大的值取决于操作系统。 32 位系统最大带符号的 integer 范围是 -2147483648 到 2147483647。举例,在这样的系统上, intval(‘1000000000000’) 会返回 2147483647。64 位系统上,最大带符号的 integer 值是 9223372036854775807。
至于服务器是什么系统的可以用burp抓包分析出来为32位
$value1 = intval($req["number"]);
$value2 = intval(strrev($req["number"]));
我们构造的2147483647经过strrev反转函数后为7463847412,又经过intval函数值又变为2147483647。
故满足条件3,可以输出flag
方法二
因为要求不能为回文数,但又要满足intval($req["number"])=intval(strrev($req["number"])),
所以我们采用科学计数法构造payload为number=0e-0%00
,这样的话我们就可以绕过。
xey在php中就是x*10的y次方
注意
is_numeric函数对于空字符%00,无论是%00放在前后都可以判断为非数值,而%20空格字符只能放在数值后
方法三
is_numeric函数在开始判断前,会先跳过所有空白字符可是题目获取$req[‘number’]的时候明明使用trim过滤了空白字符这时候我们可以引入\f(也就是%0c)在数字前面,来绕过最后那个is_palindrome_number函数,而对于前面的数字判断,因为intval和is_numeric都会忽略这个字符,所以不会影响。
所以我们构造payload=URL?%00%0c191即可绕过上面的条件获得flag