Parsing Mimikatz Log Files

Overview

On internal penetration tests and simulated attacks, mimikatz (or one of its derivatives) usually forms part of the standard toolkit. It has a huge number of features but perhaps the most common is the logonPasswords verb, which effectively dumps cleartext passwords from all of the usual locations. It usually displays output in the form:

* Username :
* Domain :
* Password :

Although some tools will parse this output, there are occasions in which it is useful to be able to parse several files which may include mimikatz output, perhaps interleaved with other output. Examples include the output from mimikatz when used with a LSASS memory dump file or parsing raw output from a range of RATs or shells which may not include built-in mimikatz parsing functionality. My solution to this problem was to write a relatively quick tool which uses regular expressions to locate interesting mimikatz output from streamed input and inserts it into a SQLite database. This has the advantage of being able to be quickly searched, joined with other reference material (e.g. the ADOffline post, which could allow you to quickly correlate compromised credentials with the groups/access levels that these credentials would provide access to), or simply exported in a variety of other formats.

Location

The source code can be obtained at https://github.com/stufus/parse-mimikatz-log or by cloning the git repository.

git clone https://github.com/stufus/parse-mimikatz-log.git

Usage

It is a commandline tool which accepts two switches, each of which requires a parameter:

SwitchExplanation
-dSpecifies the database file to use.
If it exists, the assumption is that the schema is correct.
If it doesn't exist, it will be created.
If this parameter is not specified, a temporary file will be created.
-iThe mimikatz output file to use.
If - is passed to it as a parameter, STDIN will be used.

Database

The database itself has only one table - creds - which stores the credentials which have been imported.

FieldExplanation
domainThe domain that the credentials are relevant for.
This is captured from the '* Domain:' part of the log file.
usernameThe username, captured from the '* Username:' part of the log file. This is always the first part of the credential block that appears in the log file.
passwordThe cleartext password which has been recovered, captured from the '* Password:' part of the credential block.
ntlmThe NTLM hash which has been recovered, captured from the '* NTLM:' part of the credential block.
sha1The SHA1 hash which has been recovered, captured from the '* SHA1:' part of the credential block.

There is also a view (view_usercreds) which comprises username, domain and password and excludes all usernames with a trailing dollar (which would designate a computer account).

Example

Populating the database

One option is to use a command to cat the entire log file to screen and read from STDIN:

$ find ./ -name \*.log -exec cat {} \; | pml.py -d /tmp/passwords.db -i -

.mMMMMMm. MMm M WW W WW RRRRR
mMMMMMMMMMMM. MM MM W W W R R
/MMMM- -MM. MM MM W W W R R
/MMM. _ \/ ^ M M M M W W W W RRRR
|M. aRRr /W| M M M M W W W W R R
\/ .. ^^^ wWWW| M M M W W R R
/WW\. .wWWWW/ M M M W W R R
|WWWWWWWWWWW/
.WWWWWW. Quick & Dirty Mimikatz Log Parser
stuart.morgan@mwrinfosecurity.com | @ukstufus

Opening database: /tmp/passwords.db
Reading from STDIN
Processing line 236676/236676 (100%)

Sets of credentials: 949
Unique 'user' usernames: 355
Unique 'user' passwords: 278

An alternative is to find all log files and run it individually for each one:

find ./ -name \*.log -exec ./pml.py -d /tmp/passwords.db -i {} \;

Extracting credentials

The database needs to be loaded into SQLite:

sqlite3 /tmp/tmpyIJeAn.20161011103852.mimikatz.db
SQLite version 3.14.1 2016-08-11 18:53:32
Enter ".help" for usage hints.
sqlite>

All user credentials can be listed:

sqlite> select * from view_usercreds;

The credentials could be exported in a username:password format ('||' is the concatenation operator in SQLite and '.once' writes the output to a file):

sqlite> .once /tmp/credentials.txt
sqlite> select distinct username||':'||password from view_usercreds;

The credentials could also be exported in a DOMAIN\username:password format using the same technique as above:

sqlite> select distinct domain||'\'||username||':'||password from view_usercreds;

The number of users sharing the same password could be found by a query such as the below, which would display each password and the number of users with that password:

sqlite> select password,count(username) from view_usercreds group by password;