JIT pwn
这种pwn和 RWCTF2022 | Brvc3’s Base 这种vm pwn最直观的不同在于它的输入是源码而非字节码,所以这些程序包含 源码->字节码
和 运行字节码
两个主要逻辑,其实比较像JIT。
例题
2021年上海市大学生网络安全邀请赛(东华杯)-pwn-boom_script
分析
程序直接告诉了我们keywords和内置的functions,而且提供了example。这实际上省去了逆向的部分,以为单凭这个我们就能够动态调试找漏洞了。但是,这不是我第一次见这道题,它在省赛决赛出现时并没有这些提示,因此我花了很久去逆向分析它的语法,差不多逆完了他放hint把这些内容放出了……因此我们还是来逆一下这个程序。
这种代码量大的程序,一步一步的分析调试是来不及的,比赛里遇见的话最好先调试看看常见的漏洞是否存在。
初始化
先按执行顺序分析,看初始化的阶段。这里我已经建好结构体了,所以会很直观,在做这些逆向量大的题目的时候我还是建议建立下结构体。
可以看到,这里将这些预设的标识符存入了内存,并赋予了相对应的字节码。知道了有哪些标识符,又便于我们复原出来语法。进入预编译函数看看。
这里的execve很诱人,但是发现只是拼接上echo,然后进行字符串操作罢了。
预编译
这块代码有点长(还行其实,省赛AWD那个是一个函数600行+)。直接在IDA看很难受,我喜欢复制到文本编辑器里。(我这里变量名命名得比较随意,source_end是正在处理的单词的结尾)因为没系统学过编译原理,有些描述估计不太准确,有错请及时指出。
明显,第一块是处理我们输入的标识符等,第二块是数字
先看第一块,如果和预设的标识符对上了,直接操作标识符的字节码。对不上就视作新的标识符存进去。
第二块数字没什么好看的,看第三块。这块是操作符。
可以看到,"
的操作明显是字符串。字符串是用堆块来存储的,空字符串则会申请0x20大小的chunk。其他操作符只是简单存一下字节码,而运算操作符不进行任何处理。
还原语法
)
可以看到,要求{}()一一对应,()会影响运算优先级。结尾要求有;
。
先看字符串,对应的字节码0x82在只出现在赋值区,而且预设的数据类型只要array,所以可以猜测字符串的语法和python类似。直接a = "aaaa" ;
而且可以看到,如果字符串的赋值就是free掉原来的堆块然后再用interpreter去申请一遍。
数组越界是vm pwn里常见的漏洞,所以也要重点关注数组。array在interpreter里没有特殊操作,只是简单的赋字节码。所以主要看execute。0x83除了在赋值块有,还有和赋值同级的一块。可以看到这里检查了array的字节码、[]和;
,所以猜测这是array的声明块。语法如array a[10];
可以看到这里, [] 中间的数字经过了一个函数操作,该函数返回v5,明显v5就是中间的数字。所以数组也是存在堆上的,而且会比我们所需的还大0x10。然后看赋值过程。明显,语法是a[0] = 123;
并没有对idx做任何检查,存在数组越界。造成了一个堆上的任意写。
两个print的功能比较简单。
其他的语法基本上也就可以类推了。
调试&利用
字符串 UB attack
我们可以利用UB chunk的特性来泄露libc。
但是由于我们对堆块的操作有限,这里只能够完成泄露。想要getshell还要找其他洞。
数组越界
有了堆上的数组越界,也就可以随意控制堆上的指针了,打法很多。
要注意数组的大小,图中deadbeef是我们插入的元素,分别占据了a[-2]到a[2],每个相邻索引间隔0x38。通过字符串可以构造布局,以达到篡改的目的。
exp
1 | from pwn import * |
总结
难点在于逆向,找到漏洞后利用起来其实还可以。
出这种题建议还是要给出语法,否则太恶心了。
VM pwn和解释器pwn常见漏洞:数组越界、字符串UAF