Skip to content
KitploitKITPLOIT
StrumentiExploitsBlog
Log in
Invia
StrumentiExploitsBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

FeedContattoPrivacy© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
Tenda-Router-VR-and-Exploit — Writeup per la replicazione dell'exploit relativo al rehosting del firmware del router Tenda AC15 e all'esecuzione remota di comandi (CVE-2020-10987). | Kitploit
Strumenti/GitHubGitHub/jaden-bowers/tenda-router-vr-and-exploit
Sicurezza Sistemi EmbeddedSicurezza IoTAnalisi delle VulnerabilitàExploitReverse EngineeringPenetration TestingSicurezza HardwareApprendimento e FormazioneAnalisi del Firmware

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi
Binary Exploitation
Lab e Pratica
GitHubjaden-bowers/tenda-router-vr-and-exploit

Tenda-Router-VR-and-Exploit

Writeup per la replicazione dell'exploit relativo al rehosting del firmware del router Tenda AC15 e all'esecuzione remota di comandi (CVE-2020-10987).

Vedi Repository
12110 mesi faNon ancora revisionato

Tenda-Router-VR-and-Exploit

Questo write-up mostra esattamente come ho emulato il webserver del firmware AC15 (V15.03.05.19) con QEMU, come l'ho reso raggiungibile dal browser dell'host e come ho esercitato l'handler vulnerabile /goform/setUsbUnload (CVE-2020-10987) per ottenere l'esecuzione di comandi all'interno della rootfs emulata.


Panoramica

  • Estrai (o nel nostro caso ottieni) il filesystem squashfs dall'immagine del firmware e inizia il reversing
  • Crea una guest Debian armhf in esecuzione sotto qemu-system-arm
  • Configura il networking della guest e il percorso di port-forwarding verso il router emulato
  • Crea il nostro script di avvio
  • Sfrutta il router emulato

Estrazione del filesystem

Per prima cosa dobbiamo ottenere la nostra immagine del firmware. Non sono riuscito a trovare il download dell'immagine per AC15 V15.03.05.19, ma sono riuscito a trovare un repository GitHub con il filesystem squashfs già estratto. Se avessimo il file immagine corretto, potremmo estrarlo usando binwalk in questo modo,``` 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

Poiché non abbiamo il file immagine corretto ma un repository con il filesystem già estratto, possiamo semplicemente clonare il repository.```
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;

Questa è la vulnerabilità. Il parametro `deviceName` viene passato direttamente a `doSystemCmd` consentendoci di inviare qualunque comando vogliamo.

Ora, poiché rehostaremo questo firmware usando qemu e non l'hardware originale del router, alcuni programmi proveranno a raggiungere dispositivi che non ci sono e manderanno in crash il nostro avvio.
Poiché il nostro obiettivo è sfruttare il webserver (`httpd`), mi sono concentrato solo sul rehosting di quel binario, non sull'intero avvio (`/rootfs/etc_ro/init.d/rcS`). _Col senno di poi non sono sicuro che fosse la mossa giusta_

Quindi, guardando `rcS`, volevo trovare qualsiasi cosa che `rcS` potesse fare e di cui `httpd` avesse bisogno. Verso la fine del file possiamo vedere:```bash
cfmd &
echo '' > /proc/sys/kernel/hotplug
udevd &
logserver &

rcS avvia cfmd in background subito prima che il resto dello stack si avvii. Dopo ulteriori ricerche e dopo aver osservato come la funzione vulnerabile invia il comando, sono giunto alla conclusione che:

  • httpd costruisce un cfm post
  • Poi il client cfm comunica con cfmd tramite un socket di dominio UNIX (ad es. /var/cfm_socket)

Nella routine InitServer in cfmd possiamo vedere```C unlink("/var/cfm_socket"); strncpy(sa_unix.sun_path, "/var/cfm_socket", ...); bind(fd, (sockaddr*)&sa_unix, 0x6e); listen(fd, 5);

Crea una socket UIX e resta in ascolto.
In generale, il funzionamento è che l'handler legge un frame fisso di 0x7e0 byte da ogni client (`RecvMsg`/`SendMsg`). I primi 4 byte sono un codice di comando; poi c'è un buffer per la chiave di 512 byte e un buffer per il valore di 1500 byte (puoi vedere le dimensioni degli oggetti sullo stack nell'handler). Fa lo switch sull'opcode e risponde con un codice ACK:
- `2` -> Get: `GetCfmValue(key, value)` poi risponde con codice `3`
- `0` -> Set: `SetCfmValue(key, value)` poi risponde con codice `1`
- `0x11` -> Unset: `UnSetCfmValue(key)` poi risponde con codice `0x12`
- `10` -> Commit: `SaveCfm2Flash()` poi risponde `0x10` (OK) o `0xB` (errore)

Quindi, per emulare `cfmd`, ho creato un breve 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;
}

Mostrerò come compilare questo dopo la parte successiva.

Il prossimo file helper è hooks.so. Ci sono alcune funzioni usate in httpd e cfm che tentano di interagire con hardware inesistente. Il seguente è ciò che il nostro programma presuppone all'avvio:

  • Le partizioni Flash + MTD esistono e sono montabili.
  • Esiste un dispositivo NVRAM (/dev/nvram) che restituisce valori predefiniti sensati.
  • Le routine di piattaforma varie riescono (caricatore impostazioni Layer-7, ripristino potenza RF, ecc.).

Le seguenti funzioni diventano un problema e quindi dobbiamo applicare la patch con LD_PRELOAD=/hooks.so.

  • get_flash_type() -> se restituisce 4, il codice prende un percorso basato su file (cfm_file_init), altrimenti prova a comunicare con MTD (che non abbiamo).
  • get_cfm_blk_size_from_cache() (e/o la variante j_get_cfm_blk_size_from_cache) viene consultata per il dimensionamento del blocco di configurazione.
  • Le chiamate agli shim NVRAM Broadcom (bcm_nvram_get) non devono fallire, altrimenti lo stack presume “NVRAM distrutta” e procede con la logica di ripristino/riavvio.
  • Ulteriori routine come load_l7setting_file() e restore_power() devono riuscire, ma toccano hardware/file inesistenti.

Ecco il nostro hooks.c. Crediti a azeria-labs per l'originale.```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 }

Scarica lo strumento