缓冲区溢出基础实践(二)——ROP 与 hijack GOT
3.ROP
ROP 即 Return Oritented Programming ,其主要思想是在栈缓冲区溢出的基础上,通过程序和库函数中已有的小片段(gadgets)构造一组串联的指令序列,形成攻击所需要的 shellcode 来实现攻击效果。这种方式不需要注入新的指令到漏洞程序就可以改变某些寄存器或者变量的值,从而改变程序的执行流程。
攻击者可以通过工具对目标程序搜索,寻找所需要的指令序列。通常选取的指令序列以 ret 指令结尾,这样每一个单独的指令序列结束时,都会执行 ret 指令,即会将此时栈顶的数据放入寄存器 %eip 中。攻击者在构造输入时,只需将所需的指令序列的起始地址按顺序放置在栈中,这样前一个指令序列执行完成后即会通过 ret 指令获得位于栈上的下一个指令序列的地址,从而实现一个 ret 执行链,实现一定的攻击效果。
使用 ROP 技术主要需要两个方面的准备:(1)获得所需要的指令片段的地址;(2)构造栈上的数据,使得选取的指令片段能够串联在一起;
3.1 示例程序
这里仍使用 缓冲区溢出基础实践(一)——shellcode 与 ret2libc 中使用的实例程序进行说明。使用 gcc -m32 -static -fno-stack-protector -g -o hello hello.c 生成可执行文件 hello,此时 hello 的栈数据是不可执行的,这里为了使得程序中能有较多的可供利用的指令序列,使用静态链接的方式生成可执行文件。
gcc -m32 -static -fno-stack-protector -g -o hello hello.c //生成可执行文件 hello,其栈不可执行
3.2 ROP注入过程
这里主要通过 ROP 技术实现一个简单的 execve 函数调用 execve( "/bin/sh" , NULL , NULL ),之后通过 exit(0)返回。该过程主要通过以下几个步骤实现:
a.系统调用号为0xb,即令 %eax 值为0xb
b.第一个参数为字符串"/bin/bash"的地址,即令 %ebx 值为字符串地址
c.第二和第三个参数为NULL,即 %ecx 和 %edx 的值为 NULL,之后执行 int 0x80 即可
d.系统调用号为0x1,即令 %eax 值为0x1
e.第一个参数为0,放置在 %ebx 中,之后执行 int 0x80
为了实现上述函数调用,通过在程序中已有的指令序列中寻找所需的指令序列,实现对寄存器的修改和调用过程。
(1)寻找所需的指令序列和字符串信息
这里选用 ROPgadget 实现对目标程序指令序列的选取,ROPgadget 的 GitHub 地址在这里。
a.选择对 %eax 寄存器进行修改的指令序列,这里选择第三个序列,其地址为0x080b7f86.将其视为指令序列1.可在上述的步骤 a 和 d 中使用。
b.选择对 %ebx 寄存器进行修改的指令序列,这里选择以下指令序列,可同时对 %edx、%ecx 和 %ebx 的值进行修改,其地址为 0x0806ee00.将其视为指令序列2.
另外也获取一个仅对 %ebx 修改的指令序列,其地址为 0x080481c9,将其视为指令序列3.
c.获取所需的字符串"/bin/bash"的地址,通过 ROPgadget 直接进行指令搜索时并没有找到所需的字符串,但是正如之前 ret2libc 中所利用的,环境变量 SHELL 中会包含一个"/bin/bash"字符串,获取其地址即可。这里为 0xffffd337.
d.获取系统调用 int 0x80 的地址,可获得一个地址为 0x0806ca55 的指令序列。将其视为指令序列4.
(2) 根据获得的指令序列,即可构造所需的 ROP 工作链,构造出的输入数据应该为 填充数据( 76 字节) + 指令序列1的地址( 4字节 ) + %eax的值( 4字节,0xb ) + 指令序列2的地址( 4字节 ) + %edx 的值( 4字节,NULL ) + %ecx 的值( 4字节,NULL ) + %ebx 的值( 4字节,字符串地址 ) + 指令序列4的地址( 4字节 ) + 指令序列1的地址( 4字节 ) + %eax的值( 4字节,0x1 ) + 指令序列3的地址( 4字节 ) + %ebx 的值( 4字节,0 ) + 指令序列4的地址( 4字节 )。
使用指令序列1的地址覆盖原函数的返回地址,则原函数返回时控制流跳转至指令序列1执行(返回地址已出栈),此时构造数据中"eax的值"位于栈顶,指令序列1会将其出栈并赋值给寄存器 %eax,此时指令序列2的地址位于栈顶,指令序列1执行 ret 指令时,即跳转至指令序列2执行,依次类推,构造一条完整的执行链。
构造的数据如图所示
(3) 对目标执行注入过程,结果如图所示,可以看到成功打开了一个 shell 。
在上述过程中,同样需注意调试和运行时环境变量对程序中布局变量地址的影响,但指令序列位于 .text 段,不受位于栈上的环境变量的影响,而 ROP 攻击所需的字符串参数仍需从环境变量 SHELL 中获得,故而可在 gdb 运行时设置 wrapper 程序为 env -u LINES -u COLUMNS -u _ ,而程序直接运行则通过命令 env -u _ hello程序完整路径 ,具体可参考之前实验的过程。
3.3 总结
使用 ROP 技术进行缓冲区溢出实践,充分利用了可执行程序中已有的指令序列,通过合理构造栈上数据的顺序,实现对寄存器和控制流的修改,从而实现一个完整的攻击序列,这种方法不需要额外的指令注入,对于以寄存器传递参数的 x86_64 平台同样有效,故而较之单纯的 ret2libc 技术要更有使用价值。但同样的,简单的 ROP 技术利用的指令序列的地址是通过硬编码的方式写入缓冲区的,当开启 ALSR 机制后,上述指令序列的地址在程序加载时会发生变化,此时想要使用 ROP 机制则需要进行进一步的处理。
4.hijack GOT
目前的 ELF 编译系统使用一种成为延迟绑定( lazy binding )的技术来实现对共享库中函数的调用过程。该机制主要通过两个数据结构 GOT 和 过程链接表( Procedure Linkage Table , PLT )实现。其简化的原理为 : 当目标模块存在一个外部共享库的函数调用时,其在汇编层面使用 call 指令实现调用,其作用为跳转至对应函数的 PLT 表项处执行,该表项的第一条指令为 jmp *[ 对应 GOT 项的地址 ],第一次执行函数调用时,通过 GOT 与 PLT 的合作,会将最终调用函数的地址确定下来,并存放在其对应的 GOT 表项中。当后续再发生调用时, jmp *[ 对应 GOT 项的地址 ] 指令即表示直接跳转至目标函数处执行。
4.1 示例程序
根据上述延迟绑定技术的简化原理,我们可以知道当发生过一次函数调用后,该函数的 GOT 表项存放的即为函数的真实调用地址,之后的函数调用过程会直接使用这个地址,而若能够借助缓冲区溢出手段修改该 GOT 表项的地址,即可修改程序的执行流程,达到一定的攻击目的,该方式过去通常与格式化字符串漏洞一起使用。这里仅做一个简单的 hijack GOT 示例,供实践的程序如下。
#include <stdio.h>
#include <stdlib.h>
int main( int argc , char *argv[] , char *envp[] )
{
char *pointer = NULL;
char array[];
pointer = array;
strcpy(pointer, argv[]);
printf("Array contains %s at %p\n", pointer, &pointer);
strcpy(pointer, argv[]);
printf("Array contains %s at %p\n", pointer, &pointer); return ;
}
hello.c
使用以下命令编译源程序,得到可执行文件 hello.
gcc -m32 -fno-stack-protector -g -o hello hello.c
程序的执行效果如下图所示,通过对参数的拷贝,程序输出对应的参数字符串的值,但由于并未对参数的内容和长度做检查,使得可以通过构造数据实现对格式化字符串漏洞的利用。
4.2 注入过程
作为一个简化的示例程序,这里的第一个 printf 函数调用用于对 printf 函数的延迟绑定,中间的字符串拷贝过程完成 GOT 表项的修改,而第二个 printf 函数则实现最终 hijack GOT的效果。这里以将 printf 的 GOT 表项的地址修改为 system 函数的地址为例进行说明,需要完成以下步骤:
1.使得 pointer 变量执行 printf 的 GOT 表项;
2.通过 strcpy 完成对 pointer 指向的地址位置的内容的修改为 system 函数调用地址;
3.对 printf 的函数调用实际为 system 函数的功能;
具体的注入过程如下:
(1)通过对 hello 程序的反汇编阅读可以发现,指针变量 pointer 位于 %ebp - 12 处,而缓冲区数组 array 起始地址为 %ebp - 22 ,则可以通过构造数据覆盖 pointer 变量的值,使得 pointer 变量指向特定的地址位置;
(2)对程序进行动态调试,确定所需的 printf 函数的 GOT 表项的位置和 system 函数的地址。这里同样需要对环境变量进行控制,以保证使用 gdb 获得的地址至与程序直接运行时一致。
set exe-wrapper env -u LINES -u COLUMNS -u _ //设置 wrapper 函数,之后即可下断点调试
使用 gdb 获得 printf 函数的 GOT 表项的位置为 0x804a00c,在通过第一个 strcpy 函数进行溢出操作时,需使得 pointer 指向该地址处。
同样通过 gdb 获得 system 函数的地址,后续需通过 strcpy 将原 GOT 表项处的值修改为 0xf7e3cda0.
(3)构造输入数据实现 GOT hijacking,对于 argv[1],其数据应为 填充字节( 10字节 ) + 0x804a00c( 4字节 ),这样第一次 strcpy 调用完成后,pointer 变量即指向了 printf 函数的 GOT 表项。对于 argv[2],其数据应该为 0xf7e3cda0( 4字节 ),这样第二次 strcpy 调用完成后即完成对 GOT 表项的修改。
通过如下命令进行注入。
env -u _ ./hello `perl -e 'print "A"x10'``printf "\x0c\xa0\x04\x08"` `printf "\xa0\xcd\xe3\xf7"` //由于注入过程没有使用栈上的局部变量地址,故而可以直接使用 ./hello 启动程序
注入结果如图所示:
这是由于使用 system 函数替换 printf 函数后,system 调用会试图对原来 printf 的参数 "Array contains "进行解释,而其无法找到对应的执行对象造成的。这里即可通过自地义一个 Array 程序对其进行利用。
(4)编写一个简单的 Array 程序,其源代码如下。
#include<stdlib.h>
int main()
{
system("/bin/sh");
return ;
}
Array.c
通过 gcc -o Array Array.c 编译获得一个 Array 可执行文件,并将其复制至 /bin 文件夹下。再次通过步骤(3)的方式对程序进行注入,即可获得一个 shell。
注:将 Array 程序复制至 /bin 目录需要 root 权限,这里这样做是为了演示方便,实际情况下可以选取的方案包括将当前目录 . 加入环境变量 PATH 中。
4.2 总结
通过对延时绑定技术实现中重要的数据结构 GOT 表的修改,Hijack GOT 实现了对目标函数执行流程的修改,这种方式同样可以绕过栈不可执行的保护限制,实现一定的攻击效果,其甚至可以将 SSP 机制中检测到缓冲区溢出后的处理函数 __stack_chk_fail 替换,从而使得 SSP 机制失效。但该方法在如何定位对应的 GOT 表项、如何构造输入数据实现对 GOT 表项的修改等问题上有一定的实现难度,即使在上述比较简单的例子的基础上,也需要结合较有利的格式化字符串漏洞实现 GOT 表项修改的过程。
参考资料:
(1)Return-into-libc 攻击及其防御 - IBM developerWorks
(2)基本ROP - CTF Wiki
(3)How to hijack the Global Offset Table with pointers for root shells
缓冲区溢出基础实践(二)——ROP 与 hijack GOT的更多相关文章
- 缓冲区溢出基础实践(一)——shellcode 与 ret2libc
最近结合软件安全课程上学习的理论知识和网络资料,对缓冲区溢出漏洞的简单原理和利用技巧进行了一定的了解.这里主要记录笔者通过简单的示例程序实现缓冲区溢出漏洞利用的步骤,按由简至繁的顺序,依次描述简单的 ...
- 小白日记18:kali渗透测试之缓冲区溢出实例(二)--Linux,穿越火线1.9.0
Linux系统下穿越火线-缓冲区溢出 原理:crossfire 1.9.0 版本接受入站 socket 连接时存在缓冲区溢出漏洞. 工具: 调试工具:edb: ###python在漏洞溢出方面的渗透测 ...
- 缓冲区溢出实例(二)--Linux
原理:crossfire 1.9.0 版本接受入站 socket 连接时存在缓冲区溢出漏洞. 工具: 调试工具:edb: ###python在漏洞溢出方面的渗透测试和漏洞攻击中,具有很大的优势 实 ...
- 逆向及BOF基础实践
逆向及BOF基础实践 20145316 许心远 一.缓冲区溢出基础知识 缓冲区溢出是一种非常普遍.非常危险的漏洞,在各种操作系统.应用软件中广泛存在.利用缓冲区溢出攻击,可以导致程序运行失败.系统宕机 ...
- kali渗透测试之缓冲区溢出实例-windows,POP3,SLmail
kali渗透测试之缓冲区溢出实例-windows,POP3,SLmail 相关链接:https://www.bbsmax.com/A/xl569l20Jr/ http://4hou.win/wordp ...
- 《网络对抗》 逆向及Bof基础实践
<网络对抗>-逆向及Bof基础实践 1 逆向及Bof基础实践说明 1.1 实践目标 本次实践的对象是一个名为pwn1的linux可执行文件. 该程序正常执行流程是:main调用foo函数, ...
- 缓冲区溢出利用与ShellCode编写
一.实验目的 熟悉编写shellCode的流程 掌握缓冲区溢出的利用 二.实验环境 系统环境:Windows环境 软件环境:C++ ,缓冲区溢出文件链接 三.实验原理 要实施一次有效的缓冲区溢出攻击, ...
- 网络安全(超级详细)零基础带你一步一步走进缓冲区溢出漏洞和shellcode编写!
零基础带你走进缓冲区溢出,编写shellcode. 写在前面的话:本人是以一个零基础者角度来带着大家去理解缓冲区溢出漏洞,当然如果你是开发者更好. 注:如果有转载请注明出处!创作不易.谢谢合作. 0. ...
- 2017-2018-2 20179215《网络攻防实践》seed缓冲区溢出实验
seed缓冲区溢出实验 有漏洞的程序: /* stack.c */ /* This program has a buffer overflow vulnerability. */ /* Our tas ...
随机推荐
- 自己实现一个双向绑定的Vue
我们知道双向绑定是Vue的核心之一,接下来我们自己仿照Vue实现一个基本的功能. 项目代码在GitHub上: https://github.com/zhangKunUserGit/zk-vue
- eclipse查看源码
通常eclipse中按住ctrl+左键单击,可以查看源码,很方便学习使用 如果看不到源码,需要简单的设置 设置源码 window—preference--Java—Installed JREs –jr ...
- [日常] SinaMail项目和技术能力总结
一.企邮WEBMAIL项目1.完成手机绑定二次验证,绑定手机提升账户的安全性2.登陆验证接口改造,增加一系列登陆限制,增强webmail的系统可靠性3.增加外发限制功能,及时控制用户发信行为,有利于企 ...
- HDU 1016 S-Nim ----SG求值
S-Nim Time Limit : 5000/1000ms (Java/Other) Memory Limit : 65536/32768K (Java/Other) Total Submiss ...
- set集合去重机制
- [AMPPZ2014]Petrol
关键点的最小生成树? 关键点初始化为0,跑多源最短路,然后重构整个图,用Kruskal跑最小生成树 然后跑树链剖分在线回答询问 对树上每个点维护到链顶的最大值,结合线段树可以做到\(\Theta(n ...
- PHP的一个牛逼的数组排序函数array_multisort
函数详情,具体可参考 官方手册 array_multisort 实际问题是这样的,有这么一组数据: $arr_times = array( array('2018-04-12 04:25:00', 3 ...
- DOM增删操作(创建删除表格)
<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>& ...
- 【java错误】错误: 编码GBK的不可映射字符
java源代码 今天在写java是出现一个编码错误,这里先将书上的java源代码贴出来. import java.io.Console; public class ConsoleTest { //用j ...
- css之图像替换
time: 2016-03-30 20:00 这个月有点忙,学业的事工作的事私人的事有点烦,但是不能停止学习更不能忘记写博客! 最近看了<精通css>这本书,挑了一个点纪录一下. 一.含义 ...