使用 C 语言编写 Windows下的 Shellcode

Executing Position Independent Shellcode from Object Files in Memory | Brute Ratel C4,翻译

简介

多数情况下,shellcode injection 用来获取 initial access 而不是来完成后渗透工作,其主要原因是使用 Reflective DLLs 和 C# 程序编写后渗透功能比使用汇编容易许多。但如果使用 C 编写 shellcode,可以使得PE 结构不容易被检测,并且内容会 DLL 和 C# 程序小很多。

使用 C 编写 shellcode 时需要注意:

  • 要把所有内容包含在 .text 节中,所以不能使用全局变量、导入符号或者静态的字符串,导入符号需要自己处理,需要字符串的使用 byte array 来代替
  • 默认情况下,链接器将 PE 的入口点函数链接到 mainCRTStartup 来执行一些初始化操作(加载必要的 DLL、解析命令行参数等),我们不想加载 msvcrt.dll,所以需要修改入口点函数
  • 如果编写用于 x64 的 shellcode,需要将栈按 16 字节对齐,x86 shellcode 按 4 字节对齐

adjuststack.asm

首先编写一个 Stub,来保证 shellcode 始终保持栈对齐,并作为入口点函数来调用我们的 C 函数(getprivs)。adjuststack.asm:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

汇编为目标文件:

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

addresshunter.h

shellcode 功能是获取当前用户的权限并且打印到屏幕上,因此我们需要几个导出符号:kernel32.dll 中的 LoadLibraryA、CloseHandle、GetCurrentProcess,advapi32.dll 中的 OpenProcessToken、GetTokenInformation、LookupPrivilegeNameW,msvcrt.dll 中的 calloc 和 wprintf。

我们会改变入口点,避免了静态链接 msvcrt.dll,因此我们需要手动解析以上三个导出项。先计算 kernel32.dll 的 ror13 hash,获得 LoadLibraryA 的地址,然后使用 LoadLibraryA 加载另外两个库,获取以上几个函数指针。我们使用自己编写的 GetProcAddress,因为它通常会被 AVs 和 EDRs hook。

使用以下代码从 DLL 中获取函数的地址(addresshunter.h),重写和添加了一些注释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
#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;

// PEB at gs:[0x60] and __readgsqword is compiler intrinsic,
// Equivalent to mov rax,gs:[0x60]
kernel32dll = __readgsqword(0x60);

// pLdr->InMemoryOrderModuleList is a doubly linked list
kernel32dll = (ULONG_PTR)((_PPEB)kernel32dll)->pLdr;
val1 = (ULONG_PTR)((PPEB_LDR_DATA)kernel32dll)->InMemoryOrderModuleList.Flink;

// Use deref to traverse the doubly linked list
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 == '\0') {
return c1 - c2;
}
} while (c1 == c2);
return c1 - c2;
}

// lpProcName could be either a pointer to name or ordinal of a function
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);

// for ordinal
if (((UINT64)lpProcName & 0xFFFF0000) == 0x00000000) {
exportedAddressTable += ((IMAGE_ORDINAL((UINT64)lpProcName) - exportDirectory->Base) * sizeof(DWORD));
symbolAddress = (UINT64)(dllAddress + DEREF_32(exportedAddressTable));
}
// for pointer to name
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;
}

getprivs.c

接下来编写 getprivs 。观察上面的 C 代码,我们没有使用任何字符串或者全局函数,但有时需要使用格式化字符串或者打印输出,此时我们可以使用 char 或者 wchar 字节数组,像下面这样可以将字符串存放到 .text 节而不是 .bss 节。

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

还需要一些导出函数的 typedef。

最终,获取用户权限的函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#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'A', L'd', L'j', L'u', L's', L't', L'e', L'd', L'\n', 0 };
WCHAR priv_disabled[] = { L'[', L'+', L']', L' ', L'%', L'-', L'5', L'0', L'l', L's', L' ', L'D', L'i', L's', L'a', L'b', L'l', L'e', L'd', 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);
}
}

linker.ld

另外需要将入口点设置到 alignstack 函数,然后 alignstack 会调用 getprivs,所以不需要编写 main 函数。我们需要确保链接器按顺序执行。链接器脚本 linker.ld :

1
2
3
4
5
6
7
8
9
ENTRY(alignstack)
SECTIONS
{
.text :
{
*(.text.alignstack)
*(.text.getprivs)
}
}

Makefile

使用以下 makefile 编译以上所有文件

1
2
3
4
   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
    • 使用提供的 linker script 确定函数顺序
  • 编译以上所有目标文件并去除调试符号和注释

编译完成后,使用 objdump 查看。(与原文不同,这里生成的 PE 包含 .idata section,但是没有包含导入的函数):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ make
$ x86_64-w64-mingw32-objdump -h getprivs.exe

getprivs.exe: file format pei-x86-64

Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000780 0000000140001000 0000000140001000 00000400 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .idata 00000014 0000000140002000 0000000140002000 00000c00 2**2
CONTENTS, ALLOC, LOAD, DATA

$ readpe -i getprivs.exe
Imported functions

由于并没有使用导入的函数,使用 PEditor 删除了 .idata 节,应该没影响。

保存 shellcode 并检查:

1
2
$ for i in $(objdump -d getprivs.exe | grep "^ "|cut -f2); do echo -n '\x'$i >> getprivs.bin; done;
$ x86_64-w64-mingw32-objdump -b binary -Mintel,x86-64 -D getprivs.bin -m i386|more

执行 shellcode(runshellcode.asm):

1
2
3
4
5
6
7
8
; 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"

到 Windows 上执行成功:

1
2
3
4
5
6
7
> runshellcode.exe                                                                                 
[+] SeShutdownPrivilege Disabled
[+] SeChangeNotifyPrivilege Enabled (Default)
[+] SeUndockPrivilege Disabled
[+] SeIncreaseWorkingSetPrivilege Disabled
[+] SeTimeZonePrivilege Disabled
[+] Restricted

参考

Executing Position Independent Shellcode from Object Files in Memory | Brute Ratel C4

PE 结构

自制 PE 结构图

PE

vulhub-debug

记录如何调试 vulhub 的漏洞环境

阅读更多

PWN 入门

星盟安全的 PWN 学习

阅读更多

RMI

Java RMI(Remote Method Invocation)

阅读更多

SSRF

SSRF(Server Side Request Forgery),服务端请求伪造

阅读更多