Firewall Piercing/Beispiele/OpenSSH via HTTPS-Proxy als SOCKS Proxy verwenden
Dieses Beispiel beschreibt den Ausbruch aus einem System, welches nur via HTTPS-Proxy auf das freie Internet zugreifen kann. Darüber hinaus soll die initiale SSH-Verbindung gleichzeitig einen SOCKS-Proxy anbieten, der dann für weitere SSH-Sessions verwendet werden kann.
Voraussetzungen für dieses Beispiel[Bearbeiten]
Das Rhein-Neckar-Wiki läuft in einem Jail auf dem Server der Stadtwiki Gesellschaft e.V. (rnw.stwserv.de). Die Wiki-Jails sind aus Sicherheitsgründen per Firewall von extern nur auf 80/tcp und 443/tcp erreichbar und können selbst nur über einen Proxy-Server (relay.stwserv.de) mit der Aussenwelt kommunizieren. Der Proxy-Server ist ein HTTP-Proxy und erlaubt zusätzlich die für HTTPS erforderliche CONNECT-Methode auf Port 443/tcp (https)
Ferner existiert ein Shell-Account auf shell.uugrn.org. Der dortige OpenSSH-Daemon läuft zusätzlich zum Standardport 22/tcp auch auf Port 443/tcp.
Es soll zu Demonstrationszwecken gezeigt werden, wie man unter diesen Voraussetzungen mit OpenSSH und BSD-netcat als Hilfstool eine SSH-Verbidung auf einen mehr oder weniger beliebigen Server im Internet herstellen kann, sofern man dort grundsätzlich Login-Rechte hat.
SSH-Client Konfiguration[Bearbeiten]
zumächst legen wir folgende ~/.ssh/config an (oder ergänzen die vorhandene entsprechend):
[rabe@rnw ~]$ cat .ssh/config Host shell443 HostName shell.uugrn.org Port 443 User rabe TCPKeepAlive yes DynamicForward 127.0.0.1:1080 ProxyCommand /usr/bin/nc -X connect -x relay.stwserv.de:3128 %h %p Host rabe.socks Hostname rabe.uugrn.org Port 22 User rabe ProxyCommand /usr/bin/nc -X 5 -x 127.0.0.1:1080 %h %p
Das Profil ssh443 ist so angelegt, dass der Benutzer rabe sich auf shell.uugrn.org auf Port 443/tcp einloggt. Dabei wird mit dem ProxyCommand eine HTTPS-Proxy-Connection über relay.stwserv.de:3128 aufgebaut. Die so aufgebaute SSH-Session soll ausserdem einen SOCKS-Proxy auf 127.0.0.1:1080 bereitstellen.
Das Profil rabe.socks ist so angelegt, dass es auf rabe@rabe.uugrn.org:22 einloggt unter Verwendung des SOCKS(5)-Proxys auf 127.0.0.1:1080.
Verbindungsaufbau[Bearbeiten]
- nach shell.uugrn.org via HTTPS-Proxy
[rabe@rnw ~]$ ssh -N -f shell443 The authenticity of host '[shell.uugrn.org]:443 (<no hostip for proxy command>)' can't be established. DSA key fingerprint is c4:2e:90:bc:76:b7:ca:6e:69:fd:54:c8:21:4c:97:b8. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '[shell.uugrn.org]:443' (DSA) to the list of known hosts. Password: [rabe@rnw ~]$
Durch die Optionen -N und -f sorgen wir dafür, dass nach dem Verbindungsaufbau auf dem Zielsystem kein Kommando ausgeführt wird und sich der SSH-Client als Daemon weg-forkt. Wir landen daher wieder auf unserer lokalen Shell.
- nach rabe.uugrn.org via SOCKS-Proxy
[rabe@rnw ~]$ ssh rabe.socks The authenticity of host 'rabe.uugrn.org (<no hostip for proxy command>)' can't be established. DSA key fingerprint is 5b:13:fa:16:78:93:be:e9:5c:84:33:d3:63:b6:c1:9f. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added 'rabe.uugrn.org' (DSA) to the list of known hosts. Password: [rabe@rabe ~]$
Dieser Verbindungsaufbau liefert uns eine normale, interaktive Shell auf rabe.uugrn.org.
- Spuren
- Im Proxy-Logfile auf relay.uugrn.org erscheint hier lediglich ein CONNECT auf shell.uugrn.org:443 und die Anzahl der übertragenen Bytes. Allerdings erst, wenn die Verbindung zu shell.uugrn.org:443 abgebaut wird:
1314483856.709 1579602 195.49.138.74 TCP_MISS/200 26199 CONNECT shell.uugrn.org:443 - DIRECT/195.49.138.100 -
- Aus Sicht von shell.uugrn.org erfolgt der SSH-Login von relay.stwserv.de aus, d.h. es ist nicht sichtbar, dass der SSH-Client in Wirklichkeit auf rnw.stwserv.de läuft.
- Aus Sicht von rabe.uugrn.org erfolgt der SSH-Login von shell.uugrn.org aus. Hier ist nichteinmal erkennbar, dass der Client ganz woanders läuft.
Ad-hoc[Bearbeiten]
Man will möglicherweise nicht für jeden einzelnen Zielrechner, auf den man per SSH ausbrechen will erst in der ~/.ssh/config anlegen. Die erforderlichen Direktiven kann man auch mit -o 'Direktive Parameter' als Kommandozeilenparameter für den OpenSSH-Client angeben.
- Beispiel
Es soll Ad-hoc unter Verwendung des SOCKS-Proxys auf sigsys.de zugegriffen werden und dort 'hostname ; echo $SSH_CLIENT' ausgeführt werden:
[rabe@rnw ~]$ ssh -o 'ProxyCommand /usr/bin/nc -X 5 -x 127.0.0.1:1080 %h %p' rabe@sigsys.de 'hostname ; echo $SSH_CLIENT' The authenticity of host 'sigsys.de (<no hostip for proxy command>)' can't be established. DSA key fingerprint is df:ff:2a:d7:af:4e:99:44:ad:0d:9f:81:28:96:83:de. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added 'sigsys.de' (DSA) to the list of known hosts. Password: sigsys.de 195.49.138.100 55726 22
- Zu sehen
- der Login erfolgt tatsächlich auf sigsys.de und laut $SSH_CLIENT kommt die SSH-Verbindung auch via shell.uugrn.org (195.49.138.100) rein.
- Nicht zu sehen
- Es gibt es keinen Rückschluss auf rnw.stwserv.de oder relay.stwserv.de bzw. dass der SSH Client über einen SOCKS-Proxy via shell.uugrn.org reinkommt.
SOCKS Proxy funktioniert nicht[Bearbeiten]
Versucht man einen nicht mehr vorhandenen SOCKS-Proxy zu verwenden, erhält man folgende Meldung:
ssh_exchange_identification: Connection closed by remote host
Schaut man genauer hin, findet man leider keine genaueren Infos, insbesondere nicht, dass netcat (nc) ein Problem hat:
[rabe@rnw ~]$ ssh -vvv -o 'ProxyCommand /usr/bin/nc -X 5 -x 127.0.0.1:1080 %h %p' rabe@sigsys.de 'hostname ; echo $SSH_CLIENT' OpenSSH_5.1p1 FreeBSD-20080901, OpenSSL 0.9.8e 23 Feb 2007 debug1: Reading configuration data /home/rabe/.ssh/config debug1: Reading configuration data /etc/ssh/ssh_config debug2: ssh_connect: needpriv 0 debug1: Executing proxy command: exec /usr/bin/nc -X 5 -x 127.0.0.1:1080 sigsys.de 22 debug1: permanently_drop_suid: 1001 debug1: identity file /home/rabe/.ssh/identity type -1 debug1: identity file /home/rabe/.ssh/id_rsa type -1 debug1: identity file /home/rabe/.ssh/id_dsa type -1 ssh_exchange_identification: Connection closed by remote host
Idle-Timeout[Bearbeiten]
Im gezeigten Setup wird der HTTPS-Proxy die Verbindung zwangsweise nach einer gewissen Zeit trennen, wenn keine Daten übertragen wurden (idle timeout). Das betrifft primär die Verbindung zu shell.uugrn.org:443, die als SOCKS-Proxy fungiert. Die Direktive TCPKeepAlive yes sorgt hier nicht dafür, dass Payload-Daten übertragen werden, entsprechend wird eine andere Methode benötigt.
Mit ServerAliveInterval und ServerAliveCountMax veranlasst man den OpenSSH Client, regelmäßig Alive-Messages mit dem OpenSSH Server auszutauschen. Hierbei werden innerhalb der SSH-Verbindung Daten übertragen. Wenn mehr als ServerAliveCountMax Messages fehlschlagen, erklärt der SSH-Client die Verbindung für abgebrochen und beendet sich.
Wir nutzen dieses Feature, um regelmäßig Payload-Daten zu übertragen und somit dem Idle-Timeout durch den HTTPS-Proxy vorzubeugen.
ServerAliveCountMax Sets the number of server alive messages (see below) which may be sent without ssh(1) receiving any messages back from the server. If this threshold is reached while server alive messages are being sent, ssh will disconnect from the server, terminating the session. It is important to note that the use of server alive messages is very different from TCPKeepAlive (below). The server alive messages are sent through the encrypted channel and there- fore will not be spoofable. The TCP keepalive option enabled by TCPKeepAlive is spoofable. The server alive mechanism is valu- able when the client or server depend on knowing when a connec- tion has become inactive. The default value is 3. If, for example, ServerAliveInterval (see below) is set to 15 and ServerAliveCountMax is left at the default, if the server becomes unresponsive, ssh will disconnect after approximately 45 seconds. This option applies to protocol version 2 only. ServerAliveInterval Sets a timeout interval in seconds after which if no data has been received from the server, ssh(1) will send a message through the encrypted channel to request a response from the server. The default is 0, indicating that these messages will not be sent to the server. This option applies to protocol version 2 only.
Der Standardwert für ServerAliveCountMax 3 ist für unseren Anwendungsfall ausreichend, als ServerAliveInterval benötigen wir einen Wert, der definitiv geringer ist als der Timeout vom HTTPS-Proxy. Auf der sicheren Seite sind wir bei 5 min oder 300 sec.
Die Datei ~/.ssh/config sollte daher mindestens um ServerAliveInterval 300 ergänzt werden:
Host shell443 HostName shell.uugrn.org Port 443 User rabe TCPKeepAlive yes ServerAliveInterval 300 DynamicForward 127.0.0.1:1080 ProxyCommand /usr/bin/nc -X connect -x relay.stwserv.de:3128 %h %p
- Nebenbei bemerkt
- Manche Router mit Stateful Firewall oder NAT vergessen bei inaktiven SSH-Sessions die TCP Verbindungsparameter. Hiergegen hilft manchmal TCPKeepAlive yes. Manchmal genügt das allerdings nicht, weil kein Payload übertragen wird. Hier hilft dann normalerweise ServerAliveInterval 30 recht zuverlässig. Braucht man sehr kurze Intervalle, können SSH-Sessions bei temporärem Netzausfall (zB 90 sec) verloren gehen. Hier sollte man dann zusätzlich ServerAliveCountMax 10 oder noch höher setzen, je nachdem, wie lange man einen temporären Netzausfall tolerieren möchte (z.B. Funkloch bei UMTS).
Netzwerk[Bearbeiten]
Nachfolgend ist der Traffic zwischen relay.stwserv.de:54677 und shell.uugrn.org:443 mit tcpdump dokumentiert, wenn sonst keine Daten übertragen werden (Idle):
Der erste Block zeigt den Verbindungsaufbau nach shell.uugrn.org:443:
01:08:00.077789 IP 195.49.138.67.54677 > 195.49.138.100.443: tcp 0 01:08:00.078243 IP 195.49.138.100.443 > 195.49.138.67.54677: tcp 0 01:08:00.078273 IP 195.49.138.67.54677 > 195.49.138.100.443: tcp 0 01:08:00.107722 IP 195.49.138.100.443 > 195.49.138.67.54677: tcp 40 01:08:00.108161 IP 195.49.138.67.54677 > 195.49.138.100.443: tcp 40 01:08:00.110269 IP 195.49.138.100.443 > 195.49.138.67.54677: tcp 776 01:08:00.110492 IP 195.49.138.67.54677 > 195.49.138.100.443: tcp 792 01:08:00.209650 IP 195.49.138.67.54677 > 195.49.138.100.443: tcp 24 01:08:00.209991 IP 195.49.138.100.443 > 195.49.138.67.54677: tcp 0 01:08:00.214078 IP 195.49.138.100.443 > 195.49.138.67.54677: tcp 152 01:08:00.218946 IP 195.49.138.67.54677 > 195.49.138.100.443: tcp 144 01:08:00.231205 IP 195.49.138.100.443 > 195.49.138.67.54677: tcp 1040 01:08:00.247505 IP 195.49.138.67.54677 > 195.49.138.100.443: tcp 16 01:08:00.346624 IP 195.49.138.67.54677 > 195.49.138.100.443: tcp 48 01:08:00.347045 IP 195.49.138.100.443 > 195.49.138.67.54677: tcp 0 01:08:00.347159 IP 195.49.138.100.443 > 195.49.138.67.54677: tcp 48 01:08:00.347426 IP 195.49.138.67.54677 > 195.49.138.100.443: tcp 64 01:08:00.352175 IP 195.49.138.100.443 > 195.49.138.67.54677: tcp 64 01:08:00.352478 IP 195.49.138.67.54677 > 195.49.138.100.443: tcp 96 01:08:00.356646 IP 195.49.138.100.443 > 195.49.138.67.54677: tcp 64 01:08:00.456532 IP 195.49.138.67.54677 > 195.49.138.100.443: tcp 0 01:08:02.480069 IP 195.49.138.67.54677 > 195.49.138.100.443: tcp 80 01:08:02.483309 IP 195.49.138.100.443 > 195.49.138.67.54677: tcp 48 01:08:02.483537 IP 195.49.138.67.54677 > 195.49.138.100.443: tcp 80 01:08:02.484434 IP 195.49.138.100.443 > 195.49.138.67.54677: tcp 32 01:08:02.485710 IP 195.49.138.67.54677 > 195.49.138.100.443: tcp 64 01:08:02.585450 IP 195.49.138.100.443 > 195.49.138.67.54677: tcp 0
Der zweite Block zeigt, dass alle 30sec (für ServerAliveInterval 30!) seit dem Verbindungsaufbau kleine Datenmengen (Payload) übertragen werden:
01:08:32.482686 IP 195.49.138.67.54677 > 195.49.138.100.443: tcp 64 01:08:32.483463 IP 195.49.138.100.443 > 195.49.138.67.54677: tcp 32 01:08:32.583427 IP 195.49.138.67.54677 > 195.49.138.100.443: tcp 0 01:09:02.480781 IP 195.49.138.67.54677 > 195.49.138.100.443: tcp 64 01:09:02.481610 IP 195.49.138.100.443 > 195.49.138.67.54677: tcp 32 01:09:02.581590 IP 195.49.138.67.54677 > 195.49.138.100.443: tcp 0
Der dritte Block zeigt, wenn die SSH-Session abgebrochen wird, zum Beispiel, indem der ssh-Prozess gekillt wird:
01:09:29.937978 IP 195.49.138.67.54677 > 195.49.138.100.443: tcp 0 01:09:29.938752 IP 195.49.138.100.443 > 195.49.138.67.54677: tcp 0 01:09:29.938898 IP 195.49.138.100.443 > 195.49.138.67.54677: tcp 0 01:09:29.938917 IP 195.49.138.67.54677 > 195.49.138.100.443: tcp 0