2019 第十二届全国大学生信息安全竞赛初赛 bbvvmm 解题报告(writeup)

Posted by HX on 2019-04-23 | 👓

看题目名大概是虚拟机(VM)题。先运行一下,提示输入用户名和密码,输错后提示错误退出。看 main 函数,拉到最后:

可见要打印出 flag,需要 v5v8 同时为 0。v5 是一个字符串比较的结果,v8 是一块内存的值。做题时发现 v5 不涉及虚拟机,比较好搞,先看它。

v5 和一串疑似 base64 编码的字符串比较,在 base64_encode 函数中可以确认的确是 base64 算法,但字母表改过:

用这个字母表解码那串 base64,得到 EF468DBAF985B2509C9E200CF3525AB6

再前一个函数 convert_to_binascii 只是把数组转换成字符串,如 0x61, 0x62, 0x63 转换成 616263。

前面两个函数看起来是加密算法,搜索其中魔术数 0xA3B1BAC6 可知是 SM4 算法,其密钥在前面的一串赋值语句里。https://github.com/fantapsody/libsms4 这里找了个解密库,自己改一下解密 EF468DBAF985B2509C9E200CF3525AB6

得到 6261647265723132。这应该是输入正确的用户名和密码时,提供给加密函数 sub_4018C4() 的输入,即 v13 的内容,而 v13 由前面一个函数 sub_4066C0() 对用户名进行变换得到,测试了下,这个变换应该是把用户名转成十六进制的 ASCII 码串,比如 abc 转成616263。那么 6261647265723132 对应的用户名就是 badrer12

用户名解决了,接着是密码,整个 v5 都没涉及到密码,因此 v8 应该和密码有关,看看密码输入后存到哪:

是存到 ptr 为首地址的一块内存里。上下又有两个函数引起注意:vm_init()vm_execute()vm_init() 里是大量的赋值,可能是对某个结构进行初始化,这个结构随后传给 vm_execute()vm_execute() 本身只是一个循环里调用另一个函数,进入这个函数 sub_40656D() 可以看到很明显是解释 opcode 的虚拟机:

看到这里,再回去 vm_init(),这时一些初始化操作的意义就比较清楚了。

像这样的初始化,上面是 opcode,下面是这个 opcode 对应的函数即 handler。大概是虚拟机会对输入的密码进行操作,最后得到一个值,这个值赋给 v8 然后判断是否为 0。直接从 IDA 里把虚拟机部分的代码复制出来,写一个 C 程序模拟这个虚拟机,并根据最终值是否为 0 分别打印 Right 和 Wrong,然后用 angr 符号执行一下就得到密码了。angr 脚本:

运行脚本,等几分钟就有结果。

所以密码是 xyz{|}

本地测试一下,成功。

nc 连远程服务器有个坑,密码的结尾不能有换行符,也就是输完密码不能用回车来结束输入… 不然打印不出 flag。所以输完密码立即按 Ctrl+D 输入 EOF,就可以得到 flag。