Talos Vulnerability Report

TALOS-2018-0619

TP-Link TL-R600VPN HTTP server ping address remote code execution vulnerability

November 19, 2018
CVE Number

CVE-2018-3950

Summary

An exploitable remote code execution vulnerability exists in the ping and tracert functionality of the TP-Link TL-R600VPN http server. A specially crafted IP address can cause a stack overflow, resulting in remote code execution. An attacker can send a single authenticated HTTP request to trigger this vulnerability.

Tested Versions

TP-Link TL-R600VPN HWv3 FRNv1.3.0 TP-Link TL-R600VPN HWv2 FRNv1.2.3

Product URLs

https://www.tp-link.com/us/products/details/cat-4909_TL-R600VPN.html

CVSSv3 Score

7.2 - CVSS:3.0/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H

CWE

CWE-120 - Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')

Details

The TP-Link r600vpn does not properly check the size of the data passed to its 'ping_addr' field when performing a ping operation. By sending a large amount of data to this field, it is possible to overflow the stack buffer, causing the device's HTTP server to crash. It is further possible to craft the data passed through the 'ping_addr' field to exploit this vulnerability and gain remote code execution on the device.

[Annotated Disassembly / Decompilation output] The user-supplied ping address is obtained from the environment variable "ping_addr" and placed into the register $s4 toward the beginning of the function PingIframeRpmHtm.

LOAD:004773D0 loc_4773D0:
LOAD:004773D0                 la      $a1, dword_520000
LOAD:004773D4                 move    $a0, $s2
LOAD:004773D8                 la      $t9, httpGetEnv
LOAD:004773DC                 jalr    $t9 ; httpGetEnv
LOAD:004773E0                 addiu   $a1, (aPingAddr - 0x520000)  # "ping_addr"
LOAD:004773E4                 move    $a0, $s2
LOAD:004773E8                 lw      $gp, 0x78+var_60($sp)
LOAD:004773EC                 move    $s4, $v0                     # holds the user-supplied buffer

The $s4 register is not set again before a call to the function ipAddrDispose, where it is passed as the first argument.

LOAD:004777B8                 move    $a0, $s4
LOAD:004777BC                 lw      $gp, 0x78+var_60($sp)
LOAD:004777C0                 la      $t9, ipAddrDispose
LOAD:004777C4                 jalr    $t9 ; ipAddrDispose
LOAD:004777C8                 move    $s1, $v0

The $a0 register is then moved into the $s2 register for a strlen call in ipAddrDispose. During this same instruction, we see the register $s3 being loaded with a stack address.

LOAD:0049B098                 move    $s0, $zero
LOAD:0049B09C                 la      $t9, strlen
LOAD:0049B0A0                 move    $s2, $a0
LOAD:0049B0A4                 jalr    $t9 ; strlen
LOAD:0049B0A8                 addiu   $s3, $sp, 0x78+var_50

A loop is then entered where each character is loaded into a stack-based buffer one by one.

LOAD:0049B0D4                 li      $a3, 0x20
LOAD:0049B0D8                 addiu   $a2, $sp, 0x78+var_60        # load a stack pointer into $a2
LOAD:0049B0DC                 addu    $v0, $s2, $a0
LOAD:0049B0E0
LOAD:0049B0E0 loc_49B0E0:
LOAD:0049B0E0                 addiu   $a0, 1
LOAD:0049B0E4                 lbu     $v0, 0($v0)                  
LOAD:0049B0E8                 slt     $a1, $a0, $s1
LOAD:0049B0EC                 beq     $v0, $a3, loc_49B0FC
LOAD:0049B0F0                 addu    $v1, $s0, $a2                # calculate an offset on the stack buffer
LOAD:0049B0F4                 addiu   $s0, 1
LOAD:0049B0F8                 sb      $v0, 0x10($v1)               # write the byte to the stack buffer
LOAD:0049B0FC
LOAD:0049B0FC loc_49B0FC:
LOAD:0049B0FC                 bnez    $a1, loc_49B0E0
LOAD:0049B100                 addu    $v0, $s2, $a0
LOAD:0049B104

When the function epilogue is entered, the values for $ra and $s0-$s3 are overwritten with the values from the user-controlled stack buffer.

