dll 源码_【技术分享】 | 一个JAVA内存马的源码分析
前言
偶然接觸到了這樣一個(gè)JAVA內(nèi)存馬,其作者也是冰蝎的作者,項(xiàng)目地址:
https://github.com/rebeyond/memShell
正好最近在接觸JAVA,借此機(jī)會學(xué)習(xí)下大佬的代碼,對自己的編程思路也有了一定的提升。當(dāng)然筆者只是一個(gè)腳本小子,對代碼接觸不深,如果文中出現(xiàn)理解不當(dāng)或是錯(cuò)誤的情況,還望各位大佬不吝賜教:)
背景知識
Java Instrumentation
JAVA在SE5版本引入了Java Instrumentation,其包含在java.lang.instrument中。通過Instrumentation,我們可以構(gòu)建獨(dú)立于應(yīng)用程序的代理端,而通過這個(gè)代理我們可以監(jiān)控JVM狀態(tài)以及修改類定義。而在SE6版本中,我們能夠?qū)崿F(xiàn)注入代碼到運(yùn)行時(shí)的JVM中。
Java Agent
前面提到的代理端也就是Java Agent,Java Agent是依附于JAVA應(yīng)用程序并能對其字節(jié)碼做修改的一項(xiàng)技術(shù),不能獨(dú)立運(yùn)行。加載運(yùn)行方式有兩種:premain模式和attach模式,前者是在程序運(yùn)行前加載,后者是在程序運(yùn)行后加載,本文分析的這個(gè)木馬是使用的后者。
項(xiàng)目作者在這篇文章中有個(gè)原理demo,感興趣的可以看看。
分析
分析按由外及里、由簡入難的順序進(jìn)行,即:用戶可見功能模塊、注入模塊、代理模塊、持久化模塊。
用戶可見模塊分析
從項(xiàng)目README文件里可以看到這款木馬有以下功能:
l? 歡迎頁
l? 命令執(zhí)行
l? 反彈Shell
l? 遠(yuǎn)程文件下載
l? 文件操作
l? 本地文件下載
l? 文件上傳
l? 代理
l? 菜刀連接
歡迎頁
當(dāng)我們獲取目標(biāo)服務(wù)器權(quán)限時(shí),將木馬上傳至目標(biāo)服務(wù)器,執(zhí)行java -jar inject.jar password,即可進(jìn)行進(jìn)程注入獲取一個(gè)隱藏進(jìn)程會話。
[+]OK.i find a jvm. [+]memeShell is injected.此時(shí)我們?nèi)g覽器訪問目標(biāo)服務(wù)器即可調(diào)用目標(biāo)模塊。
需要注意的是,這個(gè)木馬和常規(guī)木馬不同。常規(guī)木馬需要去訪問目標(biāo)木馬文件來執(zhí)行命令,而這款木馬訪問任意URL都可以調(diào)用木馬執(zhí)行其模塊。其原理是此木馬向org.apache.catalina.core.ApplicationFilterChain類的internalDoFilter方法注入了自定義代碼,這個(gè)JAVA類在HTTP請求調(diào)用棧的上方,可以相應(yīng)我們的任意Request請求,因此我們可以用GET請求也可以用POST請求。
internalDoFilter方法的原型如下:
換個(gè)角度來講,這個(gè)類就是一個(gè)過濾器,而過濾器可以在客戶端的請求訪問后端資源之前,攔截這些請求。也可在服務(wù)器的響應(yīng)發(fā)送回客戶端之前,處理這些響應(yīng)。也就是說用戶的每一個(gè)Request請求都會經(jīng)過過濾器,無論用戶訪問的資源是否存在,如何處理這些請求也是過濾器的核心所在。
命令執(zhí)行
命令執(zhí)行模塊的核心就是調(diào)用Runtime.getRuntime().exec(cmd)方法來執(zhí)行任意命令并獲取返回結(jié)果。
例如:
反彈Shell
反彈Shell主要依靠的是Socket通信,首先判斷操作系統(tǒng)類型,再進(jìn)行Socket連接以達(dá)到反彈Shell的目的。
例如:
遠(yuǎn)程文件下載
遠(yuǎn)程文件下載支持HTTPS,下面的代碼片段為了排版美觀我去掉了SSL認(rèn)證代碼。
文件操作
文件操作包含列文件、刪除文件、查看文件、刪除文件夾,都是一些很基礎(chǔ)的文件操作代碼,因此我們以查看文件為例。
本地文件下載
文件上傳
有普通上傳方式,有Base64編碼上傳方式。
代理
木馬里內(nèi)嵌了一個(gè)reGeorg代理,因此我們可以直接使用這個(gè)木馬實(shí)現(xiàn)代理轉(zhuǎn)發(fā),進(jìn)一步滲透內(nèi)網(wǎng)。
核心源碼就是根據(jù)reGeorg改的,網(wǎng)上也有分析文章,此處不再多贅述。
菜刀連接
菜刀模塊的源碼也是根據(jù)這個(gè)JSP菜刀源碼改的,但是個(gè)人覺得挺有意思——方法命名全是AA、BB、CC這種,不明白原作者是為了混淆還是只是單純的惡作劇。
當(dāng)然,以上的這些用戶可見功能模塊都建立在一個(gè)判斷邏輯里,也就是需要正確輸入我們所指定的密碼。
if (pass_the_world!=null&&pass_the_world.equals(net.rebeyond.memshell.Agent.password))這些用戶可見模塊除了代理模塊和菜刀模塊都是一些常用的功能代碼,簡單易懂。緊接著我們深入分析其他模塊。
注入模塊分析
注入模塊主要是用來遍歷目標(biāo)機(jī)器上的JVM實(shí)例并進(jìn)行代碼注入。前面提到我們有兩種方式進(jìn)行注入,premain和attach,這個(gè)木馬里用的attach注入方式。
注入模塊在運(yùn)行的時(shí)候,會動(dòng)態(tài)加載一個(gè)代理,也就是我們的代理模塊。換句話說,代理模塊會被注入模塊注入到tomcat進(jìn)程中。
代理模塊分析
在代理端被注入到JVM后,會自動(dòng)運(yùn)行agentmain方法。agentmain方法在獲取用戶輸入的參數(shù)后,會遍歷獲取當(dāng)前所有類,如果匹配到我們要注入的目標(biāo)類,則查看當(dāng)前的JVM配置是否支持類的重新定義,代碼如下所示。
為避免默認(rèn)端口號被更改初始化失敗,木馬會先獲取當(dāng)前工程的端口號,然后對本地的tomcat發(fā)起一起請求進(jìn)行初始化。進(jìn)行初始化的原因原作者也解釋過,因?yàn)槲覀冊谶\(yùn)行木馬后會將木馬文件刪除,因此需要在刪除之前沒有將木馬寫入內(nèi)存。寫入內(nèi)存的方式有兩種:依次加載需要的類、進(jìn)行一次模擬訪問,這款木馬選擇的后者。如下代碼所示,會訪問一次本地的tomcat服務(wù),以達(dá)到將木馬寫入內(nèi)存的目的。
在注入進(jìn)程后,需要?jiǎng)h除自身,但是我注意到刪除的代碼還比較多,原因是操作系統(tǒng)的不同,刪除方式也不同,例如Windows下不能直接刪除一個(gè)占用中的文件。因此首先需要判斷操作系統(tǒng)類型,如果是Linux直接刪除即可。
如果是Windows,需要利用unlockFile這個(gè)方法來進(jìn)行刪除,unlockFile方法里使用了一個(gè)二進(jìn)制文件——forceDelete.exe。例如當(dāng)我們要?jiǎng)h除代理端時(shí),需要先用以下代碼獲取當(dāng)前JVM進(jìn)程PID。
public static String getCurrentPid() { RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean(); return runtimeMXBean.getName().split("@")[0]; }然后利用命令執(zhí)行的方式使用這個(gè)二進(jìn)制文件強(qiáng)制刪除占用的文件,并且將自身刪除。
這個(gè)強(qiáng)制刪除功能的二進(jìn)制文件我們利用IDA進(jìn)行簡單分析,利用IDA打開后,手動(dòng)調(diào)試恢復(fù)函數(shù)即可看到整個(gè)二進(jìn)制文件代碼邏輯。我這里就沒有去恢復(fù)變量名,各位大佬湊合著看。
首先有一個(gè)判斷邏輯:
if ( !sub_411440 () || !sub_411590() ) ??????ExitProcess(0);??跟進(jìn)sub_411440()函數(shù),根據(jù)其代碼邏輯我們發(fā)現(xiàn)其實(shí)就是Change_access()函數(shù),其代碼邏輯如下:
??BOOL?sub_411440()????{????????HANDLE?v0;?//?eax????????struct?_TOKEN_PRIVILEGES?NewState;?//?[esp+D0h]?[ebp-24h]????????HANDLE?TokenHandle;?//?[esp+E8h]?[ebp-Ch]????????????NewState.PrivilegeCount?=?1;????????v0?=?GetCurrentProcess();????????if?(?!OpenProcessToken(v0,?0x28u,?&TokenHandle)?)????????????return?0;????????LookupPrivilegeValueW(0,?L"SeDebugPrivilege",?(PLUID)NewState.Privileges);?? NewState.Privileges[0].Attributes = 2; return AdjustTokenPrivileges(TokenHandle, 0, &NewState, 0x10u, 0, 0) != 0; }作用是獲取當(dāng)前進(jìn)程信息并修改其權(quán)限。
再跟進(jìn)sub_411590()函數(shù),根據(jù)其代碼邏輯推斷是Get_Functions_address()函數(shù)
??BOOL?sub_411590()????{?? v0 = GetModuleHandleW(L"ntdll.dll"); dword_41713C = (int)GetProcAddress(v0, "ZwSuspendProcess"); v1 = GetModuleHandleW(L"ntdll.dll"); QueryInformationFille = (int (__stdcall *)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD))GetProcAddress(v1, "ZwQueryInformationFile"); v2 = GetModuleHandleW(L"ntdll.dll"); QuerySystemInformation = (int (__stdcall *)(_DWORD, _DWORD, _DWORD, _DWORD))GetProcAddress(v2, "ZwQuerySystemInformation"); v3 = GetModuleHandleW(L"ntdll.dll"); QueryObject = (int (__stdcall *)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD))GetProcAddress(v3, "ZwQueryObject"); v4 = GetModuleHandleW(L"ntdll.dll"); dword_417138 = (int)GetProcAddress(v4, "ZwResumeProcess"); ??????v5?=?GetModuleHandleW(L"ntdll.dll");??14.??????QueryInformationPrecess?=?(int?(__stdcall?*)(_DWORD,?_DWORD,?_DWORD,?_DWORD,?_DWORD))GetProcAddress(v5,?"ZwQueryInformationProcess");?? return dword_41713C && QuerySystemInformation && QueryObject && dword_417138 && QueryInformationPrecess; ??}??其作用是加載ntdll.dll模塊并從中獲取ZwSuspendProcess、ZwQueryInformationFile、ZwQuerySystemInformation、ZwQueryObject、ZwResumeProcess、ZwQueryInformationProcess的函數(shù)地址。
還有一個(gè)sub_411DD0()函數(shù),根據(jù)其代碼邏輯推斷是Find_SubStr()函數(shù)。因此此時(shí)整個(gè)二進(jìn)制文件代碼邏輯就清晰了。其核心代碼如下:
??if?(?QueryInformationFille(TargetHandle,&v13,FileInformation,528,9)?>=?0?)2.??{?? if ( Find_SubStr(FileInformation + 2, L"agent.jar") ) { v4 = GetCurrentProcess(); if ( DuplicateHandle(hSourceProcessHandle, (HANDLE)v8, v4, &TargetHandle, 0, 0, 1u) ) { CloseHandle(TargetHandle); ExitProcess(0); } } ??}??我們梳理下整個(gè)二進(jìn)制文件的運(yùn)行邏輯:首先打開agent.jar進(jìn)程,遍歷該進(jìn)程的所有句柄信息,通過DuplicateHandle()函數(shù)復(fù)制句柄到本地進(jìn)程,關(guān)閉文件句柄,此時(shí)就能刪除占用中的文件了。
本來DuplicateHandle()函數(shù)是用來創(chuàng)建新句柄的,但是我們可以利用這個(gè)特性來刪除被占用的文件,巧妙的實(shí)現(xiàn)刪除文件的功能。
持久化模塊分析
持久化模塊主要是用于tomcat服務(wù)重啟后也能繼續(xù)使用這款木馬,也就是說,只要目標(biāo)機(jī)器不重啟,tomcat服務(wù)運(yùn)行起來我們無需進(jìn)行二次注入也能獲取權(quán)限,其核心代碼如下。
主要的核心原理在于addShutdownHook鉤子,JVM關(guān)閉的時(shí)候,會執(zhí)行系統(tǒng)中已經(jīng)設(shè)置的所有通過方法addShutdownHook添加的鉤子,當(dāng)系統(tǒng)執(zhí)行完這些鉤子后,JVM才會關(guān)閉。所以這些鉤子可以在JVM關(guān)閉的時(shí)候進(jìn)行內(nèi)存清理、對象銷毀等操作。當(dāng)然這些只是一些“正規(guī)的操作”,我們可以設(shè)置一些“非法操作”,在JVM關(guān)閉的時(shí)候?qū)⑽覀円呀?jīng)注入內(nèi)存的代碼寫入到文件,然后再調(diào)用startInject方法,startInject方法源碼如下:
再次調(diào)用startInject方法后就達(dá)到了持久化的目的。
總結(jié)
我們再梳理下整個(gè)木馬工作流程:
1. ? 獲取到目標(biāo)服務(wù)器權(quán)限,將Inject.jar和Agent.jar上傳至服務(wù)器。
2. ? 執(zhí)行java -jar Inject.jar password,開始注入Tomcat進(jìn)程。
3. ? 注入模塊尋找目標(biāo)類。
4. ? 將代碼注入到Tomcat進(jìn)程。
5. ? 成功注入后刪除自身。
6. ? 遇到Tomcat進(jìn)程重啟,將內(nèi)存代碼寫入臨時(shí)文件。
7. ? 再次注入Tomcat進(jìn)程達(dá)到持久化目的。
注入的核心關(guān)鍵在于Servlet過濾器的internalDoFilter方法,因?yàn)樗械挠脩粽埱蠖紩ㄟ^這個(gè)方法。
未來工作
JSP服務(wù)器的內(nèi)存注入,使得JAVA內(nèi)存馬的通用性得到提高。原作者也提到使得這個(gè)內(nèi)存馬通用性提高的關(guān)鍵就在于要尋找到“關(guān)鍵類”,Tomcat里使用的是Servlet的過濾器關(guān)鍵類,在其他JAVA容器我們也需要找到這樣的一個(gè)關(guān)鍵類,這也是未來工作的重點(diǎn)。
安洵信息技術(shù)有限公司
www.i-soon.net
以實(shí)力陪伴客戶成長??使客戶更加強(qiáng)大
400-066-5915
上海丨四川丨江蘇 | 云南
總結(jié)
以上是生活随笔為你收集整理的dll 源码_【技术分享】 | 一个JAVA内存马的源码分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 焦作治疗子宫内膜异位症最好的医院推荐
- 下一篇: hls fifo_HLS优化方法DATA