| 第一栈帧 +-+ | v ------------------------------------------------ | fake_ebp2 | f2 | leaveret | f2_arg1 | f2_argv2 ... -----|------------------------------------------ | 第二栈帧 +-- ... fake_ebp0 是第一栈帧的地址,fake_ebp1 是第二栈帧的地址,依次类推。 现在,一些想法将被呈现在下面 1)有缺陷的函数的结尾(leave;ret)将fake_ebp0的地址赋予栈基指针,并返回到leaveret。 2)结尾的两个指令(leave;ret)放fake_ebp1 地址到栈基指针,并返回到f1的地址。 3)f1执行后返回leaveret。 重复2)3)步骤,用 f1,f2,。。。fn代替。
文[4]中的返回到函数结尾的技术没有过多的用途,因而作者提议如下,堆栈应该被构建成让exploit代码返回到F函数前面的库函数的后面,不要返回到F函数自身,这中技术和前面很类似,然而我们很快就会面对这种情形,当F函数仅通过过程连接表(PLT),这种情况,就不可能返回到函数F的地址加某个地址偏移。而只会返回到自身的地址。
注意,为了使用这个技术,必须知道精确的定位伪造的栈帧,因为fake_ebp必须按照规则设置。如果所有的栈帧定位在buffer fill-up的后面,那么必须知道在溢出后面堆栈指针的确定数值。然而,如果我们知道怎样控制一个伪造的栈帧定位在一个已经知道的内存区域(静态变量更适合),就没有必要猜测堆栈的指针数值了。
有可能攻击这种用 -fomit-frame-pointer这类的便宜选项的程序,这种情况,我们不需要找程序中的leave&ret代码,但通常它能够在一些常规的联结过程中发现。因此我们要改变这些零的块。
------------------------------------------------------- | buffer fill-up(*) | leaveret | fake_ebp0 | leaveret | ------------------------------------------------------- ^ | |-- int32 会被有缺陷的函数的返回地址保存覆盖 两个leaverets是必要的,由于有缺陷的函数当返回的时候不会设置堆栈指针。由于"帧伪造"教"堆栈指针增长"有优势。一些时候是很必要的通过这种方法达到攻击。
----[ 3.4 - 嵌入零字节 还有一个问题:传给一个函数的参数中包含有零字节。当多个函数有效调用时,有一个简单的解决方法:先调用的函数通过嵌入零字节到下一个要调用的函数的参数的地址。
Strcpy是我们经常用到的一个函数,它的第二个参数指向一个程序映像中固定空间的零字节,第一个参数指向的是将无效的地址,我们在每一次函数调用的时候指定无效的零字节,在需要的int32位置可以放入零,这方法需要适当的后效的位置放置。比如,sprintf(some_writable_addr,"%n%n%n%n",ptr1, ptr2, ptr3, ptr4);可以在some_writable_addr地址使其无效,在ptr1, ptr2, ptr3, ptr4让int32放置于这些地址,同样无效。还有很多的函数可以达到这种方式的目的,比如scanf,详见文[5]。
注意,这种方法解决的一个深层次的问题。如果所有的库函数被通过含有零的地址进行地址映射处理,比如sun公司对Solar设计的堆栈不可执行补丁,我们将不能够直接的返回到一个库函数中,因为我们不能够传零字节到溢出的堆栈中。但是,如果被攻击的程序中调用过Strcpy(或者sprintf,见文[3]),并且有适当的(PLT)过程连接表入口,那么我们可以利用,开头调用函数包括strcpy这样的让函数的参数字节无效,但不是函数自身的地址的字节。在这些参数的后面,我们可以再从库函数中调用任意的函数了。
----[ 3.5 - 概要 如上两种的现有的方法都是比较类似的,从调用的函数中返回,而不是直接返回到后一个函数的地址。但在一些函数的结尾,通过调整堆栈指针或者是栈帧指针,将控制转移到同一链中的下一个函数中。
在上面两种方法中,我们都试图在可执行格式的文件的文件体中寻找一个适当的结尾。通常,最好是利用库函数的结尾。然而,一些时候,这种库函数映像的结尾是不能够直接的返回,比如库函数被通过零字节进行过地址映射处理的情况,已经被提及了。我们将面对另一种情况,可执行文件的映像不是一个固定的位置,而一定会在一个固定的位置进行随机的映射,发现,一些情况下,linux系统的这个地址是0x08048000,这样,我们可以利用这里地址顺利的返回到目标库函数。
----[ 3.6 - 简单代码 ex-move.c 和ex-frames.c是对vuln.c程序实现的exploit代码。这exploit代码中联结了一些象strcpy和mmap的函数调用。在4.2节中有更多的解释。总之,任何人可以通过这些给出的exploit代码作为模块,构建库函数返回技术的exploit实现代码。
--[ 4 - PaX的特征 ----[ 4.1 - PaX基础 如果你没有听说过PaX linux内核补丁,建议你访问他们的项目主页[7]。下面是一些从PaX文档中的引用。 "该文档讨论在IA-32处理器上实现不可执行的可能性。(比如用户模式下的页面是可读,写的,但是不能够代码执行)不过该处理器还没有提供这项功能,这是很有价值的工作。" "[...]为防止堆栈缓冲区溢出的攻击,有一些观点和方式,一种观点是,在数据段中有限的排除代码的情况下,可以通过页面的不可执行的方法达到遏止攻击[...]" "[...]在内核模式下,通过DTLB和ITLB入口写代码将导致错误[...]可以创建数据段只可读,写,而不能都执行的状态,这是保证不被溢出攻击的根本"
总的而说,缓冲区溢出攻击通常试图在执行代码中使用一些数据达到攻击的过程。PaX的主要功能是不允许在任何数据段具有可执行---这种特点让典型的溢出实现代码失去效果。
--[ 4.2 - PaX和返回库函数的实现 最初,数据段不可执行是 PaX的唯一特征。呵呵,你已经猜到了,它对return-into-lib的exploit攻击还是远远不够的。这种代码实现通过定位在库函数中和二进制的文件完美的结合。在本文的3章中进行了描述。实现代码中调用多个库函数,这种技术在高级exploit代码实现中有其优势。
下面的代码可以在 PaX 保护的系统中成功的执行! char shellcode[] = "arbitrary code here"; mmap(0xaa011000, some_length, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS|MAP_SHARED, -1, some_offset); strcpy(0xaa011000+1, shellcode); return into 0xaa011000+1; 简单的解释:mmap函数调用时在地址0xaa011000(*start参数)分配内存单元。它并不和任何的目标文件有关系。但得感谢MAP_ANONYMOUS标志,和文件描述符(int fd)等于-1。当代码定位在0xaa011000时,在Pax会被执行(由于函数mmap函数中的参数PROT_EXEC(保护模式内存页面可执行)被设置了)。显然,任何代码如果代替上面代码中的"shellcode"都将被执行。
好,我们来看实现代码了。vuln.c 是一个被攻击的程序,有明显的溢出问题,开始编译它:) $ gcc -o vuln-omit -fomit-frame-pointer vuln.c $ gcc -o vuln vuln.c -fomit-frame-pointer该编译参数通常不保存帧指针在函数寄存器中,避免指令保存,建立,恢复帧指针。其中ex-move.c是攻击vuln-omit的exploit代码;ex-frames.c是攻击vuln的exploit代码。 exploit实现代码依次调用strcpy()和mmap(),请参考README.code理解更多的指令。(详见《高级返回库函数exploit代码实现(下)》)
如果你要在最新的Pax保护的系统中演示以上代码,务必需要禁止随机映射的功能,如下: $ chpax -r vuln; chpax -r vuln-omit
----[ 4.3 - PaX与mmap随机功能 为了抗击返回库函数实现的技术,PaX增加了一个可爱的功能。如果在内核配置中设置一些适当的功能,第一次装载的库函数将随机的映射地址,而下一个又在前一个的基础上进行随机处理。相似的应用在堆栈中,第一个库函数随机映射的地址以这种方式0x40000000+random*4k。堆栈顶部等于0xc0000000-random*16。可以看出,所谓的随机不过是一个16位的无符号的整数。通过函数get_random_bytes()获得随机调用且进行过加密的强壮数值。
我们可以通过命令"ldd -r some_binary"来查看调用的库函数并了解它的行为。并还可通过"cat /proc/$$/maps" ($$代表程序的进程号)了解Pax的随机过程。 呵呵,注意看下面,每次的运行ash所调用的库函数的地址都是不同的:) nergal@behemoth 8 > ash (第一次) $ cat /proc/$$/maps 08048000-08058000 r-xp 00000000 03:45 77590 /bin/ash 08058000-08059000 rw-p 0000f000 03:45 77590 /bin/ash 08059000-0805c000 rw-p 00000000 00:00 0 4b150000-4b166000 r-xp 00000000 03:45 107760 /lib/ld-2.1.92.so 4b166000-4b167000 rw-p 00015000 03:45 107760 /lib/ld-2.1.92.so 4b167000-4b168000 rw-p 00000000 00:00 0 4b16e000-4b289000 r-xp 00000000 03:45 107767 /lib/libc-2.1.92.so 4b289000-4b28f000 rw-p 0011a000 03:45 107767 /lib/libc-2.1.92.so 4b28f000-4b293000 rw-p 00000000 00:00 0 bff78000-bff7b000 rw-p ffffe000 00:00 0 $ exit nergal@behemoth 9 > ash (第二次) $ cat /proc/$$/maps 08048000-08058000 r-xp 00000000 03:45 77590 /bin/ash 08058000-08059000 rw-p 0000f000 03:45 77590 /bin/ash 08059000-0805c000 rw-p 00000000 00:00 0 48b07000-48b1d000 r-xp 00000000 03:45 107760 /lib/ld-2.1.92.so(地址不同前面) 48b1d000-48b1e000 rw-p 00015000 03:45 107760 /lib/ld-2.1.92.so(下同) 48b1e000-48b1f000 rw-p 00000000 00:00 0 48b25000-48c40000 r-xp 00000000 03:45 107767 /lib/libc-2.1.92.so 48c40000-48c46000 rw-p 0011a000 03:45 107767 /lib/libc-2.1.92.so 48c46000-48c4a000 rw-p 00000000 00:00 0 bff76000-bff79000 rw-p ffffe000 00:00 0
可见,增加的CONFIG_PAX_RANDMMAP特征,将不容易返回到库函数了,每次运行调用的库函数的地址都不同。
同时,Pax还不是很完善,有下面一些明显的瑕疵,不过有些可以解决完善。
1)如果/proc/pid_of_attacked_process/maps 具有可读的情况,那么本地的攻击代码中的库函数和被映射的堆栈地址可以从maps被获得。如果被攻击的程序开始运行后,攻击程序将有通过运行的被攻击程序覆盖堆栈的缓冲的准备时间,一个攻击者可以利用可用的信息来构建堆栈中的溢出代码数据。比如,如果溢出代码中用到的数据来自于程序中调用函数的某个参数或者是该程序的环境,那么攻击者会无所作为,但是,如果溢出代码是来自于一些I/O(输入/输出)的操作(比如,socket或是read files),那么攻击这将成功。 解决方法:禁止访问/proc 目录。象其他的一些安全补丁一样:)
2)攻击者可以"暴力"猜测出mmap后的库函数的地址。通常(将6。1节的结尾)有足够的条件能够"暴力"猜测到库函数的基址地址,通过大约数万次的尝试,攻击者有一半的机会猜对。当然,每一次的失败都会被系统日志记录,但是,对安全而言并不可靠,如果日志被篡改:) 解决方法:配置segvguard(见文[8]),它是一个守护精灵,每一次的一个进程通过信号SIGSEGV或类似的信号进入内核将被通报,Segvguard将临时禁止该程序继续执行(可以防止暴力猜测攻击进行),并且Segvguard还有一些有趣的功能,在没有Pax 的情况下,它值得一试。
3)库函数的信息和堆栈的地址,如果利用格式化漏洞,将被泄露。比如,wuftpd的缺陷,攻击者可以通过site exec [eat stack]%x.%x.%x...命令来探测堆栈的情况。隐藏在堆栈中变量的指针将自动的在栈基呈现出来。由于对一个目标的库函数而言,动态链接和库函数的启动程序里保留有堆栈的一些指针和一些函数返回地址,利用这些,攻击者可以推论出库函数的基址地址。
4)一些特殊的情况,在被攻击的程序中,攻击者可以找到一类函数,它们的位置是固定的,而且不能被mmap函 上一页 [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] ... 下一页 >> [办公软件]在sybase中插入图片、PDF、文本文件 [办公软件]安装Sybase ASE [办公软件]linux指令大全(完整篇) [办公软件]Linux新手入门常用命令大全 [办公软件]在RedHat Linux 9里安装gaim0.80 [办公软件]浅谈Linux 下Java 1.5 汉字方块问题解决方法 [办公软件]Linux程序员必读:中文化与GB18030标准 [办公软件]linux指令大全 [办公软件]制作Linux启动盘的四种方法 [办公软件]Linux文件系统的反删除方法
|