RCE系統交互條件與受限環境下的利用
簡單來說,RCE便是目標程序被遠程執行了未經授權的指令。
var name = requset.get("name") `
var cmd = "echo 'Hello " + name + "'"
RUN_CMD(cmd)`
外部輸?,進?簡單的拼接,最終放??個執?外部命令的函數中去執?
但命令執?的函數,都?定存在這個rce嗎? 如果不?定存在RCE,它們之間的差別是什么
我們平時說的RCE,比如thinkPHP的rce漏洞,即算代碼注入漏洞,也算RCE漏洞,因為RCE漏洞危害更大,優先級更高,所以直接叫RCE漏洞了,如果你想利用thinkphp的RCE漏洞去執行phpinfo,也是可以的,本質上就是文件包含漏洞(系統)造成代碼注入,最后產生RCE的一個過程。
實際安全領域的討論中,"RCE" 通常被廣泛用來指代遠程代碼執行漏洞。這種漏洞的危害較大,因為攻擊者可以通過遠程手段在目標系統上執行任意代碼,從而完全控制受影響的系統。
在一些框架或系統的漏洞報告中,特定的漏洞可能被歸類為 "RCE",即使它們的根本原因是其他類型的漏洞,比如文件包含漏洞或代碼注入漏洞。這種歸類的目的在于強調漏洞的嚴重性和潛在危害,因為遠程代碼執行意味著攻擊者可以在遠程服務器上執行他們自己的代碼。
如果一個漏洞只能造成代碼注入,但沒辦法RCE系統命令執行,這個時候我們才稱之為代碼注入漏洞。
但?先強調,我們在這里討論的是應用程序本身的代碼執行漏洞,而不是由于其他漏洞或第三方引起的外部代碼執行。
RCE與命令注入的區別取決于執行的目標與權限。
分類區別
代碼執行與命令執行區別
遠程代碼執行,?戶的輸?被當做代碼的?部分執?;
遠程命令執行,數據里面執行外部命令的函數沒有被過濾。
?般出現遠程命令執行漏洞,是因為應?系統從設計上需要給?戶提供指定的遠程命令操作的接?。? 如我們常?的路由器、防?墻、?侵檢測(硬件設備、?業交付)等設備的web管理界?上。 僅當Web應?程序代碼包含操作系統調?(外殼程序、shell)并且調?中使?了?戶輸?時, 才可能進?OS命令注?攻擊。它們不是特定于語?的,命令注?漏洞可能會出現在所有讓你 調?系統外殼命令的語?中:C,Java,PHP,Perl,Ruby,Python等。
RCE與命令注入區別
RCE主要是在操作系統上面執行代碼,比如whoami (因為RCE涉及到對操作系統底層功能的控制,比如調試與分析)。命令注入主要是在應用程序上面執行相關代碼,比如phpinfo (因為通過命令執行的函數可獲取到相關信息方便程序與系統交互,但對用戶提交的數據過濾不嚴格)。RCE與命令注入的區別取決于執行的目標與權限。
系統命令執行漏洞,當然是對操作系統進行控制和輸出的,而代碼注入漏洞,是對應用程序的相關代碼進行注入,從而達到控制應用程序的目的。
但有時候他們是交叉的 遠程命令執行包含命令注入,命令注入也能RCE,需要調用應用程序的相關代碼。
$user_input = $_GET['input'];`
system('whoami ' . $user_input);`
如果用戶能夠控制 $_GET['input'] 的值 在系統上面執行任意指令 便算是通過命令注入來造成RCE,如果不是在系統上面執行 在應用程序上面執行 就只能算 命令注入
可利用函數
遠程代碼執行
PHP中
eval() //把字符串作為PHP代碼執行
assert() //檢查一個斷言是否為 PALSE,可用來執行代碼
preg_replace() //執行一個正則表達式的搜索和替換
call user func() //把第一個參數作為回調函數調用
call_user_func_array() //調用回調函數,并把一個數組參數作為回調函數的參數
array_map()/7為數組的每個元素應用回調困數
Sa(sb) //動態困數
Python中
eval():
- 功能:執行一個字符串表達式,并返回表達式的值。
- 風險:如果執行的字符串來自不可信的源,則可能執行惡意代碼。
exec():
- 功能:執行存儲在字符串或代碼對象中的Python語句。
- 風險:與
eval()類似,如果執行的代碼來源不安全,可能導致安全漏洞。
pickle.loads()/pickle.load():pickle.loads()/pickle.load():
- 功能:反序列化pickle對象。
- 風險:如果反序列化的數據來自不可信的源,攻擊者可能利用pickle的特性執行任意代碼。
getattr():
- 功能:獲取對象的屬性值。
- 風險:如果屬性名和對象是動態提供的,并且來源不安全,可能被用來執行不安全的方法。
subprocess.Popen()/ 相關函數:
- 功能:執行外部命令和程序。
- 風險:如果外部命令的輸入部分來自用戶輸入或不安全的源,可能被用于執行惡意命令。
os.system()/os.popen():
- 功能:執行系統命令。
- 風險:同
subprocess.Popen(),如果命令字符串不安全,可能導致代碼注入
Java的安全模型和語言設計通常不允許直接執行存儲在字符串中的代碼。然而,Java中的一個著名安全風險源自于反序列化操作,這在惡意數據被反序列化時可能導致遠程代碼執行(RCE)。
假設有一個Java應用程序,它接受并反序列化來自外部的對象數據,例如,從網絡接收的對象流。這個程序可能使用了Apache Commons Collections庫。
- 攻擊者構建惡意對象:
- 攻擊者創建了一個特制的序列化對象,這個對象在反序列化時會觸發執行代碼的邏輯。
- 利用的是Apache Commons Collections中的
InvokerTransformer類,這個類可以調用任意方法。攻擊者將其配置為執行惡意代碼,比如運行一個外部命令。
- 序列化并發送對象:
- 這個惡意對象被序列化為字節流,并發送到目標Java應用程序。
- 目標應用反序列化:
- Java應用程序接收到這個對象,并開始反序列化過程。
- 在反序列化過程中,
InvokerTransformer被觸發,執行了預設的惡意代碼。
漏洞分析
這個漏洞的關鍵在于InvokerTransformer類的transform方法可以用來執行任意代碼,而在反序列化過程中,對象的方法會自動執行。因此,攻擊者可以構造一個當被反序列化時會自動執行惡意操作的對象。
防御措施
- 限制反序列化:在Java應用中,應限制或完全避免反序列化來自不可信源的數據。
- 使用安全庫:使用更新的安全庫,例如更新的Apache Commons Collections版本,這些版本已修復了此類漏洞。
- 使用對象白名單:在反序列化時,應用程序應該實現白名單機制,只允許預定義的安全類被反序列化。
遠程命令執行
PHP中
exec- 執行一個外部程序passthru- 執行外部程序并且顯示原始輸出proc_open- 執行一個命令,并且打開用來輸入/輸出的文件指針shell_exec- 通過shell 執行命令并將完整的輸出以字符串的方式返回system- 執行外部程序,并且顯示輸出
Python中
os.system()- 執行系統指令os.popen()- popen()方法用于從一個命令打開一個管道subprocess.call()- 執行由參數提供的命令
Java中
Runtime.getRuntime().exec()ProcessBuilder()
RCE與系統的交互
用戶 系統調用內核態
(進程是資源分配的基本單位與程序獨立運行的載體)
要深入理解RCE,我們需要探討操作系統如何處理來自用戶程序的請求,特別是涉及權限和安全性的方面。
用戶態與內核態
操作系統有兩種運行模式:用戶態和內核態。用戶態限制了程序對關鍵系統資源的訪問,以防止用戶程序直接與硬件交互,可能導致系統不穩定或不安全。內核態則允許操作系統內核訪問和控制硬件。
系統調用的作用
系統調用是用戶態與內核態之間的橋梁。當用戶程序需要執行如文件操作、網絡通信等需要更高權限的任務時,它會通過系統調用請求操作系統內核執行這些任務。系統調用是一種受控的機制,它確保了即使在執行這些高權限操作時,系統的安全性和穩定性也得到保護。
RCE與系統調用
RCE攻擊通常涉及操縱系統調用來執行惡意代碼。例如,在Linux系統中,攻擊者可能會利用不安全的程序邏輯,通過注入惡意命令來控制exec()系統調用,從而在受影響的系統上執行任意代碼。
RCE漏洞的復雜性與跨語言特性
RCE漏洞的復雜性在于它們可以跨越不同的編程語言和應用架構。例如,一個Web應用的RCE漏洞可能起始于一個簡單的PHP腳本注入,但最終可能導致在底層服務器的操作系統上執行惡意命令。
RCE的跨平臺特性
RCE不局限于任何特定的編程語言或平臺。它們可以出現在任何處理外部輸入的程序中,從服務器端的Web應用程序(如PHP、Java、Python)到客戶端的桌面應用程序(如C++或Java應用程序)。
syscall系統調用
操作系統將運行模式分為了 用戶態和內核態 他們中間通過系統調用syscall來進行交互,用戶態可以通過syscall來對內核態發起調用系統特權指令的請求,而請求的結果內核態可以通過syscall返回給用戶態,最大程度保障了操作系統的安全穩定(用戶空間通過向內核空間發出Syscall,產生[軟中斷]、而讓程序陷入內核態,執行相應的操作)。
系統調用 和 用戶自定義函數一樣,不同的是,系統調用運行在內核態,而用戶自定義函數運行在用戶態 由于某些指令(如設置時鐘 關閉 打開 和I/O操作)只能運行在內核態,所以操作系統必須提供一種能夠進入內核態的方式,系統調用便是這樣的機制,他提供了用戶態和內核態交互的接口。
?比如在C 語言中:
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
int main() {
// 打開文件
int file_descriptor = open("example.txt", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
if (file_descriptor == -1) {
perror("Error opening file");
return 1;
}
// 寫入文件
const char *text = "Hello, System Call!";
ssize_t bytes_written = write(file_descriptor, text, strlen(text));
if (bytes_written == -1) {
perror("Error writing to file");
close(file_descriptor);
return 1;
}
// 關閉文件
close(file_descriptor);
return 0;
}
在這個例子中,open、write和close等函數都是系統調用。open用于打開文件,write用于寫入文件,close用于關閉文件。這些函數提供了對底層文件系統的訪問,涉及到了內核態的操作。
在運行時,程序將使用syscall指令觸發從用戶態到內核態的切換,執行相應的系統調用。這里的例子演示了如何創建一個文件并寫入內容,是系統調用在文件操作中的簡單應用。
系統調用 fork與execve
進程調用 fork(創建新的進程) execve (可以靈活的調用與管理進程)。
系統調用fork是創建一個新的進程,與父進程共享資源但雙方的進程任務互不影響,判斷他們可以看返回值和uid。父進程返回子進程的ID 子進程返回0 ,而如果子進程被意外終結 可以等待父進程進行收回,如果不成功,則是僵死進程,但如果是父進程被終結,那么他的孤兒進程會被系統ID為1的進行收回。
比如:當用戶輸入的是bash -c whoami時,流程是這樣的:
用戶輸入的是bash -c whoami 而內核態執行的是bash(pid:52350) --> sys\_fork --> bash(pid:52796) --> sys\_execve --> /bin/whoami(pid:52796)
而詳細過程是這樣的:
- bash -c: 這部分命令告訴系統使用bash來執行一段特定的代碼。
- whoami: 這部分命令是
bash -c要執行的具體代碼,它將返回當前執行這段代碼的用戶的用戶名。
下面是更詳細化的執行過程:
- 用戶輸入:
bash -c whoami - Shell解析:Shell(用戶界面)解析用戶輸入,發現
bash -c是一條命令,并理解后面的whoami是bash -c要執行的代碼。 - 創建新進程(bash):Shell 使用系統調用
fork創建一個新的進程(子進程),讓子進程執行bash。
- 新進程(bash)的PID(Process ID)為52796。
- 執行新進程(bash):子進程(bash)開始執行,解釋
bash -c whoami。 - 創建新進程(whoami):在
bash -c的執行過程中,bash發現后面要執行whoami,于是使用系統調用fork再創建一個新的進程(子進程),讓這個新進程執行whoami。
- 新進程(whoami)的PID為52796。
- 執行新進程(whoami):子進程(whoami)開始執行,它是
bash -c的一個子過程,執行的是whoami命令。 - 系統調用(execve):子進程(whoami)調用
execve系統調用來執行/bin/whoami。 - 加載程序:
execve系統調用加載/bin/whoami程序到子進程(whoami)的內存空間。 - 執行程序:子進程(whoami)開始執行
/bin/whoami程序。 - 返回結果:
/bin/whoami程序執行完成后,將結果返回給子進程。 - 退出子進程(whoami):子進程(whoami)執行完畢后退出。
- 返回結果給父進程(bash):子進程(whoami)的執行結果返回給了父進程(bash)。
- 退出父進程(bash):父進程(bash)等待子進程(whoami)執行完畢后,也退出。
這個過程涉及了兩次進程的創建和兩次程序的執行,使用了fork和execve等系統調用。
最終,用戶輸入的bash -c whoami在內核態得到執行,返回了當前用戶的用戶名
liunx的進程設計到了很多東西 文件的創建 命令的調用,而進程又涉及到了權限的問題,權限分為用戶態和內核態,所以大多數進程的調用與創建會和系統調用聯合一起進行使用,從而提高系統的靈活穩定性。
# RCE漏洞的原理與成功執行的條件
## Shell 與 RCE 漏洞
Shell作為用戶與操作系統交互的接口,在RCE漏洞的產生中扮演了關鍵角色。當應用程序通過shell執行外部命令時,如果這些命令包括未經過濾或轉義的用戶輸入,就可能導致RCE漏洞。這種情況下,惡意構造的輸入被shell解釋執行,從而允許攻擊者運行任意代碼。
### Fork-Execve過程與RCE
在Unix和類Unix系統中,進程的創建和命令的執行通常通過fork()和execve()系統調用實現。當應用程序以擁有shell權限的用戶身份運行,并使用這些系統調用執行外部命令時,惡意輸入的拼接可能觸發RCE漏洞。相比之下,直接使用execve()執行命令通常更安全,因為它不會創建新的shell進程,從而減少了惡意輸入被解釋執行的可能性。
### PHP的特殊情況
在PHP中,許多執行外部命令的函數實際上是通過調用sh -c來執行命令的。這增加了RCE的風險,因為它為惡意輸入提供了一個直接的執行路徑。## 受限情況下的RCE利用
在受限的環境中,例如沙箱環境、受限的服務器配置或權限受限的賬戶,RCE的實現更具挑戰性。在這些環境中,攻擊者可能無法直接執行任意命令,但仍可以通過利用現有進程或應用程序的特定功能和參數來實現RCE。
### 示例:利用Curl進行RCE
在一個只允許執行curl命令的沙箱環境中,攻擊者可能利用curl的功能和shell的命令替換來繞過限制。例如,通過構造如下命令:
bashCopy code curl https://example.com/file.txt -o >(cat)
在這個示例中,>(cat)是一種命令替換的語法,它允許將cat命令的輸出結果作為文件路徑傳遞給-o參數。這種方法可以被用來繞過寫入文件的限制,直接在標準輸出上顯示下載的內容,從而在受限環境中實現間接的RCE。(cat) 的命令替換: >(cat) 是一種命令替換的語法。在這個上下文中,它的作用是將 cat 命令的輸出結果作為文件路徑傳遞給 -o 參數。
構造的目的是在沙箱環境中繞過將內容寫入文件的限制,而是直接輸出到標準輸出,使得你可以在沙箱環境中查看下載的內容,而無需直接寫入文件。
所以,當我們不能執?任意進程的時候(我們的輸?只是某個特定進程的輸?的時候),突破口取決的使?的進程程序本身的參數是不是能利用。
FreeBuf
ManageEngine卓豪
RacentYY
007bug
RacentYY
合天網安實驗室
Timeline Sec
LemonSec
Timeline Sec
云計算和網絡安全技術實踐
LemonSec