RV130X Firmware Analysis
1. RV130X Firmware Analysis
Firmware analysis is an essential part of security research and targeted search for vulnerabilities in IoT products, vehicle components, industrial control systems, and a multitude of other types of software/hardware systems designed for various purposes.
1.1. Cisco RV130 VPN router
We will be using a firmware for the Cisco RV130 VPN router which can be downloaded from Cisco. The file is named RV130X_FW_1.0.3.55.bin
1.1.1. Information Gathering
By examining the firmware header, it’s possible to identify the string “RV132ND7CODE”.
Performing this check is crucial to verify that the information shown in the filename RV130X_FW_1.0.3.55.bin is accurate. Through quick OSINT research, we can determine that this string corresponds to the target hardware, the Cisco RV130 VPN Router.
It appears that this firmware is also compatible with the WiFi-enabled model, the Cisco RV130W VPN Router. This assumption will be confirmed later when we examine the contents of the files present in the filesystem.
Router Motherboard
Through OSINT research, we were able to locate a PCB board image of the Cisco RV130W router and identify its exposed development interfaces:
-
JTAG Port: A series of pin headers can be observed that matches the typical layout of a JTAG connector, which is commonly used for debugging and programming embedded devices. This interface features an unpopulated 20-pin header (only the holes where components should be soldered are present).
-
UART Serial Port: Typically used for accessing the management console, basic configuration, and debugging. This interface features an unpopulated 5-pin header.
Hardware Specification
Component | Specification |
---|---|
CPU | Cavium CNS3410 ARM11MPCore (@600MHz) |
Memory | 128MB (SDRAM) DDR2 |
Storage | 32MB Parallel NOR Flash |
Network | 5x Gigabit Ethernet ports |
WiFi | Broadcom BCM43217 (2T2R 802.11bgn) |
USB | 1x USB 2.0 host port |
Power Port | 12 VDC, 2 A |
FCC ID | N89-RV130W |
IC ID | 5005A-RV130W |
Board ID | 3763-20400102R |
Physical Dimensions | 149.86 x 29.9 x 150.11 mm |
Weight | 0.72 kg |
Power Supply | 12V 2.5A |
Firmware Stock Specifications
Feature | Specification |
---|---|
Stock bootloader | U-Boot 2008.10-mpcore |
Stock OS | Linux 2.6.31 |
Default SSID | ciscosb1 |
Default IP Address | 192.168.1.1 |
Default login user | cisco |
Default login password | cisco |
Network Protocols Support
Protocol | Support Details |
---|---|
DHCP | Server and Client |
PPPoE | Point-to-Point over Ethernet |
PPTP | Point-to-Point Tunneling Protocol |
DNS | Proxy support |
IGMP | Proxy and multicast forwarding |
RSTP | Rapid Spanning Tree Protocol |
DynDNS | Yes, with NOIP support |
NAT/PAT | Network/Port Address Translation |
Security Features
Feature | Details |
---|---|
Access Control | Management ACLs plus MAC ACLs |
Secure Management | HTTPS with username/password complexity |
Firewall Type | Stateful Packet Inspection (SPI) |
DoS Protection | SYN Flood, Echo Storm, ICMP/UDP/TCP Flood |
Content Blocking | Java, cookies, active-X, HTTP proxy |
Web Filter | Yes, filters and blocks harmful sites |
Furthermore, OSINT research revealed that the Cisco RV130W VPN router has reached End Of Lifestatus, with its last firmware update (target) released in June 2020.
As a result, there are known software vulnerabilities that Cisco has not patched for this router model.
1.1.2. Extract Filesystem
We’ll recursively extract files from the firmware using Binwalk with the following command:
binwalk –preserve-symlinks -Me RV130X_FW_1.0.3.55.bin
Where:
- –preserve-symlinks: Maintains the original symbolic links (symlinks) during firmware extraction, instead of modifying them for security purposes
- -M: Recursively scan extracted files
- -e: Automatically extract known file types
- RV130X_FW_1.0.3.55.bin: Target firmware file
This command will extract all the files while keeping the original symlinks structure intact, which is particularly useful for maintaining the exact firmware structure for analysis purposes.
From the image, we can determine the following information:
- Architecture: ARM Little Endian
- Board ID: RV13
- Build date: 2020-03-27
- The image contains 4 sections:
- Bin Header
- Linux Kernel (zImage)
- gzip compressed data
- Squashfs filesystem (very common in embedded devices).
Additionally, using the previous command, the recursive extraction of file 3479, which represents the compressed gzip file, is performed.
The binwalk execution on the gzip file reveals the following information:
- Linux kernel version: 2.6.31 (confirming the OSINT research findings)
- User developer: johnny
- Project name: RV110W_cavium
It appears that the “squashfs-root-0” extraction contains the most significant information.
After extracting the binary using binwalk the extracted file system looks like:
Now that we’ve extracted the filesystem, we can navigate through the files and attempt to find some useful information.
Let’s start with the study of binaries.
1.2. The choice of binaries for fuzzing
1.2.1. First – takes input from a file
Let’s begin by obtaining a list of all binaries with the execution flag enabled present in the firmware, along with their respective sizes.
Through an LLM, it was possible to further narrow down the search using the following prompts:
- ”< list of binaries > From this list, check ten binaries at a time to tell me if they accept file input (and with which input flags)”
- ”Which of the previously mentioned binaries would make good AFL targets?”
Since the “jsonparse” and “xmlparser1” binaries have already been analyzed in the post below, I opted not to fuzz them again and instead focused on identifying a more intriguing target: https://blog.attify.com/fuzzing-iot-devices-part-1/.
The choice for the first file remains between various “busybox” applets and “rxp”.
Let’s use QEMU to execute busybox with ARM architecture to discover its version and available functions.
While busybox 1.7.2 is from 2007, the build date shows 2020-03-27, suggesting a patched version. Given that busybox is built upon widely-tested utilities, finding novel vulnerabilities here is unlikely.
Let’s examine the rxp binary:
OSINT research enabled us to locate the source code for both the target binary version 1.4.4 and the latest version 1.5.2, which simplified our analysis.
A diff comparison was performed between the two projects to identify patches missing from version 1.4.4.
Both projects contain the file “xmlparser.c” which appears to handle XML file parsing.
Several vulnerable functions were identified in version 1.4.4 that were subsequently patched in version 1.5.2.
However, this information proved irrelevant, as subsequent analysis of the binary using Ghidra revealed that the rxp binary in the firmware is merely a branch of the open-source version 1.4.4 and had already been patched by Cisco.
1.2.2. Second – receives data not directly from files
Here too, I used an LLM to obtain a list of potentially suitable binaries. The prompt:
After manual processing of the search results, the following candidates were identified:
Among these binaries, httpd proved to be the most interesting target, and was subsequently selected for the second fuzzing campaign.
The selection of httpd as the target was strategically motivated, as this component is responsible for managing the router’s web interface.
Analysis of historical CVE vulnerabilities for this model demonstrated that the majority of successful attacks exploited vulnerabilities in the login webpage. These vulnerabilities were particularly significant as they frequently enabled attackers to completely circumvent the router’s authentication mechanisms.
1.3. Fuzzing Campaign - rpx
The binary selected for this fuzzing campaign is “rxp”, an executable that, as previously mentioned, is used for parsing XML files.
Below is an excerpt from rxp’s man page:
1.3.1. Summary
The fuzzing operation ran for approximately 15 hours and 40 minutes, producing the following results:
Metric | Master | Slave1 | Slave2 | Slave3 |
---|---|---|---|---|
Unique/Saved Crash | 8 | 9 | 8 | 7 |
Unique/Saved Hang | 0 | 2 | 2 | 3 |
Total Crash | 340 | 626 | 639 | 635 |
Corpus Count | 1989 | 2033 | 2086 | 1983 |
Cycle Done | 196 | 35 | 34 | 37 |
It should be noted that the unique crashes detected may be either identical or different across various instances.
1.3.2. Functions target
The fuzzing was performed on the following functions responsible for analyzing the input XML file:
The function names were taken from the rxp 1.4.4 open-source project.
1.3.3. Corpus Acquisition and Minimization
The test cases were generated using an LLM and sourced from the following GitHub directories:
- https://github.com/wasmerio/wasm-fuzz/blob/master/corpus_minimization.md
- https://github.com/winlibs/libxml2/tree/master/fuzz
All files from these directories were then consolidated into a single directory named “corpus_mega” using the following command:
Subsequently, realizing that the Windows Kali subsystem would not be suitable for AFL++ installation, the operation was moved to a Kali VMware machine to install AFL++ with QEMU support and perform corpus minimization using afl-cmin.
Subsequently, a decision was made to remove excessively large XML files to prevent slowdowns during the fuzzing process.
The final corpus consists of 148 XML elements.
1.3.4. Launch the fuzzing campaign
After corpus minimization, four AFL++ processes were launched
- one master and three slaves,
- using QEMU virtualization to execute the ARM32 architecture binary.
Each instance was configured to employ dictionary-based mutations using xml.dict to enhance corpus transformation accuracy.
./AFLplusplus/afl-fuzz -Q -M Master -i corpus -o output -x ../AFLplusplus/dictionaries/xml.dict ./squashfs-root/usr/sbin/rxp -s @@ 2> /dev/null
./AFLplusplus/afl-fuzz -Q -S Slave1 -i corpus -o output -x ../AFLplusplus/dictionaries/xml.dict ./squashfs-root/usr/sbin/rxp -s @@ 2> /dev/null
./AFLplusplus/afl-fuzz -Q -S Slave2 -i corpus -o output -x ../AFLplusplus/dictionaries/xml.dict ./squashfs-root/usr/sbin/rxp -s @@ 2> /dev/null
./AFLplusplus/afl-fuzz -Q -S Slave3 -i corpus -o output -x ../AFLplusplus/dictionaries/xml.dict ./squashfs-root/usr/sbin/rxp -s @@ 2> /dev/null
After approximately 5 hours of scanning, initial results were obtained, including several “crashes”.
After approximately 12 hours, both crashes and hangs were detected:
After approximately 15 hours and 40 minutes, the results remained largely consistent with the previous findings.
1.3.5. Proof of Work
As expected, the binary crashes when executed.
The content of the file causing the program to crash is as follows:
Now, it’s important to identify the exact instruction where the binary crashes, so we’ll use gdb-multiarch and QEMU for debugging.
Let’s start the binary in remote debugging mode:
Let’s connect gdb-multiarch to the debug socket:
Let’s identify the crash location:
Continue with execution:
The binary crashes when calling “free” in the shared library. Further investigation in this direction could lead to binary exploitation and potential CVE discovery.
1.4. Fuzzing Campaign – httpd
To practice, I decided to analyze the httpd binary, which does not directly accept input from stdin.
In this case, since the binary accepts neither file nor stdin input, it will be necessary to patch “httpd” and utilize AFL++ environment variables to directly fuzz the HTTP request parsing function.
1.4.1. Summary
The fuzzing operation ran for approximately 9 hours and produced the following results:
Metric | Master | Slave1 | Slave2 | Slave3 |
---|---|---|---|---|
Unique/Saved Crash | 0 | 0 | 0 | 0 |
Unique/Saved Hang | 0 | 0 | 0 | 0 |
Total Crash | 0 | 0 | 0 | 0 |
Corpus Count | 877 | 302 | 294 | 309 |
Cycle Done | 879 | 151 | 141 | 142 |
Although no crashes were detected.
1.4.2. Binary Patching and library Injection
In this case, the binary had already been analyzed in the following post, so I simply reproduced the steps for patching.
The httpd binary currently forks to the background using the daemon function. This forking behavior is not desirable during fuzzing..
The daemon function needs to be overridden to return 0 without forking. This can be accomplished either through LD_PRELOAD or by patching the assembly instructions.
An additional modification is required to make “httpd” process exactly one request (unlike a typical web server that processes requests indefinitely) before terminating. This enables identification of which specific request, if any, causes the web server to crash.
To close a socket, httpd calls the close function. There are three locations from where “close” is called.
Among them, we need to modify the one at 0x231c0 to call exit(0) instead of close.
To patch the instructions, we will use Cutter which is a GUI for radare2. Ghidra also supports patching binaries, but Cutter is better suited for this use case.
Upon navigating to 0x231c0 in Cutter, the following disassembly can be observed:
Double-clicking on close takes us to 0x106b4.
The exit function is located at 0x10b64.
The bl close instruction can be modified to bl 0x10b64 to call the exit function instead.
The instruction immediately before can be changed from mov r0, sl to eor r0, r0 which sets register r0 to 0 to give us the following disassembly.
The modification results in calling exit(0). A secondary modification is required to patch out the daemon call at 0x22CB4.
The instruction can be modified to eor r0, r0 to make the application interpret the call as successful.
Finally, with the changes in place go to File -> Commit changes to save the modifications. Let’s rename the file to httpd_patched.
Let’s verify if the binary has been correctly patched and modify its permissions for execution.
Running “httpd_patched” we can see that it doesn’t fork to the background and that quits after processing a single request as shown below.
To enable file-based input for httpd, we can utilize the opensource desocksmulti project, which serves as a bridge between network applications and file-based tools. This tool is particularly valuable for fuzzing network binaries with tools like AFL++ that primarily work with file input.
Desocksmulti works by intercepting network socket operations (socket(), bind(), listen(), and accept()) and redirecting them to stdin and stdout.
The compilation of desockmulti requires an ARM cross compiler. The armv7-eabihf-uclibc toolchain from bootlin is particularly suitable for this purpose. A uclibc-based toolchain is necessary as the firmware binaries utilize the same library.
This compatibility can be verified by running the file command on /usr/sbin/httpd, which indicates that the binary is dynamically linked to “ld-uClibc.so.0”.
So let’s clone the desockmulti repository:
Before compiling the code, the setup_timer() function call within the socket() function’s code must be commented out.
The compilation can be initiated by running make with the ARM-Linux-GCC compiler path specified in the CC environment variable.
The generated file desockmulti.so can be copied to the squashfs-root directory.
To verify that desockmulti functions correctly, httpd can be debugged using gdb-multiarch. First, a dependency to the “libpthread.so.0” library must be added using patchelf (which can be installed via apt). This addition is necessary because desockmulti utilizes threads, while httpd does not link to libpthread by default.
The binary can now accept requests from stdin, thus enabling fuzzing through AFL.
Although httpd_patched is prepared for fuzzing, it requires root privileges because it attempts to access /var/run/httpd.pid. To avoid this requirement, the binary can be modified to reference an alternative path that’s accessible without root privileges.
The binary can be opened with a hexadecimal editor to change /var/run/httpd.pid to /home/kali/hpd.pid and saved.
Rerunning httpd_patched we can see it runs without admin rights and errors.
Additionally, the file hpd.pid is created within the kali’s home directory.
1.4.3. How to see http responses
To see the web server’s response, the debugger is required, as these will not be printed on the screen.
Go into “www” folder and run the binary in qemu specifying the -g parameter.
The path to desockmulti.so is specified in the “LD_PRELOAD” environment variable. The other variable “USE_RAW_FORMAT” is specific to desockmulti.
In another terminal, we can start gdb-multiarch, set a breakpoint on fprintf and attach to port 5555.
When the breakpoint on fprintf is triggered, pressing continue multiple times will eventually reveal the response code in register $r2.
1.4.4. Corpus Acquisition and Minimization
The test cases and dictionary were sourced from the following GitHub directory:
These were supplemented with additional requests generated via LLM, along with the router’s basic login request.
Let’s now proceed with corpus minimization using afl-cmin.
The minimized corpus consists of 21 files.
The fuzzing process of the patched httpd binary can now begin.
1.4.5. Launch the fuzzing campaign
Four AFL++ instances were launched (1 master and 3 slaves):
QEMU_LD_PREFIX=.. QEMU_SET_ENV=USE_RAW_FORMAT=1,LD_PRELOAD=../desockmulti.so ../../../AFLplusplus/afl-fuzz -Q -M Master -x ../../dict/http_request_fuzzer.dict.txt -i ../../req_input_cmin/ -o ../../req_output/ -- ../usr/sbin/httpd_patched
QEMU_LD_PREFIX=.. QEMU_SET_ENV=USE_RAW_FORMAT=1,LD_PRELOAD=../desockmulti.so ../../../AFLplusplus/afl-fuzz -Q -S Slave1 -x ../../dict/http_request_fuzzer.dict.txt -i ../../req_input_cmin/ -o ../../req_output/ -- ../usr/sbin/httpd_patched
QEMU_LD_PREFIX=.. QEMU_SET_ENV=USE_RAW_FORMAT=1,LD_PRELOAD=../desockmulti.so ../../../AFLplusplus/afl-fuzz -Q -S Slave2 -x ../../dict/http_request_fuzzer.dict.txt -i ../../req_input_cmin/ -o ../../req_output/ -- ../usr/sbin/httpd_patched
QEMU_LD_PREFIX=.. QEMU_SET_ENV=USE_RAW_FORMAT=1,LD_PRELOAD=../desockmulti.so ../../../AFLplusplus/afl-fuzz -Q -S Slave3 -x ../../dict/http_request_fuzzer.dict.txt -i ../../req_input_cmin/ -o ../../req_output/ -- ../usr/sbin/httpd_patched
Below, all instances can be observed simultaneously working to fuzz httpd.
Unfortunately, after 8 hours and 40 minutes of execution, no crashes were detected.
Several factors may explain the absence of crashes:
- Binary Maturity and Previous Analysis
- The binary is widely known and has undergone extensive security research
- Many vulnerabilities have likely been identified and patched through previous investigations
- The codebase has benefited from multiple security reviews and improvements
- Hardware Performance Limitations
- The fuzzing was conducted on a VMware virtual machine with restricted resources
- Limited computational power can affect:
- The number of executions per second
- The effectiveness of the fuzzing process
- The ability to explore deeper program states
- Technical Approach Limitations
- The absence of grammar mutators significantly impacted the fuzzing efficiency
- Grammar mutators would have provided:
- Better understanding of HTTP protocol structure
- More intelligent mutation strategies
- Higher quality test case generation
- Improved coverage of protocol-specific edge cases
- More effective exploration of protocol-dependent code paths
These factors combined likely limited the fuzzing campaign’s effectiveness in identifying potential vulnerabilities.
2. Recap and Conclusions
2.1. Overview of Findings
The security assessment of the Cisco RV130X router firmware (version 1.0.3.55) revealed several significant findings. Through comprehensive analysis and testing, we identified both known vulnerabilities and potential new security concerns.
The firmware, which hasn’t received updates since 2020, demonstrated multiple attack surfaces that could be exploited by malicious actors.
2.1.1. Firmware Analysis
Through OSINT research, we gathered extensive information about both the hardware specifications and software components of the Cisco RV130X/RV130W VPN router.
The firmware analysis revealed that the device runs on an ARM11MPCore architecture with Linux kernel version 2.6.31.
Multiple outdated components were identified within the firmware, as no updates have been released since June 2020.
CVEs have been identified, including several critical security issues that could pose significant risks.
2.1.2. Fuzzing Campaigns
The assessment included two distinct fuzzing campaigns with notably different outcomes:
RXP Binary Campaign:
- Successfully identified multiple crash scenarios (32 unique crashes across four instances)
- Detected seven potential hanging conditions
- Achieved significant corpus coverage with over 8,000 test cases
- Debug analysis confirmed exploitable conditions in memory management functions
- Campaign duration: approximately 15 hours and 40 minutes
HTTPD Binary Campaign:
- Required extensive binary patching to enable AFL++ compatibility
- Implemented custom solutions for network protocol fuzzing using desockmulti
- No crashes or hangs detected despite thorough testing
- Campaign duration: approximately 9 hours
2.2. Technical Achievements
The assessment demonstrated successful implementation of various advanced security testing techniques:
- Binary patching and modification for fuzzing compatibility
- Custom tooling integration for network protocol testing
- Effective use of AFL++ with QEMU for ARM architecture testing
- Implementation of dictionary-based fuzzing strategies
- Successful debugging of crash conditions using gdb-multiarch
2.3. Limitations and Challenges
Several factors influenced the assessment outcomes:
- Limited computational resources in the virtual environment
- Performance impact on fuzzing effectiveness
- Complexity in adapting network services for fuzzing
- Absence of grammar-aware mutation engines
- Limited access to physical hardware for testing