首页
社区
课程
招聘
[原创]Flare-on5 12题 suspicious_floppy writeup
2018-10-8 22:21 4254

[原创]Flare-on5 12题 suspicious_floppy writeup

2018-10-8 22:21
4254

题目给出一个镜像文件



观察镜像中的文件判断为DOS系统盘


使用DOSBox挂载镜像文件,编辑dosbox.conf

[cpu]
[dosbox]
machine=vga
[autoexec]mount c test
boot suspicious_floppy_v1.0.img

运行DOSBox,尝试输入密码,回显密码是否正确



解压镜像文件可以发现DOS启动后运行AUTOEXEC.BAT,然后执行infohelp.exe

对infohelp.exe进行逆向分析,可以发现程序是由Watcom编译的16位程序

16位程序直接无法使用F5插件,掏出王爽老师的书籍温习16位x86汇编,分析时要注意区分CS、DS、ES段



知道了infohelp.exe使用的编译器,制作Watcom标准库的FLIRT signature的签名,IDA自动标记了Watcom的库函数,大大简化了逆向工作量

(参考https://blog.csdn.net/hgy413/article/details/50612296)



infohelp.exe程序由sub_10000作为入口,主要流程可以按如下表示:

printf("Enter the password to receive the message:")
scanf("%s", &pw)
f = open("key.dat")
f.write(pw)
f.close()
f = open("message.dat")
message = f.read()
f.close()
printf(message)

在infohelp.exe中并没有检查密码,而是在输入密码后,直接输出了message.dat的文件内容

此外我们还注意到了程序并没有回显Welcome to FLARE spy...的信息,这和DOSBox运行的情况不符

为了验证这个情况,把镜像中的文件解压挂载到用户盘,启动DOSBox自带的DOS系统运行infohelp.exe

解压镜像到DOSBox\floppy,编辑dosbox.conf并再次运行DOSBox

[cpu]
[dosbox]
machine=vga
[autoexec]
mount c floppy


实验现象和预期的一样,程序在输入密码后直接输出了Message


将镜像文件和原版的DOS系统镜像进行比对


可以发现镜像文件的头部,MBR有一段已经被修改了

使用VM+IDA5.5对这个MBR进行调试(16位程序,下面的内存地址按段:偏移表示)

(参考https://www.cnblogs.com/alwaysking/p/8511280.html)


MBR一开始将镜像中的tmp.dat拷贝0x6000字节到0:0x800并跳转到该区域执行

在执行这段shellcode时,MBR继续加载tmp.dat的一部分数据到数据内存中,然后修改了0:0x4c的数据,然后跳转到解码出的微软原版MBR继续运行

其中0:0x4c地址是int 13h中断服务函数的地址,运行后13h中断服务地址被改为0x97400012



这是一种Bootkit,Bootkit通过感染磁盘主引导记录(MBR)方式,在启动时对系统服务进行hook,由于预先于操作系统、应用程序加载所以很难被查杀

对hook后的中断服务13h继续跟进

伪造的int 13h函数会先记录int 13h所有的参数并调用原版系统服务,原版系统服务执行完后,根据系统服务的参数进入不同的handler

以0x9740:0x105为例,当中断服务13h的参数cx == 0x2111 && dh == 1 && al == 1时,会调用0x9740:0x17e的handler,根据镜像中的信息该镜像的线性地址LBA  = 0x200 * [(磁盘柱面 * 36) + (磁头 * 18) + (扇区号 – 1)],此时LBA正好对应message.dat

也就是说当读取message.dat会调用0x9740:0x17e地址的函数



其他的handler和这个例子类似,其中和密码相关的一个就是在打开message.dat文件时会被hook执行tmp.dat中的函数,另一个就是写key.dat时会将写入的内容复制到指定的内存区域



也就是说通过Infohelp.exe作为触发器,infohelp.ex在输入密码后将密码写入key.dat,此时输入的密码会被复制到指定的内存位置,下一步在打开message.dat时,被hook到真正的密码验证程序

恢复0x9740:0x17e这个handler,大致流程如下



查阅wiki资料,这是One-instruction set computer (OISC)中的subleq的模拟器

只支持一种指令的处理器却可以执行任意的逻辑,可以降低芯片成本,同时给逆向增加难度

(参考: https://en.wikipedia.org/wiki/One_instruction_set_computer)

在python中重构了subleq的simulator,并执行dump的subleq程序

def decrypt_unit_9740_5d9d(baseAddr_s0, srcIdx_s1, tarIdx_s2, flag_s3):
    retval = 0
    if flag_s3 != 0:
        if cmpLeShort(getShort(baseAddr_s0,tarIdx_s2), getShort(baseAddr_s0,srcIdx_s1)):
            retval = 1
        else:
            retval = 0
    else:
        retval = 0
    val = subShort(getShort(baseAddr_s0,tarIdx_s2), getShort(baseAddr_s0,srcIdx_s1))
    baseAddr_s0 = setShort(baseAddr_s0, tarIdx_s2, val)
    return [baseAddr_s0, retval]

def decrypt_9740_5dd9(baseAddr_s0, length_s1, startIdx_s2):
    idx = startIdx_s2
    printChr = 0
    while idx + 3 <= length_s1:
        ret = decrypt_unit_9740_5d9d(baseAddr_s0, \
                                  getShort(baseAddr_s0,idx), \
                                  getShort(baseAddr_s0,idx+1), \
                                  getShort(baseAddr_s0,idx+2))
        baseAddr_s0 = ret[0]
        result = ret[1]
        if result == 0:
            idx += 3
        else:
            idx = getShort(baseAddr_s0,idx + 2)
            if idx == 0xffff:
                break
        if getShort(baseAddr_s0, 4) != 0:
            printChr = chr(baseAddr_s0[4])
            print printChr,
            baseAddr_s0 = setShort(baseAddr_s0, 4, 0)
            baseAddr_s0 = setShort(baseAddr_s0, 2, 0)


参考Flare-on4的11题,也是解subleq密码,读了网上去年的WP,一般使用到了进行侧信道攻击,通过不同密码输入的CPU Titk、程序指针图等手段来测试密码

但是我按去年的方法进行尝试,除了发现密码中输入了’@’会大大增加CPU Tick外并没有总结明显的规律

然后进行数据流分析,追踪密码的流向,对读写密码的敏感位置进行标记,也没有明显发现



无奈之下,我尝试对subleq程序进行逆向,还原subleq程序逻辑

在读去年Flare-on4 11题的官解时,有一个地方是值得我注意的,出题者给了几个宏来表示诸如MOV和MOVJMP的汇编操作


由此推测,标准汇编指令是可以由固定的subleq组合而得的,通过不断尝试和摸索,我定义了以下指令


其中最难的是subleq通过指令修改了自身指令,这种情况通过指令操作的target address写入了已经识别为指令的地址区域列出,进一步可以发现这些自身修改的指令主要用于指针操作*(%a) = %b, %b = *(%a), jmp *(%a)等操作,可以理解通过修改自身指令替换简单mov中的操作地址,来实现指针访问


反编译器的设计思路是先区分指令区、数据区,分类subleq基础指令

subleq基础指令由3个操作数构成,通过区分source、destination、target是否为零,是否相等,区分出不同的基础指令,如 subleq s,d,t / subleq z,d / subleq z,z等

然后将不同的基础指令组合成复合指令,复合指令一般会以基础指令的固定组合重复多次,并且意义明显,如mov或者跳转操作


然后将指令解析成对应的LikeC


静态分析后得到以下逻辑



OMG,这又是一个OSIC中的另一类rssb

再编写一个反汇编器,rssb比subleq还要抽象,基础指令只有一条rssb x,但是x有几个特殊值


x为-2时代表返回,-1只填补位置,0为当前程序指针,1为rssb累加器,2为常0寄存器,6为输出寄存器

当然这个是由本题的编译器定义的,并不一定通用,类似subleq反汇编器一样,合并基础指令为复合指令如下


生成对应的rssb解析文件和LikeC




进行静态分析,还原函数逻辑如下





密码检查的逻辑渐渐浮出水面,‘@’作为检查密码的结尾字符,而最后的密码校验方式是自定义hash算法与预定的hash表比对

其中hash计算公式如下

for i in range(15):
    hash[i] = (((key[i+1]-32) << 7 + (k[i]-32)) ^ (i * 33) + magicsum) & 0xffff

其中magicsum是所有密码的ascii和加3072乘密码长度

magicsum = 3072 * len(key) + sum(key)

由于magicsum不得知,key也不知到,只有期望的hash表,magicsum只能为0~0xffff

进行枚举倒推key[],然后检查magicsum是否符合约定并打印




找到密码Av0cad0_Love_2018@flare-on.com



最后感谢poyoten(我们团队的po叔)和Alexander Polyakov的帮助,ChaMd5安全团队 up up!,欢迎各位大佬加入


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

最后于 2018-10-9 11:53 被chenzhuoru编辑 ,原因:
上传的附件:
收藏
点赞2
打赏
分享
最新回复 (3)
雪    币: 32408
活跃值: (18775)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
kanxue 8 2018-10-9 09:35
2
0
建议将镜像文件 *.img上传到论坛上一份。
雪    币: 133
活跃值: (94)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
chenzhuoru 1 2018-10-9 11:53
3
0
kanxue 建议将镜像文件 *.img上传到论坛上一份。
updated
雪    币: 32408
活跃值: (18775)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
kanxue 8 2018-10-9 16:16
4
0
chenzhuoru updated
感谢分享!
游客
登录 | 注册 方可回帖
返回