Living Off The Land - Built-In Pwning

Common examples of these tools include CMD, PowerShell, WMI, and other built-in administrative tools that can be leveraged to enumerate and exploit vulnerabilities across a system or domain.

Adversaries can use this approach to help avoid detection by security solutions such as anti-virus (AV) and endpoint detection and response (EDR), which typically focus on detecting more obvious threats. By using existing and legitimate tools, adversaries can attempt to blend in with regular user activity and evade detection.

The phrase 'PowerShell is dead' has been coined for years, but even with protections enabled, it is still very much alive in many environments. PowerShell is a robust command-line shell and scripting language that can interact with Windows Management Instrumentation(WMI) on Windows-based computers and other modules. PowerShell provides several cmdlets and modules that enable administrators to query WMI, retrieve information, and perform various management tasks.

The following lab was created to demonstrate some easy techniques for quick enumeration wins:

Domain - hacklab.local
Domain Controller - Microsoft Windows Server 2022 10.0.20348 N/A Build 20348
Domain Host 1 - Microsoft Windows 11 Enterprise 10.0.22621 N/A Build 22621
Domain Host 2 - Microsoft Windows 11 Enterprise 10.0.22621 N/A Build 22621

Get-CimInstance

Get-CimInstance is a PowerShell cmdlet which was introduced in Windows PowerShell version 3.0. It can be used to retrieve information about a host or domain. Still, more specifically, it allows you to retrieve information about one or more instances of a particular class from a CIM (Common Information Model) repository.

In other words, Get-CimInstance enables you to query a system's CIM repository to retrieve a wide range of system information, including hardware configurations, installed software, and network settings. It is similar to the older Get-WmiObject cmdlet but uses, the newer CIM infrastructure instead of WMI.

Combining the Get-CimInstance with a Windows Management Instrumentation (WMI) class such as Win32_GroupInDomain, can be used to retrieve detailed information such as all domain groups or a specific group that is a member of a domain. This can be useful when enumerating users' access and permissions across a Windows domain.

Domain Group Enumeration

PS C:\Users\user1> Get-CimInstance -ClassName Win32_GroupInDomain | Where-Object {$_.GroupComponent -match "Win32_NTDomain"} | Format-Table PartComponent -AutoSize

