Information Goldmine PowerShell
In the previous section,
we discussed how we can use sensitive information
found in plain-text files to elevate our privileges.
Over the last decade,
IT security awareness for the average enterprise user
has tremendously improved through training,
IT policies,
and the prevalent threat of cyber attacks painted by media.
Fortunately,
this has led to less sensitive information being stored in notes or text files.
Because of the growing threat of cyber attacks,
more defensive measures were developed and implemented for clients and servers alike.
One of these measures is to collect and record
more data on systems about executed commands and operations,
which allows the IT staff to review and respond accordingly to threats.
One important source of this information is PowerShell,
since it is a vital resource for attackers.
With default settings,
Windows only logs a small amount of information on PowerShell usage,
which is not sufficient for enterprise environments.
Therefore,
we’ll often find PowerShell logging mechanisms enabled
on Windows clients and servers.
Two important logging mechanisms for PowerShell
are PowerShell Transcription and PowerShell Script Block Logging.
Transcription is often referred to as “over-the-shoulder-transcription”,
because, when enabled,
the logged information is equal to what a person would obtain
from looking over the shoulder of a user entering commands in PowerShell.
The information is stored in transcript files,
which are often saved in the home directories of users,
a central directory for all users of a machine,
or a network share collecting the files from all configured machines.
Script Block Logging records commands
and blocks of script code as events while executing.
This results in a much broader logging of information
because it records the full content of code and commands as they are executed.
This means such an event
also contains the original representation of encoded code or commands.
Both mechanisms are quite powerful
and have become increasingly common in enterprise environments.
However, while they are great from the defensive perspective,
they often contain valuable information for attackers.
In this example,
we’ll demonstrate how we can retrieve information recorded by PowerShell
with the help of enabled logging mechanisms and the PowerShell history.
We’ll again connect on port 4444
to the bind shell running as the user dave and launch PowerShell.
For the purpose of this demonstration,
we’ll assume that the files containing sensitive
from the previous section don’t exist.
Before we check if Script Block Logging or PowerShell Transcription is enabled,
we should always check the PowerShell history of a user.
We can use the Get-History Cmdlet
to obtain a list of commands executed in the past.
The output indicates that no PowerShell commands were issued so far.
At this point, we have to explore how PowerShell’s history works.
Most Administrators use the Clear-History command to clear the PowerShell history.
But this Cmdlet is only clearing PowerShell’s own history,
which can be retrieved with Get-History.
Starting with PowerShell v5, v5.1, and v7,
a module named PSReadline is included,
which is used for line-editing and command history functionality.
Interestingly,
Clear-History does not clear the command history recorded by PSReadline.
Therefore, we can check if the user in our example
misunderstood the Clear-History Cmdlet
to clear all traces of previous commands.
To retrieve the history from PSReadline,
we can use Get-PSReadlineOption
to obtain information from the PSReadline module.
We put it in parentheses and add HistorySavePath prepended with a dot.
This syntax allows us to get only one option
from all available options of the module.
The results show us the path of the history file
from PSReadline.
Let’s display the contents of the file.
The output contains various highly interesting commands for us.
First,
dave executed Register-SecretVault
with the module SecretManagement.keepass,
which implies that the user created a new password manager database for KeePass.
In the next line,
dave used Set-Secret to create a secret,
or entry,
in the password manager with the name Server02 Admin PW
and password paperEarMonitor33@.
As the name suggests,
these are probably credentials for another system.
However, we should attempt to leverage this password for any user,
service, or login on CLIENTWK220
as it may be reused by the user.
For now, we’ll note the password for later and continue analyzing the history.
Next, the output shows that dave used Clear-History
believing that the history is cleared after executing the Cmdlet.
Finally, dave used Start-Transcript to start a PowerShell Transcription.
This command contains the path where the transcript file is stored.
Before we examine it, let’s also analyze the next line.
The user executed Enter-PSSession with the hostname of the local machine
as argument for -ComputerName
and a PSCredential object named $cred
containing the username and password for -Credential.
The commands to create the PSCredential object
are not included in the history file
and therefore we don’t know which user and password were used for Enter-PSSession.
Therefore, a user needs to be in the Windows Management Users local group
to be a valid user for these Cmdlets.
However, instead of WinRM,
SSH can also be used for PowerShell remoting.
Let’s analyze the transcript file
and check if we can shed more light on the user and password in use.
Since the PowerShell Transcription started before Enter-PSSession was entered,
it may contain the plain-text credential information
used to create the PSCredential object stored in the variable $cred.
The transcript file indeed contains the commands used to create
the variable $cred,
which were missing in the history file.
To create the previously discussed PSCredential object,
a user first needs to create a SecureString to store the password.
Then, the user can create the PSCredential object
with the username and the stored password.
The resulting variable, containing the obj
can be used as argument for -Credential in commands such as Enter-PSSession.
Let’s copy the three highlighted commands and paste it into our bind shell.
The output shows that we could use these three commands
to start a PowerShell remoting session via WinRM
on CLIENTWK220 as the user daveadmin.
While whoami works, other commands do not.
We received no output from the commands we entered.
We should note that creating a PowerShell remoting session
via WinRM in a bind shell like we used in this example
can cause unexpected behavior.
To avoid any issues, let’s use evil-winrm
to connect to CLIENTWK220 via WinRM from our Kali machine instead.
This tool provides various built-in functions for penetration testing
such as pass the hash,
inmemory loading and file upload/download.
However, we’ll only use it to connect to the target system via WinRM
to avoid the issues we faced by creating a PowerShell remoting session
in our bind shell.
We’ll enter the IP as argument for -i,
the username for -u,
and the password for -p.
We need to escape both “!”s in the password.
We can now execute commands without any issues.
Great!
PowerShell artifacts such as the history file of PSReadline or transcript files
are often a treasure trove of valuable information.
We should never skip reviewing them,
because most Administrators clear their history with Clear-History and therefore,
the PSReadline history stays untouched for us to analyze.
Administrators can prevent PSReadline from recording commands
by setting the -HistorySaveStyle option
to SaveNothing
with the Set-PSReadlineOption Cmdlet.
Alternatively,
they can clear the history file manually.
In this section, we explored PowerShell Transcriptions
as powerful mechanisms to record commands and scripts in PowerShell.
In addition, we discussed how and where PowerShell saves its history.
We used this knowledge in an example to obtain a password for another system
as well as the password for the administrative user daveadmin.