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繞過
我們還可以使用科學計數法繞過:
補充: