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 示例,供实践的程序如下。

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 int main( int argc , char *argv[] , char *envp[] )
 4 {
 5         char *pointer = NULL;
 6         char array[10];
 7         pointer = array;
 8         strcpy(pointer, argv[1]);
 9         printf("Array contains %s at %p\n", pointer, &pointer);
10         strcpy(pointer, argv[2]);
11         printf("Array contains %s at %p\n", pointer, &pointer);
12
13         return 0;
14 }     

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 程序,其源代码如下。  

1 #include<stdlib.h>
2 int main()
3 {
4         system("/bin/sh");
5         return 0;
6 }     

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

转载于:https://www.cnblogs.com/yhjoker/p/9165178.html

缓冲区溢出基础实践(二)——ROP 与 hijack GOT相关推荐

  1. 缓冲区溢出基础与实践

    缓冲区溢出 缓冲区溢出是指当计算机向缓冲区内填充数据时超过了缓冲区本身的容量,溢出的数据覆盖在合法数据上.理想的情况是:程序检查数据长度并不允许输入超过缓冲区长度的字符,但是绝大多数程序都会假设数据长 ...

  2. 小白日记18:kali渗透测试之缓冲区溢出实例(二)--Linux,穿越火线1.9.0

    Linux系统下穿越火线-缓冲区溢出 原理:crossfire 1.9.0 版本接受入站 socket 连接时存在缓冲区溢出漏洞. 工具: 调试工具:edb: ###python在漏洞溢出方面的渗透测 ...

  3. java grpc 客户端处理 go 服务端多返回值_grpc基础实践(二)

    在此篇中我们将简要介绍关于grpc对java客户端的实现. 在开始开发前,我们需要先导入 io.grpc grpc-netty 1.11.0io.grpc grpc-protobuf 1.11.0io ...

  4. C语言实现缓冲区溢出实例

    参考书目:0day安全:软件漏洞分析技术  相关工具使用:OD,IDA Pro,VC++6.0,UltraEdit 最近需要做课堂演习,就选了缓冲区溢出的实践.主要参考0day安全这本书,一面一句话很 ...

  5. 缓冲区溢出攻击原理分析

    <缓冲区溢出攻击实践>以实践者角度介绍了初级缓冲区溢出攻击方法,本文从原理上对该方法做原理性介绍. 函数帧结构 现在高级语言C(或者C++),在函数开头的几指令要建立好函数帧结构,而函数返 ...

  6. 【软件安全】缓冲区溢出攻击(stack overflow)实践

    文章目录 前言 攻击准备 攻击目标与原理 漏洞程序 攻击程序 攻击步骤 关闭现有安全机制 以root身份编译漏洞程序 确定`shellcode`在内存中位置 执行攻击程序 总结 参考资料 前言 最近在 ...

  7. 软件安全实验——lab7(缓冲区溢出3:返回导向编程技术ROP)

    目录标题 1.举例详细解释什么是Return-orientd Programming ROP?(至少两个例子:x86 和arm) (1)ROP在X86指令集上的实例 (2)ROP在ARM上的可行性 2 ...

  8. ROS理论与实践——二、ROS基础

    ROS理论与实践--二.ROS基础 前言 一.创建工作空间 1 什么是工作空间 2 创建流程 二.创建功能包 1 创建命令 2 创建流程 三.ROS通信编程 1 话题编程 1.1 话题编程流程 1.2 ...

  9. 【甄选靶场】Vulnhub百个项目渗透——项目二十七:Pinkys-Palace-2(LFI,端口敲震,ssh爆破,64位缓冲区溢出)

    Vulnhub百个项目渗透 Vulnhub百个项目渗透--项目二十七:Pinkys-Palace-2(LFI,端口敲震,ssh爆破,64位缓冲区溢出) 靶场地址

最新文章

  1. Python requests模块相关接口
  2. Highmaps网页图表教程之图表配置项结构与商业授权
  3. HDFS修改副本数并生效
  4. Java 第二章 程序设计基础
  5. 利用向量叉积求三角形的面积(+STL:nth_element求第K大的数)
  6. java的基础类库称为_JAVA基本类库介绍
  7. 《C++ Primer 5th》笔记(2 / 19):变量和基本类型
  8. C++并发编程之std::future
  9. sql二进制转十进制_了解SQL十进制数据类型
  10. (转)2017德勤技术趋势报告:未来8年,机器智能如何创造价值
  11. 微型计算机原理与应用彭楚武,微型计算机原理及其应用
  12. Vasya the Hipster
  13. 弗兰克赫兹大物实验数据处理
  14. 经典Java题目:输入一个数字,输出它的大写汉字(阿拉伯数字转汉字)
  15. Q: 'BMap' is not defined?BMap初始化报错爆红
  16. 用pymongo对mongoDB增删改查(CRUD)
  17. 原生js实现简易版消消乐
  18. STM32脱机烧录器源文件、离线烧录器 制作资料 源文件
  19. 自学Java有什么好方法?
  20. Python学习.第六天.字典

热门文章

  1. 计算机基础知识与公文写作,公文写作与计算机基础知识
  2. Java Idea设置运行内存大小(开发必备)
  3. Jenkins拉取代码返回错误码128
  4. PS 2019 Mac版 自学入门系列(八)—— 替换背景
  5. BERT-QE:用于文档Rerank的上下文化查询扩展模型
  6. 美学心得(第二百三十七集) 罗国正
  7. 64位程序调用32位dll
  8. 将军今天讲c语言了吗第六,计算机c语言 第六章:函数 教学设计(修改).doc
  9. 浪潮 服务器 修改raid,浪潮服务器RAID配置方法-20210723233916.docx-原创力文档
  10. Win8下双系统win7 教程详解