//感谢wyx学姐
0x00 栈帧复习
栈帧建立过程:
压参(从右向左)
—>将紧接着CALL后面的那条指令的内存地址即返回地址压入栈中
—>压入当前栈帧EBP
—>EBP指向ESP指向的栈顶即刚压入的当前栈帧EBP
—>ESP上移设置新的栈顶
栈帧销毁过程:
ESP下移指向EBP销毁栈空间
—>弹出上一个栈帧的EBP(ESP指向返回地址)
—>弹出返回地址
0x01 栈溢出
栈溢出指的是程序向栈中某个变量中写入的字节数超过了这个变量本身所申请的字节数,因而导致栈中与其相邻的变量的值被改变。
栈溢出前提
- 程序必须向栈上写入数据。
- 写入的数据大小没有被良好地控制。
ebp-x = esp+y
system("/bin/sh")
调用shell。
实例
level1
如标红部分所示。在ebp
上方88h
是buf
存储位置,而读进buf
的长度
面向题解写脚本,pwntools
了解一波。
关于pwntools
使用from pwn import *
将所有的模块导入到当前namespace,这条语句还会帮你把os,sys等常用的系统库导入。
常用模块如下:
- asm : 汇编与反汇编,支持x86/x64/arm/mips/powerpc等基本上所有的主流平台
- dynelf : 用于远程符号泄漏,需要提供leak方法
- elf : 对elf文件进行操作
- gdb : 配合gdb进行调试
- memleak : 用于内存泄漏
- shellcraft : shellcode的生成器
- tubes : 包括tubes.sock, tubes.process, tubes.ssh, tubes.serialtube,分别适用于不同场景的PIPE
- utils : 一些实用的小功能,例如CRC计算,cyclic pattern等
1.连接
1 | 本地 :sh = porcess("./level0") |
2.IO模块
1 | sh.send(data) 发送数据 |
3.Shellcode生成器
结合asm可以可以得到最终的pyaload。
其中的子模块声明架构,比如shellcraft.arm
是ARM架构的,shellcraft.amd64
是AMD64架构,shellcraft.i386
是Intel 80386
架构的,以及有一个shellcraft.common
是所有架构通用的。而这里的shellcraft.sh()
则是执行/bin/sh
的shellcode。
context
是pwntools
用来设置环境的功能。在很多时候,由于二进制文件的情况不同,我们可能需要进行一些环境设置才能够正常运行exp
,比如有一些需要进行汇编,但是32的汇编和64的汇编不同,如果不设置context会导致一些问题。
log_level
设置日志输出的等级为debug,这句话在调试的时候一般会设置,这样pwntools
会将完整的io
过程都打印下来,使得调试更加方便,可以避免在完成CTF题目时出现一些和IO相关的错误。
1 | from pwn import * |
4.ELF文件操作
1 | >>> e = ELF('/bin/cat') |
5.数据打包
数据打包,即将整数值转换为32位或者64位地址一样的表示方式,比如0x400010表示为\x10\x00\x40一样,这使得我们构造payload变得很方便。
用法:
- p32/p64: 打包一个整数,分别打包为32或64位
- u32/u64: 解包一个字符串,得到整数
p对应pack,打包,u对应unpack,解包,简单好记。
0x02 安全机制
1.Canary探测
要检测对函数栈的破坏,需要修改函数栈的组织,在缓冲区和控制信息(如 EBP 等)间插入一个 canary word。这样,当缓冲区被溢出时,在返回地址被覆盖之前 canary word 会首先被覆盖。通过检查 canary word 的值是否被修改,就可以判断是否发生了溢出攻击。
GS编译选项(金丝雀)
微软在编译程序时使用的安全编译选项,在VS中默认启动这个编译选项。在所有函数调用发生时,向栈中压入一个额外的随机DWORD,被称作“Security cookie”。
SSP保护
作为GCC的一部分来提供堆栈保护,取fs寄存器0x28处的值作为canary word,且有意将局部变量中的数组放在函数栈的高地址。
2.DEP
基本原理是将数据所在内存页标识为不可执行,当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,此时CPU就会抛出异常而不是去执行恶意指令。
![[]](/2018/10/23/20181022zyc/2.png)
3.ASLR
内存地址随机化,通过加载程序的时候不在使用固定的基地址,从而干扰shellcode定位的一种保护机制。
(操作系统分页)
0x03 ROP
什么是ROP?
ROP的全称为Return-oriented programming(返回导向编程),这是一种高级的内存攻击技术可以用来绕过现代操作系统的各种通用防御。
ROP主要思想
其主要思想是在栈缓冲区溢出的基础上,利用程序中已有的小片段 (gadgets) 来改变某些寄存器或者变量的值,从而控制程序的执行流程。所谓 gadgets 就是以 ret 结尾的指令序列,通过这些指令序列,我们可以修改某些地址的内容,方便控制程序的执行流程。
ROP攻击前提
- 程序存在溢出,并且可以控制返回地址。
- 可以找到满足条件的 gadgets 以及相应 gadgets 的地址。(一个漏洞可以利用很多次)
基本ROP分类
ret2text
即控制程序执行程序本身已有的的代码 (.text)。其实,这种攻击方法是一种笼统的描述。我们控制执行程序已有的代码的时候也可以控制程序执行好几段不相邻的程序已有的代码。
ret2shellcode
在栈溢出的基础上,要想执行shellcode,需要对应的 binary 在运行时,shellcode所在的区域具有可执行权限。
ret2syscall
控制程序执行系统调用,获取shell。一般情况下,利用execve(“/bin/sh”,NULL,NULL)
这个系统调用。
ret2libc
控制函数的执行 libc
中的函数,通常是返回至某个函数的 plt
处或者函数的具体位置 (即函数对应的 got 表项的内容)。一般情况下,我们会选择执行 system(“/bin/sh”)
。
0x04 .plt/.got表
GOT(Global Offset Table)和PLT(Procedure Linkage Table)是Linux系统下面ELF格式的可执行文件中,用于定位全局变量和过程的数据信息。
共享库机制
在二进制文件(比如object file)中会有一段叫relocations的部分,这部分的内容在链接时候(link time)再进行敲定确切的值,注意链接可以发生在运行前(称为静态链接,toolchain linker),也可发生在运行时(称为动态链接,dynamic linker)。具体relocations部分中的内容就是在讲:“确定X这个符号(symbol)的值,然后把这个值写到二进制文件的Y偏移处”。每一条relocation都有确定的类型(定义与ABI文档中),从而确切地说明每个类型的值到底该如何敲定。
1.Global Offset Table (.got)
共享库(.so文件)
可以加载到任意一段物理内存上就用。
代码重用(code sharing)。
virtual memory的神奇机制实现,对于每一个进程,要找到其特有的data段时,当前地址+已知的相对位置=索引的data段。
如果一个so文件想去索引其他so文件中的data怎么办?
我们可以patch这个so文件的代码部分,直接把那个data的位置为patch上,但这样就破坏了so文件的 code-sharability。而计算机中有一个原理就是:所有问题都可以通过加一个间接层来解决。这里,这个间接层就叫global offset table or GOT.
2.procedure linkage table(.plt)
如果是外部函数调用?
此处,我们使用的“间接层”叫做procedure linkage table or PLT。code只会通过PLT stub(PLT桩代码,其实就是一小段代码),实现外部函数调用。
延迟绑定
第一次调用foo函数,通过PLT stub(PLT桩代码)进行动态链接(地址解析),找到foo函数真实地址并patch GOT表,第二次直接通过PLT桩代码跳到foo函数真实地址。
通过PLT表跳转到GOT表,而在第一次运行某一个函数之前,这个函数PLT表对应的GOT表中的数据为@plt函数中第二行指令的地址。0x540为PLT[0]的地址,该处的指令会进入动态链接器入口,执行一个函数将真正的函数地址覆盖到GOT表中。
![[]](/2018/10/23/20181022zyc/3.png)
0x05 附件:PWN向环境配置(DJ)
改架构
第一步: 确认你有一个64位架构的内核 你可以打开终端然后输入:
1 | $dpkg --print-architecture |
你将会看到像下面这样的内容 amd64 这说明着你已经拥有了64位架构内核。
第二步: 确认你打开了多架构支持功能(多架构支持可以让你在有64位库的情况 下使用32位库。) 输入:
1 | $dpkg --print-foreign-architectures |
输出是: i386 如果你还没有多架构支持你需要打开它。 打开多架构支持: 输入
1 | $sudo dpkg --add-architecture i386 |
然后就会开始下载更新然后运行。 之后你需要输入:
1 | $sudo apt-get -f dist-upgrade |
它会检查你已经拥有的库文件是否有更新的版本。 然后可能需要安装32位 库,在终端里输入以下命令
1 | $sudo apt-get install lib32c-dev |
安装pwntools
1 | $sudo apt-get update |