During an internal penetration test earlier this year, we got ourselves access to a couple of Windows workstations. Fully patched Windows 10 machines, from which we had to try and find a way to escalate our privileges to a user with higher privileges.
Our goal was to escalate our access all the way to Domain Admin, so that we could achieve the objective for this project as set out by the customer. We tried working our way through the internal network by identifying alive hosts, enumerating potential exploitable network services and obtaining credentials but with no luck. We found ourselves stuck on this one host, so decided to dive all the way into the rabbit hole.
This story is based on an internal pentest, but screenshots, names and references are taken from our research lab.
The first step was to gather Active Directory data using Bloodhound and try to find attack paths we would be able to infiltrate. During the enumeration and exploitation phases we landed on a vulnerable Windows machine. From this machine we were able to extract the SAM and retrieve the password NLTM hash for various users. Using a derivative local admin password hash, we found our way to a Windows workstation that was part of the domain and appeared to be extremely well protected, prohibiting us to execute even using CMD or running PowerShell scripts.
Having nothing special in our hands to continue, we took a step back and started digging around for something that we may have missed during enumeration. Further down the rabbit hole we go...
Making use of the valid users’ credentials that we discovered and extracted from various vulnerable workstations, our first objective was to identify the nature of the Windows machine (ARROW-Win10) and try to find out what the system is running on and what we might be able to leverage in further attacks. We started with verifying our credentials and checking if we can access the Windows machine through SMB protocol and what kind of permissions our user has.
Unfortunately, our user was not able to access the C$ drive which may mean that the user is not part of the local group Administrators or extra security layers are applied on top through local GPO policies. Taking into consideration the outcome of Bloodhound we noticed that a certain user can RDP to the remote host.
In a Windows environment, each domain and local user, group and other security objects are assigned to a unique identifier - Security Identifier called SID. A security identifier is a unique value of variable length that is used to identify a security principal (such as a security group) in Windows operating systems and is used to control access to different resources such as network shared folders, registry keys, file system objects, etc.
There are multiple ways to convert the above SID to an actual username. For our convenience and as we are having access to a workstation joined to the domain, we used the following PowerShell commands to convert the SID:
$objSID = New-Object System.Security.Principal.SecurityIdentifier("S-1-5-21-2914800294-3420094760-3837107029-1006")
$objSID.Translate([System.Security.Principal.NTAccount]).Value
Taking advantage of the above information, we managed to get access to the remote workstation (ARROW-Win10) through RDP with the Pass-The-Hash technique by running the following command from our Kali machine.
xfreerdp /u:aliceland /d:. /pth:9a1a8b1dc3a4996ffa48b6e9a617b6cc /v:ARROW-Win10
Pass-the-Hash technique involves a user logging in using a username and password hash instead of a password in clear text format. Microsoft, in their quest to mitigate the “Pass-the-Hash” techniques, introduced the “Restricted Admin” mode which allows admins to easily connect to remote systems to locally administer them without having to worry about exposing their credentials to that weaker/less secure system.
Being able to access the affected workstation using the remote desktop through Restricted Admin mode, we established that our user has Administrator privileges on the remote host. As described in MS KB article 951916, Microsoft introduced this feature as part of UAC. Although this might be a little-known feature, UAC remote restrictions effectively filters the access token for connections made with local user accounts or Microsoft accounts. As described by Microsoft:
When a local user who is a member of the local Administrators group on the target remote computer establishes a remote administrative connection they won't connect as a full administrator. The user has no elevation potential on the remote computer, and the user cannot perform administrative tasks. If the user wants to administer the workstation with a Security Account Manager (SAM) account, the user must interactively log on to the computer that is to be administered with Remote Assistance or Remote Desktop, if these services are available.
This means we had access to ARROW-Win10 Workstation through RDP, but we are severely limited. So, we need to look for alternative ways to expand our privileges.
Our next attempt was to execute Mimikatz so we could dump the memory and retrieve any valuable information. Unfortunately, local security policy was enforced prohibiting us to debug privileges and interact with the LSASS etc even using the local administrator user. Bypassing the AppLocker policy using the MsBuild technique, also did not get us any further.
Note: Having admin privileges we had the opportunity to enable the execution of Mimikatz, but as this requires a reboot to take effect, we may lose data that is stored in memory.
Then we went for something truly magical: Credential Manager! Credential Manager is the "digital vault" where Windows stores log-in credentials such as usernames, passwords, and network addresses. This information can be saved by Windows for use on your local computer, on other computers in the same network, servers, or Internet locations. This data can be used by Windows itself or by apps and programs. Credentials are split into several categories, such as Windows credentials, generic credentials, and certificate credentials. In this blogpost, our focus will be the first category: Windows credentials.
Windows credentials are used only by Windows and its services. For example, Windows can use these credentials to automatically log you into the shared folders of another computer on your network. It can also store the password of the Homegroup you have joined and uses it automatically each time you access what is being shared in that particular Homegroup.
Reviewing the Credential Manager window shown below, we quickly realized that this may be our golden ticket to getting Domain Admin or at least another step of privilege escalation.
As we can’t run our trusty tools Mimikatz, nor on its more obscure cousins Pypikatz, we had to find another way to abuse this built-in goldmine called Windows Credentials. As the user is part of the Domain and part of a group with interesting (read: elevated) permissions... We were determined to not let this opportunity pass by unused!
We attempted to recon the Active Directory for that user (puppet_user) using ADSI (Active Directory Service Interfaces) without triggering AMSI or leaving artifacts in the event logger of the compromised host.
Active Directory Service Interfaces (ADSI) is a set of COM interfaces used to access the features of directory services from different network providers. It works by abstracting the capabilities of directory services to present a single set of interfaces for managing network resources in a distributed computing network. LDAP query examples can be found under the following links (ldapwiki, selfADSI).
([ADSISearcher]'(&(objectCategory=user)(memberOf=CN=SQLGroup,OU=Users,OU=Lab,DC=flame,DC=delos,DC=lab))').FindAll() | foreach {[ADSI]$_.path} | Select -Property sAMAccountName,memberOf | Format-List
One (legitimate) way to reveal Windows Credential information through command lines is to use the cmdkey command and running the following to list all stored credentials on the local machine’s Credential Manager:
cmdkey /list
Great, we see through the commandline and the information we want gets revealed... But how do we take advantage of this? How can we reveal the actual credentials or otherwise re-use the information that is dangling right in front of us?
Digging around further, we discovered that you can indeed use the command option runas /savecred and specify the user. In doing so, Windows will use the credentials that are stored for that user in Credentials Manager. Using the saved user credentials, along with our MSBuild command allow us to bypass the AppLocker restrictions policy and connect to the remote host.
runas /savecred /user:FLAME\puppet_user "C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe C:\Temp\MSBuildPS.csproj"
Get-WmiObject -Class win32_operatingsystem -ComputerName ORCHID-SQL
The above information was a crucial stepping stone for our red team engagement and at the end of this project would eventually give us access to the Workstation where “puppet_user” had local administrative access and the SQL Server is installed. This gave us not only valuable information, but also allowed us to escalate our privileges by simply abusing built in Windows tooling and slip between the cracks in an otherwise secure configuration and laterally move through to other systems in further pursuit of our objectives.
Sqldumper.exe is an existing (and trusted) Microsoft tool that can be used to generate a dump file on demand for any Microsoft Windows application. For example, we can generate a dump file for debugging an application when a computer that is running a SQL Server Application is not responding. A dump file can be a mini-dump file, a full dump file, or a filtered dump file. Taking advantage of this information, we established a PowerShell remote connection to the Server where "puppet_user" had local admin rights and SQL instance was running on it.
PS C:\WINDOWS\system32> $session = New-Pssession -ComputerName ORCHID-SQL
PS C:\WINDOWS\system32> Enter-PSSession -Session $session
To generate a specific kind of a dump file, we needed to type the corresponding command followed by the process ID and the DumpType. Using the 0x01100:40 flag whichcreates a Mimikatz compatible dump file, for example:
sqldumper.exe <PID> 0 0x01100:40
After having established a PSRemote session using the "puppet_user" account, we used the SQLDumper tool to dump the LSASS process. Then by invoking Mimikatz and using the dumped file on our local machine, we can extract the dumped info, which included the SQLAdmin’s NTLM hash.
PS C:\Temp> Invoke-Command -FilePath .\Invoke-Mimikatz.ps1 -Session $session
PS C:\Temp> Enter-PSSession -Session $session
[ORCHID-SQL]: PS C:\Temp> Invoke-Mimikatz -Command '"sekurlsa::minidump C:\Temp\SQLDmpr0001.mdmp" "sekurlsa::logonPasswords full"'
.#####. mimikatz 2.1.1 (x64) built on Nov 29 2018 12:37:56
.## ^ ##. "A La Vie, A L'Amour" - (oe.eo) ** Kitten Edition **
## / \ ## /*** Benjamin DELPY `gentilkiwi` ( [email protected] )
## \ / ## > http://blog.gentilkiwi.com/mimikatz
'## v ##' Vincent LE TOUX ( [email protected] )
'#####' > http://pingcastle.com / http://mysmartlogon.com ***/
mimikatz(powershell) # sekurlsa::minidump C:\Temp\SQLDmpr0001.mdmp
Switch to MINIDUMP : 'C:\Temp\SQLDmpr0001.mdmp'
mimikatz(powershell) # sekurlsa::logonPasswords full
Opening : 'C:\Temp\SQLDmpr0001.mdmp' file for minidump...
<snip…>
Authentication Id : 0 ; 2119563 (00000000:0020578b)
Session : Interactive from 0
User Name : SQLAdmin
>Domain : FLAME
Logon Server : DHARMA-DC
Logon Time : 3/30/2021 3:04:39 PM
SID : S-1-5-21-3814968796-4193774654-733324460-1109
msv :
[00000003] Primary
* Username : sqladmin
* Domain : FLAME
* NTLM : 91ff8d675a9a348b6416b7c76bc5ba8e
* SHA1 : 90ef8dcb01e594c22c7adbcf62b438ef6ff4d6fd
* DPAPI : ca5425a189a847f19e992dc8f95396af
<…snip>
The nature of the ORCHID-SQL server was to perform the AzureAD synchronization and handle Windows updates through WSUS configuration. Having all this information in the table we started digging into the server to find any useful information for our next step. Furthermore, checking closely the Bloodhound queries we made to the following results which indicates that the internal infrastructure is Azure Domain joined.
Azure AD Connect creates the account, an account is created that follows the naming convention resulting in a name starting with MSOL_, followed by the first 8 bytes of the Azure AD Connect installation ID (a version 4 UUID) and the server name. It is placed in the Users container per Active Directory domain in scope.
Since we had admin rights in the Windows machine, we were able to retrieve the Azure access token(s) for the user "sqladmin"
The next step was to make use of the Azure token(s) and connect to the company’s tenant to extract any valuable information if possible.
We copied the files “TokenCache.dat” and “AzureRmContext.json” back to our Windows machine and by adding the Az module we had to manipulate the stolen .dat and .json file so we can import them into our PowerShell session.
PS C:\Temp> $token_bytes = Get-Content "C:\Users\labuser.FLAME\Desktop\TokenCache.dat" -Encoding byte
PS C:\Temp> $token_b64 = [Convert]::ToBase64String($token_bytes)
PS C:\Temp> Add-Content "C:\Temp\Azure-token.txt" $token_b64
To accomplish this, we had to add the content of the Azure-token.txt into the AzureRmContext.json file by replacing the null string from the “CachData” option and by running the following command to import the new token:
PS C:\Temp> Import-AzContext -Profile "C:\Temp\AzureRmContext.json"
Since we had successfully connected to the company Azure tenant, this gave us further information to continue our escalation and find any possible way to compromise the entire infrastructure by also using the Azure services.
The above story is a short story about an attack path we have uncovered during a recent Red team assessment. The purpose of this story is to introduce different attack techniques and tools that can be used to bypass AppLocker restrictions, escalate privileges either by derivate local admin credentials or reusing the Credentials Managers credentials and by taking advantage of the trusted built-in tools to dump the essential processes and extract stored and Azure info.