Skip to content
KitploitKITPLOIT
FerramentasExploitsBlog
Log in
Enviar
FerramentasExploitsBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
Tenda-Router-VR-and-Exploit — Writeup sobre rehospedagem de firmware do roteador Tenda AC15 e replicação de exploit de execução remota de comandos (CVE-2020-10987). | Kitploit
Ferramentas/GitHubGitHub/jaden-bowers/tenda-router-vr-and-exploit
Segurança de Sistemas EmbarcadosSegurança IoTAnálise de VulnerabilidadesExploraçãoEngenharia ReversaTestes de PenetraçãoSegurança de HardwareAprendizado e EducaçãoAnálise de Firmware

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar
Exploração de Binários
Labs e Prática
GitHubjaden-bowers/tenda-router-vr-and-exploit

Tenda-Router-VR-and-Exploit

Writeup sobre rehospedagem de firmware do roteador Tenda AC15 e replicação de exploit de execução remota de comandos (CVE-2020-10987).

Ver Repositório
119há 10 mesesAinda não revisado

Tenda-Router-VR-and-Exploit

Este write-up mostra exatamente como emulei o servidor web do firmware AC15 (V15.03.05.19) com QEMU, tornei-o acessível a partir do navegador do host e explorei o handler vulnerável /goform/setUsbUnload (CVE-2020-10987) para obter execução de comandos dentro do rootfs emulado.


Visão geral

  • Extrair (ou, no nosso caso, obter) o sistema de arquivos squashfs da imagem do firmware e começar a fazer engenharia reversa
  • Construir um convidado Debian armhf rodando sob qemu-system-arm
  • Configurar a rede do convidado e o caminho de redirecionamento de portas para o roteador emulado
  • Construir nosso script de inicialização
  • Explorar o roteador emulado

Extraindo o sistema de arquivos

Primeiro precisamos obter nossa imagem de firmware. Não consegui encontrar o download da imagem para AC15 V15.03.05.19, porém consegui encontrar um repositório no github com o sistema de arquivos squashfs já extraído. Se tivéssemos o arquivo de imagem correto, poderíamos extraí-lo usando binwalk da seguinte forma:``` user@computer $ binwalk -e AC15_V15.03.05.19.bin

DECIMAL HEXADECIMAL DESCRIPTION

64 0x40 TRX firmware header, little endian, image size: 6778880 bytes, CRC32: 0x80AD82D6, flags: 0x0, version: 1, header size: 28 bytes, loader offset: 0x1C, linux kernel offset: 0x1A488C, rootfs offset: 0x0 92 0x5C LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 4177792 bytes 1722572 0x1A48CC Squashfs filesystem, little endian, version 4.0, compression:xz, size: 5052332 bytes, 848 inodes, blocksize: 131072 bytes, created: 2017-04-19 16:18:08

user@computer $ cd _AC15_V15.03.05.19.bin.extracted

Como não temos o arquivo de imagem correto, mas um repositório com o sistema de arquivos já extraído, podemos simplesmente clonar o repositório.```
git clone https://github.com/lapinpt/Tenda-AC15-Firmware-V15.03.05.19-9061

I have made a directory called VR and cloned this repo inside it. My rootfs is at $HOME/VR/Tenda-AC15-Firmware-V15.03.05.19-9061/rootfs

Great now we have our AC15 V15.03.05.19 firmware. Lets move onto some reversing to see whats going on.

Reverse Engineering

I will be using Ghidra 11.4.2 for my reversing. Lets navigate to our target binary here rootfs/bin/httpd and load it in Ghidra. If we go to the formsetUsbUnload function we can see,```C uVar1 = FUN_0002bd4c(param_1,"deviceName",&DAT_000f4bdc); doSystemCmd("cfm post netctrl %d?op=%d,string_info=%s",0x33,3,uVar1); FUN_0002c6cc(param_1,"HTTP/1.0 200 OK\r\n\r\n"); FUN_0002c6cc(param_1,"{"errCode":0}"); FUN_0002cc14(param_1,200); return;

Esta é a vulnerabilidade. O parâmetro `deviceName` é passado diretamente para `doSystemCmd`, permitindo que enviemos quaisquer comandos que quisermos.

Agora, como vamos re-hospedar este firmware usando qemu e não o hardware do roteador original, alguns programas vão tentar alcançar dispositivos que não existem e travar nossa inicialização.
Como nosso objetivo é explorar o servidor web (`httpd`), eu foquei apenas em re-hospedar esse binário, não toda a inicialização (`/rootfs/etc_ro/init.d/rcS`). _Em retrospectiva, não tenho certeza se essa foi a decisão certa_

Então, ao olhar para `rcS`, eu queria encontrar qualquer coisa que `rcS` pudesse fazer que `httpd` precisaria. Perto do final do arquivo, podemos ver:```bash
cfmd &
echo '' > /proc/sys/kernel/hotplug
udevd &
logserver &