PartComponent
-------------
Win32_Group (Name = "Cert Publishers", Domain = "HACKLAB")
Win32_Group (Name = "RAS and IAS Servers", Domain = "HACKLAB")
Win32_Group (Name = "Allowed RODC Password Replication Group..., Domain = "HACKLAB")
Win32_Group (Name = "Denied RODC Password Replication Group", Domain = "HACKLAB")
Win32_Group (Name = "DnsAdmins", Domain = "HACKLAB")
Win32_Group (Name = "accounts", Domain = "HACKLAB")
Win32_Group (Name = "administration", Domain = "HACKLAB")
Win32_Group (Name = "Cloneable Domain Controllers", Domain = "HACKLAB")
Win32_Group (Name = "DnsUpdateProxy", Domain = "HACKLAB")
Win32_Group (Name = "Domain Admins", Domain = "HACKLAB")
Win32_Group (Name = "Domain Computers", Domain = "HACKLAB")
Win32_Group (Name = "Domain Controllers", Domain = "HACKLAB")
Win32_Group (Name = "Domain Guests", Domain = "HACKLAB")
Win32_Group (Name = "Domain Users", Domain = "HACKLAB")
Win32_Group (Name = "Enterprise Admins", Domain = "HACKLAB")
Win32_Group (Name = "Enterprise Key Admins", Domain = "HACKLAB")
Win32_Group (Name = "Enterprise Read-only Domain Controllers..., Domain = "HACKLAB")
Win32_Group (Name = "Group Policy Creator Owners", Domain = "HACKLAB")
Win32_Group (Name = "help_desk", Domain = "HACKLAB")
Win32_Group (Name = "Key Admins", Domain = "HACKLAB")
Win32_Group (Name = "Protected Users", Domain = "HACKLAB")
Win32_Group (Name = "RDP", Domain = "HACKLAB")
Win32_Group (Name = "Read-only Domain Controllers", Domain = "HACKLAB")
Win32_Group (Name = "sales", Domain = "HACKLAB")
Win32_Group (Name = "Schema Admins", Domain = "HACKLAB")
Win32_Group (Name = "support", Domain = "HACKLAB")

Domain User Account Enumeration

PS C:\Users\user1> Get-CimInstance -ClassName Win32_UserAccount | Format-Table -AutoSize

Name               Caption                            AccountType SID                                            Domain
----               -------                            ----------- ---                                            ------
admin              DESKTOP-UH58RJD\admin              512         S-1-5-21-24335520-1574975053-1288484576-1001   DESKTOP-UH58RJD
Administrator      DESKTOP-UH58RJD\Administrator      512         S-1-5-21-24335520-1574975053-1288484576-500    DESKTOP-UH58RJD
DefaultAccount     DESKTOP-UH58RJD\DefaultAccount     512         S-1-5-21-24335520-1574975053-1288484576-503    DESKTOP-UH58RJD
Guest              DESKTOP-UH58RJD\Guest              512         S-1-5-21-24335520-1574975053-1288484576-501    DESKTOP-UH58RJD
WDAGUtilityAccount DESKTOP-UH58RJD\WDAGUtilityAccount 512         S-1-5-21-24335520-1574975053-1288484576-504    DESKTOP-UH58RJD
Administrator      HACKLAB\Administrator              512         S-1-5-21-2638285952-2988835435-2893458979-500  HACKLAB
Guest              HACKLAB\Guest                      512         S-1-5-21-2638285952-2988835435-2893458979-501  HACKLAB
krbtgt             HACKLAB\krbtgt                     512         S-1-5-21-2638285952-2988835435-2893458979-502  HACKLAB
n.collins          HACKLAB\n.collins                  512         S-1-5-21-2638285952-2988835435-2893458979-1109 HACKLAB
o.davidson         HACKLAB\o.davidson                 512         S-1-5-21-2638285952-2988835435-2893458979-1110 HACKLAB
p.davies           HACKLAB\p.davies                   512         S-1-5-21-2638285952-2988835435-2893458979-1111 HACKLAB
q.dawson           HACKLAB\q.dawson                   512         S-1-5-21-2638285952-2988835435-2893458979-1112 HACKLAB
u.dixon            HACKLAB\u.dixon                    512         S-1-5-21-2638285952-2988835435-2893458979-1113 HACKLAB
r.edwards          HACKLAB\r.edwards                  512         S-1-5-21-2638285952-2988835435-2893458979-1114 HACKLAB
s.elliot           HACKLAB\s.elliot                   512         S-1-5-21-2638285952-2988835435-2893458979-1115 HACKLAB
t.evans            HACKLAB\t.evans                    512         S-1-5-21-2638285952-2988835435-2893458979-1116 HACKLAB
u.fisher           HACKLAB\u.fisher                   512         S-1-5-21-2638285952-2988835435-2893458979-1117 HACKLAB
v.fletcher         HACKLAB\v.fletcher                 512         S-1-5-21-2638285952-2988835435-2893458979-1118 HACKLAB
w.ford             HACKLAB\w.ford                     512         S-1-5-21-2638285952-2988835435-2893458979-1119 HACKLAB
x.foster           HACKLAB\x.foster                   512         S-1-5-21-2638285952-2988835435-2893458979-1120 HACKLAB
y.fox              HACKLAB\y.fox                      512         S-1-5-21-2638285952-2988835435-2893458979-1121 HACKLAB
z.gibson           HACKLAB\z.gibson                   512         S-1-5-21-2638285952-2988835435-2893458979-1122 HACKLAB
a.graham           HACKLAB\a.graham                   512         S-1-5-21-2638285952-2988835435-2893458979-1123 HACKLAB
b.grant            HACKLAB\b.grant                    512         S-1-5-21-2638285952-2988835435-2893458979-1124 HACKLAB
c.gray             HACKLAB\c.gray                     512         S-1-5-21-2638285952-2988835435-2893458979-1125 HACKLAB
d.green            HACKLAB\d.green                    512         S-1-5-21-2638285952-2988835435-2893458979-1126 HACKLAB
m.jenkins          HACKLAB\m.jenkins                  512         S-1-5-21-2638285952-2988835435-2893458979-1127 HACKLAB
n.johnson          HACKLAB\n.johnson                  512         S-1-5-21-2638285952-2988835435-2893458979-1128 HACKLAB

A more exciting demonstration consists of using Get-CimInstance along with Win32_UserAccount to retrieve information about user accounts on the system combined with a Where-Object:

{ -not [string]::IsNullOrEmpty($_.Description) } 

This filters the output to include only instances where the description property is not null and not an empty string. In other words, only reveal domain accounts with a description attribute filled out; people sometimes put interesting notes or passwords in the description attribute.

PS C:\Users\user1> Get-CimInstance -ClassName Win32_UserAccount | Where-Object { -not [string]::IsNullOrEmpty($_.Description) } | Select-Object -Property Name, Description, Domain, SID

Name               Description                                                                                     Domain          SID
----               -----------                                                                                     ------          ---
Administrator      Built-in account for administering the computer/domain                                          DESKTOP-UH58RJD S-1-5-21-24335520-1574975053-1288484576-500
DefaultAccount     A user account managed by the system.                                                           DESKTOP-UH58RJD S-1-5-21-24335520-1574975053-1288484576-503
Guest              Built-in account for guest access to the computer/domain                                        DESKTOP-UH58RJD S-1-5-21-24335520-1574975053-1288484576-501
WDAGUtilityAccount A user account managed and used by the system for Windows Defender Application Guard scenarios. DESKTOP-UH58RJD S-1-5-21-24335520-1574975053-1288484576-504
Administrator      Built-in account for administering the computer/domain                                          HACKLAB         S-1-5-21-2638285952-2988835435-2893458979-500
Guest              Built-in account for guest access to the computer/domain                                        HACKLAB         S-1-5-21-2638285952-2988835435-2893458979-501
krbtgt             Key Distribution Center Service Account                                                         HACKLAB         S-1-5-21-2638285952-2988835435-2893458979-502
d.atkinson         User Password Summer123                                                                         HACKLAB         S-1-5-21-2638285952-2988835435-2893458979-1179

Another great tool that we use a lot at Lares is ADSI Searcher. ADSI (Active Directory Service Interfaces) Searcher is a programming interface used to search for and retrieve information from Active Directory, a directory service developed by Microsoft for Windows domain networks.

The ADSI Searcher provides a means to programmatically access and manipulate Active Directory objects, including user accounts, groups, organizational units, and other directory objects. It allows developers to perform searches based on criteria such as object type, attribute values, and other conditions.

The ADSI Searcher is often used in automation and scripting scenarios, such as automated user provisioning, account management, and other administrative tasks. It provides a powerful way to manage and automate large-scale Active Directory environments and thus a great avenue for attackers to live off the land.

Find where WinRM is enabled on all domain hosts.

([adsisearcher]"(&(objectCategory=computer)(objectClass=computer))").FindAll() | ForEach-Object {Test-NetConnection -ComputerName $_.Properties.name -Port 5985 -ErrorAction SilentlyContinue | Where-Object {$_.TcpTestSucceeded -eq $true} | Select-Object ComputerName}

Example output:

PS C:\Windows\system32> ([adsisearcher]"(&(objectCategory=computer)(objectClass=computer))").FindAll() | ForEach-Object {Test-NetConnection -ComputerName $_.Properties.name -Port 5985 -ErrorAction SilentlyContinue | Where-Object {$_.TcpTestSucceeded -eq $true} | Select-Object ComputerName}

WARNING: TCP connect to (fe80::5615:5a87:8d00:a170%5 : 5985) failed
WARNING: TCP connect to (192.168.68.113 : 5985) failed
ComputerName
------------
WIN-CH4HJ7UU7Q5
LAB_WIN11_1

The query above can be used for any port to perform password spraying too. The following queries are additional queries that can be used for enumeration that do not require additional installs or modules.

Query all Computers and Search for Local admin.

$searcher=[adsisearcher]'objectCategory=computer'; $searcher.PageSize = 1000; $computer_list = $searcher.FindAll() | ForEach-Object{$_.Properties.name}; foreach ($computer in $computer_list) { try { $path = "\\" + $computer + "\C$\"; if(Test-Path $path -ErrorAction SilentlyContinue) { $string = "[!] Local Admin to: " + $computer; Write-Host $string }} catch {}}
$computers = @((New-Object DirectoryServices.DirectorySearcher("(&(objectCategory=computer)(operatingSystem=Windows*))")).FindAll() | ForEach-Object { $_.Properties.name[0] })

$username = Read-Host -Prompt "Enter your username"
$password = Read-Host -Prompt "Enter your password" -AsSecureString
$credential = New-Object System.Management.Automation.PSCredential ($username, $password)

$computers | ForEach-Object {
    $computer = $_
    $scriptBlock = {
        Try {
            $smb = Get-SmbSession
            Write-Host "SMB service is accessible on $($env:COMPUTERNAME)" -ForegroundColor Green
        } Catch {
            $error_message = $_.Exception.Message
            if ($error_message -notlike "*The user name or password is incorrect.*") {
                Write-Host $error_message -ForegroundColor Red
            }
        }
    }
    Invoke-Command -ComputerName $computer -Credential $credential -ScriptBlock $scriptBlock -ErrorAction SilentlyContinue
}

Pull only active computers (lastlogon<31) (No test connection, with time throttle function (can be 0 for no time throttle))

$sleep_secs=0; $date_offset=-31; $searcher=[adsisearcher]"(&(objectCategory=computer)(lastlogon>=$((Get-Date).AddDays($date_offset).ToUniversalTime().ToFileTime())))";$searcher.PageSize = 1000; $computer_list = $searcher.FindAll() | ForEach-Object{$_.Properties.name}; $start_date=Get-Date; $info_source="`nCurrent Time: " + $start_date + "`nActive Workstations: " + $computer_list.count + "`nThrottle Seconds: " + $sleep_secs; Write-Host $info_source `n; foreach ($computer in $computer_list) { try { Start-Sleep -Seconds $sleep_secs; $path = "\\" + $computer + "\C$\"; if(Test-Path $path -ErrorAction SilentlyContinue) { $string = "`t[!] Admin to: " + $computer; Write-Host $string }} catch {}} $end_time=Get-Date; Write-Host "`nFinished! - " $end_time`n;

Thanks for reading this quick overview of living off the land; many modules can be used in Powershell with built-in tooling; if you want more examples, my colleague Andy and I have released more research here https://github.com/myexploit/living_off_the_land.