LOAD:0049B2FC loc_49B2FC:
LOAD:0049B2FC
LOAD:0049B2FC                 lw      $ra, 0x78+var_8($sp)
LOAD:0049B300                 move    $v0, $v1
LOAD:0049B304                 lw      $s3, 0x78+var_C($sp)
LOAD:0049B308                 lw      $s2, 0x78+var_10($sp)
LOAD:0049B30C                 lw      $s1, 0x78+var_14($sp)
LOAD:0049B310                 lw      $s0, 0x78+var_18($sp)
LOAD:0049B314                 jr      $ra
LOAD:0049B318                 addiu   $sp, 0x78
LOAD:0049B318  # End of function ipAddrDispose

Crash Information

[   18.484000] do_page_fault() #2: sending SIGSEGV to httpd for invalid read access from
[   18.484000] 42424242 (epc == 42424242, ra == 42424242)
[   18.508000] Cpu 0
[   18.520000] $ 0   : 00000000 1000a400 00000000 00000000
[   18.528000] $ 4   : 0067bc68 00000000 00000000 00000000
[   18.532000] $ 8   : 00000434 00000001 00000001 00000007
[   18.540000] $12   : 00000003 00000000 00000002 00000003
[   18.544000] $16   : 41414141 41414141 41414141 41414141
[   18.548000] $20   : 0067bc68 00520000 00000040 00000004
[   18.560000] $24   : 8f055f08 004be740                  
[   18.568000] $28   : 00598790 7d9ff8e0 7d9ffa62 42424242
[   18.572000] Hi    : 00000005
[   18.576000] Lo    : 00000000
[   18.576000] epc   : 42424242 0x42424242
[   18.584000]     Not tainted
[   18.584000] ra    : 42424242 0x42424242
[   18.588000] Status: 0000a413    USER EXL IE 
[   18.592000] Cause : 10800008
[   18.600000] BadVA : 42424242
[   18.608000] PrId  : 00019300 (MIPS 24Kc)
[   18.608000] Modules linked in:
[   18.612000] Process httpd (pid: 9668, threadinfo=8f0cc000, task=8f07e4c0, tls=00000000)
[   18.624000] Stack : 43434343 43434343 43434343 43434343 43434343 43434343 43434343 43434343
[   18.628000]         43434343 43434343 43434343 43434343 43434343 43434343 43434343 43434343
[   18.644000]         43434343 43434343 43434343 43434343 43434343 43434343 43434343 43434343
[   18.652000]         43434343 43434343 43434343 43434343 43434343 43434343 43434343 43434343
[   18.660000]         43434343 43434343 43434343 43434343 43434343 43434343 43434343 43434343
[   18.668000]         ...
[   18.672000] Call Trace:
[   18.676000] 
[   18.684000] 
[   18.684000] Code: (Bad address in epc)
[   18.692000] 
[   18.700000] httpd/9668: potentially unexpected fatal signal 11.
[   18.708000] 
[   18.708000] Cpu 0
[   18.716000] $ 0   : 00000000 1000a400 00000000 00000000
[   18.732000] $ 4   : 0067bc68 00000000 00000000 00000000
[   18.736000] $ 8   : 00000434 00000001 00000001 00000007
[   18.744000] $12   : 00000003 00000000 00000002 00000003
[   18.748000] $16   : 41414141 41414141 41414141 41414141
[   18.752000] $20   : 0067bc68 00520000 00000040 00000004
[   18.764000] $24   : 8f055f08 004be740                  
[   18.772000] $28   : 00598790 7d9ff8e0 7d9ffa62 42424242
[   18.776000] Hi    : 00000005
[   18.780000] Lo    : 00000000
[   18.780000] epc   : 42424242 0x42424242
[   18.784000]     Not tainted
[   18.784000] ra    : 42424242 0x42424242
[   18.788000] Status: 0000a413    USER EXL IE 
[   18.792000] Cause : 10800008
[   18.792000] BadVA : 42424242
[   18.796000] PrId  : 00019300 (MIPS 24Kc)

Timeline

2018-06-28 - Vendor Disclosure
2018-10-09 - Vendor provided beta
2018-10-11 - Patch tested and confirmed fix
2018-11-19 - Public Release

Credit

Discovered by Jared Rittle of Cisco Talos