ARMpwn方法汇总

极客

ARM pwn方法汇总:从入门到放弃?不,是到精通!

哎,兄弟们,姐妹们,大家好!今天咱们不聊那些云里雾里的高大上,就来点实打实的干货——ARM pwn方法汇总,说实话,我当初刚接触这块的时候,那叫一个头大啊,感觉像是走进了迷宫,到处都是岔路,一不小心就撞墙,但熬过那段时间后,回头看,发现其实也就那么几板斧,今天我就把这几个月踩过的坑、总结出的经验,掏心窝子地跟你们唠唠,希望能帮你们少走点弯路,别像我当初那样,对着一个栈溢出愣是发呆了三天三夜,最后发现是偏移算错了,当场气得我差点把键盘给啃了!

ARMpwn方法汇总

咱们得搞清楚,ARM和x86那可不是一个物种啊!x86那家伙,CISC,指令复杂得跟老太太的裹脚布似的,长度还不一样,ARM呢,RISC,指令定长(基本都4字节),风格那叫一个清爽利落,但清爽归清爽,坑也不少。寄存器少啊!x86那寄存器多得跟地主家的余粮似的,ARM就那么十几二十个通用寄存器,功能还往往有重叠,搞得你一会儿用r0传参,一会儿用sp指栈,稍不留神就把自己绕进去了,还有那ARM和Thumb模式切换,简直像是人格分裂,你得时刻盯着PC寄存器的bit0,是0还是1,不然你调试器断点都下不对地方,气不气人?

咱们汇总一下最常见的攻击面吧,第一大类,那必须是栈溢出,别笑,真的,别觉得老土,在ARM上做栈溢出,关键点在于找溢出点和控制PC,但ARM的栈,跟x86的栈有点不一样,通常会涉及栈指针的对齐,比如必须8字节对齐啊啥的,你ROP链稍微堆砌错个地址,直接段错误,程序一脸懵,你更懵。

那怎么控制PC呢?这就引出了第二个大杀器——ROP(Return-Oriented Programming),但ARM上的ROP可比x86麻烦多了,x86那是“ret”遍地走,gadget多如狗,ARM呢?你得找pop {pc}或者ldr pc, [sp], #4这样的指令,由于是定长指令,你找gadget的时候,得留意中间别混入奇奇怪怪的Thumb指令碎片,否则你的链子啪一下就断了,前期功夫全白费,我当时找gadget找得眼都快瞎了,终于在某个库函数里找到一个合适的pop {r0-r3, pc},那一刻我差点没喊出来:“找到你啦,小宝贝!”

如果你面对的是个开了NX(堆栈不可执行)的环境,那ROP就是必须的,但如果运气好,遇到个没开NX的,那直接往栈上丢shellcode,然后bx sp跳过去执行,那酸爽,简直就像在沙漠里喝到冰可乐——爽翻天!不过嘛,现在这种好事儿越来越少了,咱还是得学点硬功夫。

第三类,那必须是格式化字符串漏洞了,这玩意儿在ARM上玩起来也别有一番风味,x86上变参是通过栈传的,ARM上呢?前四个参数分别放在r0-r3寄存器里,从第五个参数开始才用栈,你写格式化字符串的时候,%x那可真能把你弄晕,你得找准偏移,有时候你以为读的是栈上的第三个参数,结果人家是从寄存器里拿的,你读出来一串R0的“神秘数字”,还以为是某个地址,心里乐开了花,结果一算,嘿,是外壳的入口地址,白高兴一场!

怎么利用呢? 常见的就是任意地址读任意地址写,泄露地址的话,用%p或者%s一个个试过去,像个侦探一样,写的话,就得用%n了,那玩意儿简直是指哪打哪的精准导弹,但它又喜欢一次写4字节(ARM是32位的话),你得把地址拆开分成低半和高半,依次写进去,这过程犹如在写微型汇编,半点马虎不得。

光说这些理论,可能你们还觉得云里雾里,咱们拿当下最火的 Meltdown和Spectre 那类CPU漏洞来说吧,虽然跟传统的pwn方法不完全一样,但本质也是ARMSide-channel攻击在搞事,你得研究流水线、分支预测这些硬件知识,才能构造出那种能“跨过”权限检查的恶意探针,我当时读完那篇论文,惊得嘴巴都合不拢,原来在机器内部,有那么多数不清的秘密缓冲区,真是隔墙有耳啊!

高级玩法,那就是堆利用了,ARM上的堆管理器(比如glibc的,或着嵌入式常用的那个lwIP里的堆),结构跟x86的有很多相似,但也有自己的脾气,像是unlink宏,还有fastbin的分配规则,在ARM上表现出的字节对齐问题、前向/后向指针的影响,有时候会让你觉得像是在玩一个超级复杂的魔方,特别是当你的目标是个嵌入式设备,没有ASLR,也没有PIE的时候,那利用起来真是舒服,地址都是写死的,简直就像带了个GPS。

说了这么多,再分享一道我常用的解题思路,算是个小总结吧。

遇到一个ARM pwn的题,我的内心戏是:

“兄弟,先别急着GDB,赶紧先file一下看看是什么架构,32位还是64位的,开了哪些保护。” (内心OS:虽然是ARM吧,但万一给我来个ARM64呢,分析指令集都要多花我半天时间,!)

然后就是日常三连:查安全特性(NX、ASLR、PIE)、找漏洞点(字符串拷贝、输入函数)、看保护机制

如果发现没开PIE,那玩意儿的基址就是死的,我们只需要找一个能getshell或者泄露libc的ROP gadget就行,那gadget从哪找?别傻愣着,用Ropper甚至用命令行找一把梭,ROPgadget --binary ./pwn --only 'pop|ret',分分钟搞定。

要是保护拉满,那也没事,咱先泄露出libc的地址,利用格式化字符串漏洞,读一下GOT表,拿到libc的基址,再system("/bin/sh"),那感觉就像是在雪地里摸索着点火,成功的那一刻浑身暖洋洋。

还有,调试的时候,我强烈建议用gdb-multiarch,配一个gef或者pwndbg,你别指望Windows上的那套调试工具能管ARM的事,跨平台调试不支持的,到时候显示乱码或者寄存器全是错的,你会疯掉的,一定要装好模拟器(qemu-arm)或者有真机直接怼上去。

唉,折腾这行时间长了,你会发现,其实ARM pwn最难的还不是技术本身,而是耐心,一条ROP链,可能就卡在一个分支跳转上,你反反复复推演,最后发现其实就是个地址算错了一位,就像小时候做奥数题,难的不在思路,而在那个最后计算的竖式,错了一个数,满盘皆输。

ARM pwn是一个需要细心、耐心和“抠细节”精神的活儿。 你得像个侦探一样,不放过任何一个字节的异常;又得像一个外科医生,精准地切开那层薄薄的保护膜,别怕刚入门时的挫败感,那都是正常排毒过程,只要熬过了初期的“蒙圈阶段”,后面你会发现,原来在ARM世界里纵横驰骋,可比x86下那种“粗暴”的利用来得更为性感!

好了好了,今天先唠叨这么多,这些方法汇总都是我亲测有效或者掉坑里得出的血泪教训,下次咱们可得找个具体的CTF题目来实际操作一把,不然光说不练假把式,纸上谈兵可学不会pwn,兄弟们要是有什么更骚的操作或者更好的技巧,欢迎底下留言区分享出来,大家一起互相学习、少走弯路啊!咱们下期见!拜拜!

文章版权声明:除非注明,否则均为极客网安-咸鱼原创文章,转载或复制请以超链接形式并注明出处。

目录[+]