流浪者
十道题中最简单的一道题目,程序要求输入一个密码,首先判断了密码是否由字母 + 数字组成,然后会将它们进行一步转换,其算法如下
1 | for ( i = 0; flag[i]; ++i ) |
然后会将经过转换的密码传入另一个函数,代码如下
1 | BOOL __cdecl sub_4017F0(int int_flag) |
把密码作为下标,从一个硬编码的字符串中取出对应的字符,要求最终拼接的结果是 “KanXueCTF2019JustForhappy”,思路很简单,只需要逆推回去就好了,但是需要注意的是逆推回去的字符有多重排列可能,通过动态调试就可以找出真正的 flag。
1 | table = "abcdefghiABCDEFGHIJKLMNjklmn0123456789opqrstuvwxyzOPQRSTUVWXYZ" |
C 与 C++
程序的主要缺陷在于将 C 和 C++ 的内存分配、回收函数进行了混用,原则上通过 malloc 分配的内存必须使用 free 回收,而不能用 delete 回收,同样的,new 分配的内存只能使用 delete 进行回收,而不能使用 free 回收。
但是题目中同时给出了 malloc、free、new 和 delete,并且没有限制函数的调用,这时就有可能造成一些问题。
主要漏洞在 del 函数,代码如下
1 | void __fastcall del(__int64 a1) |
通过 new 分配的内存中存在一些代码段指针,其值为 0x401228,在 del 函数中对这个地址进行了验证,如果这个地址已经被修改,那么就会调用它指向的代码。现在一个很明显的思路就是想办法修改这个指针,指向我们想要的地址。
由于程序中 C 和 C++ 代码混用的问题,我们可以使用 delete 来回收 malloc 的内存,如果提前在 malloc 出来的内存中伪造好数据,就有可能构造一条函数“调用链”,进而拿到 shell。
1 | # coding=utf-8 |
变形金刚
安卓题目,一开始用 JEB 看,发现主要的代码似乎有些问题,因为行为和真机上操作的并不一样,当密码输入错误时会输出 error,这一点在代码中也没有找到。后来用 APKIDE 查看程序,搜索“用户名或密码为空”这个字符串的时候发现在 android\support\v7\app 目录下面存在一个文件也包含这个字符串,实际上这个才是真正的代码。
1 | package android.support.v7.app; |
调用了 liboo000oo.so 这个库,用 IDA 打开发现里面的逻辑很复杂,基本上没办法静态逆向,所以上真机开始动态调试。
调试的时候也踩了很多坑,关键函数代码如下
1 | int __fastcall sub_CDEAB784(int a1) |
乍一看很像 BASE 64 算法,而且还给出了“表盘”和密文
1 | !:#",0x24,"%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz01234 |
但是找了很多 BASE64 算法(各种语言的都试了一次),没有一个能够正常解码,后来通过调试仔细分析了一下代码,发现算法虽然和 BASE64 很像,但是里面夹杂了额外的操作,例如异或和移位等等,将算法整理出来,然后编写了一个 python 脚本尝试解密成功。
1 | enc = r" {9*8ga*l!Tn?@#fj'j$\g;;" |
最后可以解出密码为 fu0kzHp2aqtZAuY64
拯救单身狗
这是一道比较简单的 pwn 题,其主要漏洞点在编辑堆块的时候 index 会越界。我们可以利用越界的指针去修改其他堆块,例如 edit_singledog 却修改了 luckydog。
题目的一大坑点在于没有给出 libc,也就是说 2.23 和 2.27 两个版本都有可能,我一开始编写了 2.23 的利用脚本,本地测试通过,但是拿到远程服务器就会崩溃,检查原因是地址没有正确的泄露,猜测远程服务器是 2.27 版本的 libc,由于 2.27 存在 tcache 机制,所以需要先将 tcache 填满。
1 | # coding=utf-8 |
影分身之术
拿到题目上网搜索了一下,和某年的题目很相似,按照网上的 wp,将程序中屏蔽右键菜单的函数进行 patch,然后运行程序通过查看源代码得到一段 js
1 |
|
从 js 里面看到一部分代码逻辑,首先 key 是由 simpower91 开头,后跟一定长度的其他字符,验证正确之后会调用 sptWBCallback 函数,这个函数负责与程序中的原生代码进行交互,它会将 simpower91 进行剔除,只留下剩余的字符到程序中进行下一步验证。
通过动态调试分析,00492088 处的函数用来处理 sptWBCallback 发送的请求,其内部调用了一个隐藏函数,静态分析不能确定它的地址,通过调试得到隐藏函数的地址为 00493F70,简单分析一下这个函数可以得知我们的输入为 4 个字节
这个函数又调用了很多其他函数,我动态调试了很久也没有看懂在做些什么,于是换了一种思路,当程序获取到输入的时候,在相应的内存区域下硬件断点,F9 几次来到 0047162C 函数内部,从这里开始去我们输入的字符串进行了一堆看不懂的操作,不过在 0x94000 地址附近我发现了一些动态生成的数据,而且程序进入了某种循环当中,每次循环都会对这里的数据进行一些操作。
直到一条 long jmp 指令,会将程序执行流定位到这段数据中,这时我才发现所谓的数据原来是一段动态生成的 shellcode,其功能是将每个字符加上 0x7f,之后会和预置的数据进行对比。那么通过单步跟踪找到这 4 组数据解密就可以得到真正的密码。
最终得到 flag :simpower91a123
- 本文作者: Catalpa
- 本文链接: https://wzt.ac.cn/2019/03/11/kanxue2019Q1/
-
版权声明:
本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。