rcS inicia o cfmd em segundo plano logo antes do restante da stack subir. Após mais algumas pesquisas e ao observar como a função vulnerável envia o comando, cheguei à conclusão de que,

  • o httpd constrói um cfm post
  • Em seguida, o cliente cfm fala com o cfmd por meio de um socket de domínio UNIX (ex.: /var/cfm_socket)

Na rotina InitServer no cfmd, podemos ver```C unlink("/var/cfm_socket"); strncpy(sa_unix.sun_path, "/var/cfm_socket", ...); bind(fd, (sockaddr*)&sa_unix, 0x6e); listen(fd, 5);

Ele cria um socket UIX e escuta.
No geral, o funcionamento é: o handler lê um frame fixo de 0x7e0 bytes de cada cliente (`RecvMsg`/`SendMsg`). Os primeiros 4 bytes são um código de comando; depois há um buffer de chave de 512 bytes e um buffer de valor de 1500 bytes (você pode ver os tamanhos dos objetos de stack no handler). Ele faz switch no opcode e responde com um código ACK:
- `2` -> Get: `GetCfmValue(key, value)` em seguida responde com o código `3`
- `0` -> Set: `SetCfmValue(key, value)` em seguida responde com o código `1`
- `0x11` -> Unset: `UnSetCfmValue(key)` em seguida responde com o código `0x12`
- `10` -> Commit: `SaveCfm2Flash()` em seguida responde `0x10` (OK) ou `0xB` (erro)

Então, para emular `cfmd`, criei um pequeno script `cfm_stub`.```C
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>

#define SOCK_PATH "/var/cfm_socket"

// minimal UNIX-domain server that httpd expects.
// Replies with an IP string when it sees the key it asks for.
int main(void) {
  int s = socket(AF_UNIX, SOCK_STREAM, 0);
  struct sockaddr_un addr = {0};
  if (s < 0) { perror("socket"); return 1; }
  unlink(SOCK_PATH);
  addr.sun_family = AF_UNIX;
  strncpy(addr.sun_path, SOCK_PATH, sizeof(addr.sun_path)-1);
  if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) { perror("bind"); return 1; }
  if (listen(s, 5) < 0) { perror("listen"); return 1; }

  for (;;) {
    int c = accept(s, NULL, NULL);
    if (c < 0) { if (errno==EINTR) continue; perror("accept"); break; }
    char buf[1024]; ssize_t n = read(c, buf, sizeof(buf));
    if (n > 0) {
      // In some builds httpd asks for "lan.webiplansslen" etc.
      // Any non-empty reply that looks like an IP keeps init happy.
      const char *reply = "192.168.0.1";
      write(c, reply, strlen(reply));
    }
    close(c);
  }
  close(s);
  return 0;
}

Vou mostrar como compilar isso depois da próxima parte.

O próximo arquivo auxiliar é hooks.so. Há algumas funções usadas em httpd e cfm que tentam interagir com hardware inexistente. A seguir está o que nosso programa assume ao iniciar:

  • Partições Flash + MTD existem e são montáveis.
  • Um dispositivo NVRAM existe (/dev/nvram) e retorna padrões sensatos.
  • Rotinas diversas da plataforma são bem-sucedidas (carregador de configurações da camada 7, restauração de potência de RF, etc.).

As seguintes funções tornam-se um problema e, portanto, precisamos corrigir com LD_PRELOAD=/hooks.so.

  • get_flash_type() -> se retornar 4, o código segue um caminho baseado em arquivo (cfm_file_init), caso contrário, tenta falar com MTD (que não temos).
  • get_cfm_blk_size_from_cache() (e/ou a variante j_get_cfm_blk_size_from_cache) é consultada para dimensionamento do bloco de configuração.
  • Chamadas aos shims de NVRAM da Broadcom (bcm_nvram_get) não devem falhar, ou a pilha assume "NVRAM destruída" e entra na lógica de restauração/reinicialização.
  • Rotinas adicionais como load_l7setting_file() e restore_power() devem ter sucesso, mas tocam em hardware/arquivos inexistentes.

Aqui está nosso hooks.c. Créditos ao azeria-labs pelo original.```C #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <dlfcn.h> #include <string.h>

int j_get_cfm_blk_size_from_cache(const int i) { puts("j_get_cfm_blk_size_from_cache called....\n"); return 0x20000; // 128 KiB block — what the file path expects }

int get_flash_type() { puts("get_flash_type called....\n"); return 4; // force file-backed CFM init, not MTD }

int load_l7setting_file() { puts("load_l7setting_file called....\n"); return 1; // pretend Layer-7 settings loaded OK }

int restore_power(int a, int b) { puts("restore_power called....\n"); return 0; // success (don’t touch RF/power hardware) }

Baixar ferramenta