HY300: cosa mandava in Cina il mio proiettore da 80 euro?
Analisi di un proiettore Android economico: backdoor di fabbrica, telemetria verso la Cina e un root senza sbloccare il bootloader.
INDICE · 6 SEZIONI
Il mio mini-proiettore Android mandava a server in Cina il suo numero di serie, il suo indirizzo MAC, l'elenco delle app che aprivo con orari e durata, e un segnale "sono acceso" ogni cinque minuti. L'ho scoperto soltanto perché laggava durante il mio meritato rewatch di Better Call Saul, in 720p.
È una storia che ho già raccontato al lavoro, e che i colleghi mi hanno convinto a scrivere. Un po' di colpa ce l'ha anche il mio collega Danilo, che mi passa di continuo articoli sulla sicurezza: a forza di leggerli, quando un dispositivo di casa rallenta il primo pensiero non è più "sarà vecchio". Nella cyber, del resto, la mia vita è questa:

Il proiettore in questione è uno dei tanti della famiglia HY300, venduti con mille marchi diversi su Amazon e AliExpress: questo ha Android 11, 1 GB di RAM e un processore Allwinner H713. Volevo solo capire perché laggasse così tanto. Alla fine mi sono ritrovato con il root, cioè i permessi da amministratore che su Android il produttore di solito tiene per sé, senza più nessuna app del produttore e con le prove di cosa faceva prima.
Il proiettore aveva tre accessi da amministratore lasciati aperti in fabbrica, e un gruppo di app del produttore che raccontava a server cinesi cosa ci facevo. Ho preso il controllo del dispositivo senza perdere dati, ho chiuso tutto e ho ricostruito cosa era uscito: i dati d'uso sì, il malware più pericoloso segnalato su questi modelli no. Più per fortuna che per merito, come vedremo.
Molti pacchetti del firmware, il software installato in fabbrica, si chiamano com.htc.*, ma HTC, l'azienda taiwanese, non c'entra niente. È solo il prefisso scelto da chi ha assemblato il sistema. Partiamo benissimo.
Il lavoro è andato avanti in quattro fasi, con una sola regola: ogni modifica doveva essere reversibile, e l'unica operazione rischiosa, il root, doveva arrivare solo dopo un backup completo e verificato. Il proiettore l'avevo comunque pagato 80 euro, e tornare a guardare le serie dal letto chino sull'iPad non è mai stata un'opzione.
| Fase | Obiettivo | Cosa ho toccato |
|---|---|---|
| 1 · Ricognizione | capire cosa gira, senza toccare niente | niente: adb in sola lettura |
| 2 · Root | avere il controllo completo, senza perdere dati | solo la partizione di avvio, dopo il backup di 25 partizioni |
| 3 · Pulizia | chiudere le backdoor e togliere le app del produttore | un modulo Magisk, che si toglie quando si vuole |
| 4 · Indagine | scoprire cosa aveva mandato fuori il proiettore | niente: solo lettura di contatori e file |
Una nota: non ero il primo a mettere le mani su questi proiettori, ma l'ho scoperto solo dopo. Tre settimane prima un ricercatore aveva pubblicato su GitHub un debloat per il Magcubic HY300 Pro, cioè uno script che toglie le app inutili o dannose, e lo apriva così:
The 3 following packages are malware installed on Magcubic HY300 Pro.
Uno dei tre è proprio il launcher, la schermata principale, che girava sul mio. Fase per fase, vediamo cosa c'era intorno.
| Scheda | HY300 (il mio esemplare) |
|---|---|
| SoC | Allwinner H713 (sun50iw12p1), 4 core |
| Sistema | Android 11 a 32 bit su kernel a 64 bit |
| Bootloader | dichiarato "locked", ma non verifica nulla |
| SELinux | Permissive |
| Root | Magisk 30.7, senza sblocco e senza perdere dati |
| Esito | dati d'uso inviati in Cina; nessuna traccia del malware proxy |
Fase 1: la ricognizione
L'obiettivo della prima fase era semplice: capire cosa gira sul proiettore senza toccare niente. Il proiettore espone sulla rete Wi-Fi, alla porta 5555, adb, lo strumento di debug di Android che permette di comandarlo da un computer: basta autorizzare la chiave del computer sullo schermo del proiettore e si ha una shell, cioè una riga di comando sul dispositivo. Da lì, prima di toccare qualsiasi cosa, ho guardato.
Per rendere l'idea di cosa ho trovato, pensate a una casa nuova in cui il costruttore ha lasciato tre porte di servizio aperte, ha tolto la serratura al caveau e ha staccato l'allarme. Assurdo, no? Ecco cosa c'era:
- tre accessi di root lasciati dal produttore:
/system/bin/wifi, una copia rinominata disu, il comando che dà i permessi da amministratore, con il bit SUID, che la fa girare come root chiunque la lanci;qw, un vecchio demonesusempre attivo in background; edbgserver, il server di debug di Allwinner, avviato come root e in ascolto sulla porta 65528, che accettava comandi da qualsiasi dispositivo della rete di casa; - la memoria flash intera (
/dev/block/mmcblk0), dove sta tutto il sistema, scrivibile da qualsiasi app, con permessirw-rw-rw-; - SELinux in modalità Permissive: SELinux è l'ultima barriera tra un'app e il sistema, e in questa modalità registra le violazioni senza bloccarle.
Un dettaglio sulla prima backdoor, perché i dettagli contano:
$ adb shell /system/bin/wifi root id
uid=0(root) ... context=u:r:shell:s0È un root "monco": uid 0, cioè l'utente amministratore, ma senza capability (CapEff=0), i singoli poteri che di solito l'amministratore si porta dietro. Su questo dispositivo, però, conta poco. Con SELinux permissivo e la flash scrivibile da chiunque, per fare danni non serviva nemmeno.
Sopra tutto questo girava una rete di app del produttore:
| App | Cosa faceva |
|---|---|
| Launcher | pubblicità e profilazione (Zeasn, AdMob, DoubleClick, Baidu Analytics) |
| Telemetria | invio dei dati d'uso a un server su Tencent Cloud |
| Store | download e installazione di app in autonomia |
| Tre servizi OTA | aggiornamenti di sistema via Internet, in HTTP non cifrato |
com.hotack.writesn |
identificazione del dispositivo, della stessa famiglia del malware proxy segnalato su questi proiettori |
E il lag? A riposo restavano 186 MB di RAM liberi su 960, 470 MB erano già nello swap, la memoria di riserva (lenta) che si usa quando la RAM finisce, e giravano circa 1.500 thread. Il produttore aveva anche reso "immortali" il Play Store e le proprie app di servizio: il sistema non poteva chiuderle nemmeno con la memoria agli sgoccioli.
Con questo quadro in mano, il passo successivo era prendere il controllo del dispositivo. Prima, però, il backup: 25 partizioni, le sezioni in cui è divisa la memoria, salvate e verificate una per una con SHA-256, un'impronta digitale che cambia se cambia anche un solo bit.
Fase 2: il root senza sbloccare il bootloader
Su uno smartphone, all'accensione il bootloader, il programma che carica il sistema, verifica la firma della partizione di avvio con Android Verified Boot (AVB). Se il bootloader è bloccato e l'immagine è modificata, si rifiuta di avviarla. Per installare Magisk, lo strumento più usato per avere il root su Android, bisogna quindi sbloccarlo, il che cancella tutti i dati, e poi scrivere l'immagine col cavo USB in fastboot, una modalità di avvio fatta apposta per riscrivere il sistema.
Su questo proiettore no. Il sistema si dichiara "locked", ma il bootloader avvia con androidboot.trustchain=false e non passa al kernel lo stato della verifica. È un buttafuori che chiede il documento e non lo guarda: entra chiunque, anche un'immagine modificata. E visto che la flash era scrivibile da qualsiasi app, la partizione di avvio si poteva riscrivere dall'interno di Android, via Wi-Fi, senza cavo.
flowchart LR
subgraph S["Smartphone"]
S1["Sblocco<br/>cancella i dati"] --> S2["Patch con Magisk"] --> S3["Flash via USB<br/>in fastboot"]
end
subgraph P["Questo proiettore"]
P1["Nessuno sblocco<br/>dati intatti"] --> P2["Patch sul<br/>proiettore stesso"] --> P3["Scrittura con dd<br/>via Wi-Fi"]
end
classDef rischio stroke:#e0607e,stroke-width:2px
classDef ok stroke:#3fa66b,stroke-width:2px
class S1 rischio
class P1 okLa patch l'ha fatta lo script ufficiale di Magisk direttamente sul proiettore, con i binari a 32 bit. Poi l'immagine è andata al suo posto con dd, che copia la memoria blocco per blocco, e prima di riavviare l'ho riletta per confrontarne l'hash:
export BOOTMODE=true KEEPVERITY=true KEEPFORCEENCRYPT=true
./busybox sh boot_patch.sh boot.img # produce new-boot.img
# boot_b inizia al blocco 2.632 da 64 KB ed è lunga 1.024 blocchi
dd if=new-boot.img of=/dev/block/mmcblk0 bs=65536 seek=2632 count=1024 conv=notrunc,fsync
dd if=/dev/block/mmcblk0 bs=65536 skip=2632 count=1024 | sha256sumIl risultato dopo il riavvio:
$ adb shell su -v
30.7:MAGISKSU
$ adb shell su -c id
uid=0(root) ... context=u:r:magisk:s0Stavolta root vero, con tutte le capability.
C'è però il rovescio della medaglia: la stessa falla che mi ha permesso il root permetteva a qualsiasi app di riscrivere il sistema. Per questo, appena ottenuto il root, l'ho chiusa il prima possibile, anche perché non sapevo ancora cosa stesse facendo davvero il proiettore. Tutto questo a mezzanotte (chi mi conosce sa che per me è un orario impensabile: sono moooolto vecchio, lo so).
Fase 3: la pulizia con il modulo hy300-clean
Da qui in poi ho chiamato in causa il mio amico Claudio (all'anagrafe Claude, l'unico altro pazzo che a mezzanotte può reversare binari senza lamentarsi) per un'analisi approfondita. Tutta la pulizia sta in un modulo Magisk, hy300-clean. A ogni avvio sovrappone i suoi file a quelli di sistema senza modificare la partizione di sistema: togliendo il modulo si torna esattamente all'originale.
- Backdoor:
wifi,qwedbgserversono sostituiti da script innocui, senza bit SUID. La porta 65528 non esiste più. - App del produttore: 21 rimosse con un file
.replace, che fa apparire vuota la loro cartella. Per Android quelle app non esistono più. - Server: i domini del produttore puntano a
0.0.0.0nel file hosts, la rubrica degli indirizzi interna al sistema: cercarli porta nel vuoto. - Firewall per app: le poche app del produttore che servono all'hardware possono parlare solo con la rete di casa.
Il firewall usa una funzione di Linux che su Android si vede poco: il match owner, che filtra il traffico in base all'utente che lo genera. Su Android ogni app gira con un utente tutto suo:
iptables -A hy300 -m owner --uid-owner <uid> -d 192.168.0.0/16 -j RETURN # rete di casa ok
iptables -A hy300 -m owner --uid-owner <uid> -j REJECT # tutto il resto rifiutatoIl risultato si vede subito:
| Misura | Prima | Dopo |
|---|---|---|
| RAM disponibile a riposo | 186 MB | circa 515 MB |
| Swap usato a riposo | 470 MB | circa 185 MB |
| Porte aperte come root sulla rete di casa | 1 | 0 |
| File SUID nel sistema | 1 | 0 |
Un bonus inatteso riguarda YouTube. Il chip decodifica in hardware H.264 e HEVC, cioè li gestisce da solo senza pesare sul processore, ma non VP9 e AV1, i formati che YouTube manda di preferenza. Impostando SmartTube, un'app alternativa per YouTube, su 720p H.264, la risoluzione nativa del proiettore, i fotogrammi persi sono passati da "continui" a zero e la CPU dal 67% al 36%.
Sistemato il presente, restava il passato: cosa aveva fatto il proiettore prima che arrivassi io?
Fase 4: l'indagine
Con il root potevo finalmente leggere quello che Android tiene per sé: i contatori del traffico di ogni app e i file delle app di sistema. Due piste, e la prima mi ha fatto prendere un bello spavento.
Il giallo dei 18 GB
Android conserva per circa tre mesi e mezzo i contatori di traffico di ogni app. Il riepilogo di default copre solo l'ultimo avvio; per lo storico servono le opzioni giuste:
adb shell su -c 'dumpsys netstats --full --uid --tag' > ns_full.txtLeggendoli, l'app AirPlay/DLNA del produttore, quella che riceve i video dal telefono, aveva ricevuto 9,1 GB e inviato 8,8 GB. Un ricevitore dovrebbe soprattutto ricevere. Un traffico in uscita pari a quello in entrata è la firma tipica di un proxy residenziale: un dispositivo che presta di nascosto la tua connessione a sconosciuti, che navigano con il tuo IP. Ed è proprio quello che fa il malware segnalato su questi proiettori. Il mio primo pensiero è stato: vabbè, è finita. Mezza Cina è uscita su Internet con il mio IP: tra due o tre ore mi piomberà in casa il GIS. Nel periodo dell'analisi ho vissuto quasi sempre con due designer: tra loro e un Red Team Penetration Tester (almeno secondo LinkedIn), indovinate chi se bevono, come si dice a Roma.
I numeri però dicevano altro:
- il ritmo era quello di una macchina: in media circa 85 MB al giorno in entrata e altrettanti in uscita, con lo stesso andamento di giorno e di notte. Una persona non usa AirPlay così, un timer sì;
- i pacchetti erano piccoli: in media 390–400 byte, la taglia di un annuncio UPnP/SSDP (ci arriviamo tra poco). Un proxy che inoltra navigazione sposta pacchetti vicini ai 1.400 byte;
- dentro la VPN i conti tornavano al byte;
- Claudio sembrava tranquillo: roba da non sottovalutare.
contatori di traffico Android, app AirPlay/DLNA, 8 luglio – 22 ottobre 2026
| Rete | Ricevuti | Inviati |
|---|---|---|
| VPN | 211.158.684 byte (529.884 pacchetti) | 211.158.684 byte (529.884 pacchetti) |
| Wi-Fi | 8.922.228.314 byte | 8.552.099.200 byte |
Per capire cosa succedeva, immaginate di urlare in una stanza vuota e di contare anche la vostra eco come "messaggi ricevuti". L'app DLNA mandava circa cinque annunci multicast al secondo, giorno e notte: messaggi "sono qui" spediti a tutti i dispositivi della rete insieme, gli stessi con cui il telefono trova la TV per lo streaming. E si ritrovava indietro la copia dei propri pacchetti:
sequenceDiagram
participant A as App DLNA
participant M as Multicast SSDP<br/>239.255.255.250
participant D as Altri dispositivi di casa
loop circa 5 volte al secondo
A->>M: annuncio (~400 byte)
M-->>A: eco del proprio annuncio
end
Note over A,M: nella VPN c'è solo l'eco:<br/>ricevuti = inviati
D->>M: i loro annunci
M-->>A: arrivano anche questi
Note over A,D: sul Wi-Fi l'entrata supera l'uscita del 4–6%Nella VPN non ci sono altri dispositivi, quindi tornava solo l'eco: numeri identici. Sul Wi-Fi si aggiungevano gli annunci del resto della casa, e l'entrata superava l'uscita di qualche punto percentuale.
Niente dati usciti, quindi: solo uno spreco continuo di CPU e Wi-Fi. Se mi fossi fermato al primo numero avrei scritto un articolo sbagliato, e molto più cliccabile di questo.
Cosa mandava davvero
La prova vera era in un file dimenticato: la coda di invio del servizio di telemetria, in JSON leggibile, con gli ultimi eventi prima che lo disattivassi.
{"channel":"hy260_...",
"content":"{\"endTime\":1792702128475,\"pkgName\":\"com.android.tv.settings\",\"runTime\":6382,\"startTime\":1792702134857}",
"deviceId":"<indirizzo MAC>","deviceSn":"<numero di serie>",
"eventCode":100002,"language":"it_IT","model":"GM200", ...}Ogni evento legava all'identità del dispositivo una di due informazioni:
- evento 100002: ogni app aperta, con inizio, fine e durata. Il nome dell'app, non il titolo guardato;
- evento 100001: un "sono acceso" esattamente ogni 300 secondi.
Un piccolo regalo per chi guarda i numeri: endTime viene prima di startTime, e i 6382 millisecondi di runTime sono la loro differenza al contrario. Perfino la telemetria è scritta male.
Che quegli eventi partissero lo dice la coda stessa. Ogni "sono acceso" riporta anche da quando il proiettore è acceso: 13 giorni. A uno ogni cinque minuti, se ne sarebbero dovuti accumulare più di 3.700; nel file ce n'erano tre. La coda si svuotava di continuo: segno che gli eventi partivano e venivano cancellati.
La destinazione era un server su Tencent Cloud, in HTTP non cifrato: chiunque si trovasse lungo la strada poteva leggerli. Nella memoria condivisa c'era anche un identificativo permanente di Baidu, nascosto in backups/.SystemConfig/.cuid2 proprio per sopravvivere alla reinstallazione delle app.
Resta una domanda: come si comanda a distanza un dispositivo dietro un router di casa, che blocca le connessioni in entrata? Semplice, non serve. È il proiettore a chiedere periodicamente "ci sono istruzioni per me?", e la risposta torna indietro come una qualsiasi pagina web:
sequenceDiagram
participant P as Proiettore
participant R as Router di casa
participant S as Server del produttore
S-xR: connessione in entrata
Note over R: bloccata
P->>R: "ci sono istruzioni per me?"
R->>S: richiesta in uscita (permessa)
S-->>P: aggiornamento, app o codiceÈ il principio di base di una reverse shell, un classico degli attaccanti: se non puoi raggiungere tu la risorsa, fai in modo che sia lei a contattare te. E il canale funzionava: il 28 aprile 2025 il mio proiettore ha installato un aggiornamento di sistema completo, e anche store e launcher risultavano aggiornati rispetto alla versione di fabbrica.
E la VPN che avevo sul proiettore? Fa passare il traffico di tutte le app, comprese quelle di sistema. Nasconde i dati al provider e cambia l'IP visto dai server, ma non impedisce a un'app di inviarli: è un tubo, non un filtro.
Quanto è andata male?
Sul mio esemplare, per quanto si può vedere, poteva andare molto peggio. Sono partiti i dati d'uso legati all'identità del dispositivo: quali app aprivo, quando e per quanto, e quando il proiettore era acceso. Fastidioso, ma niente di più.
Il resto è pulito:
writesn, il pezzo della famiglia del malware proxy, nei tre mesi e mezzo registrati non ha mai usato la rete;silentsdk, il malware vero e proprio, sul mio non c'era;- nessuna delle app installate oggi viene dal produttore, e non ci sono certificati CA aggiunti (servirebbero a leggere il traffico cifrato), amministratori del dispositivo o servizi di accessibilità sospetti, i permessi preferiti dai malware per comandare un dispositivo.
Il punto però è un altro, ed è quello che mi ha fatto cambiare idea mentre studiavo le fonti per questo articolo. Su altri esemplari della stessa famiglia, silentsdk si presentava come un SDK di analisi, cioè un componente per le statistiche d'uso, ed era in realtà un trojan con privilegi di sistema. Soprattutto, il codice malevolo non restava sul dispositivo: lo consegnava il server di comando e controllo (C2) quando serviva, e poteva ritirarlo appena qualcuno iniziava ad analizzarlo. Un altro report descrive la stessa architettura con due pezzi: il RAT SilentSDK, un programma per il controllo a distanza, e un dropper chiamato StoreOS, che serve solo a scaricare e installare altro.
Sul mio proiettore c'era un'app di nome com.htc.storeos, uno store che poteva installare app in autonomia. Non ho confrontato il mio APK con quello del report, quindi non posso dire che siano lo stesso codice. Ma il quadro è chiaro: avevo tutta l'infrastruttura per ricevere un payload, il codice malevolo vero e proprio, e per quel poco che posso vedere mancava solo lui.
flowchart LR
C2["Server C2<br/>consegna il payload"] -.-> ST["Store<br/>installa in silenzio"]
C2 -.-> LA["Launcher<br/>scarica ed esegue codice"]
C2 -.-> OT["3 servizi OTA<br/>aggiornamenti in HTTP"]
ST -.-> SS["silentsdk<br/>mai arrivato"]
WS["writesn<br/>presente, mai in rete"] -.- SS
classDef presente stroke:#e0607e,stroke-width:2px
classDef assente stroke:#3fa66b,stroke-width:2px,stroke-dasharray:5 4
classDef incerto stroke:#e0a030,stroke-width:2px,stroke-dasharray:5 4
class ST,LA,OT,WS presente
class SS assente
class C2 incertoQuindi sì, sono stato fortunato. Che il payload non sia mai arrivato, o che sia arrivato e se ne sia andato senza lasciare traccia, dipendeva dal server, non da me.
E c'è un limite onesto. Le app del produttore giravano con l'utente di sistema, il cui traffico si confonde con quello di Android, e i contatori coprono solo gli ultimi tre mesi e mezzo, mentre il proiettore era in uso da molto prima. "Non ho trovato prove", purtroppo, non vuol dire "non è successo niente". Però, almeno negli ultimi tre mesi e mezzo, nessun comunista mi ha sentito russare. Che noia, comunque.
Conclusioni
Questo proiettore non aveva un'unica falla, ma un sistema intero costruito senza alcuna idea di confine: backdoor di root lasciate di fabbrica, una flash scrivibile da chiunque, SELinux permissivo, un bootloader che si dice bloccato e non verifica nulla. Sopra, una rete di app pronte a raccontare a server lontani cosa facevo, e a ricevere da quei server qualsiasi cosa.
Cosa mi porto a casa, e cosa consiglierei a chi ha un dispositivo così:
- rete separata: proiettori, TV box e prese smart vanno sulla rete ospiti, non accanto al computer di lavoro;
- "locked" non vuol dire sicuro, e una VPN non è un filtro: cambia la strada dei dati, non decide quali partono;
- igiene dell'account: se il dispositivo ha avuto il vostro account Google, disconnettetelo da "I tuoi dispositivi" e rientrate;
- prima di comprare: diffidate dei dispositivi Android senza un marchio riconoscibile, quelli venduti identici con mille nomi diversi: nessuno risponde del firmware e nessuno lo aggiorna. Se ce l'avete già, usatelo come schermo: niente Wi-Fi, e nell'HDMI una chiavetta di un produttore che conoscete.
Si dice che se un prodotto è gratis, il prodotto sei tu. Questo proiettore l'avevo pagato, e il prodotto ero io lo stesso.
Spero vi sia piaciuto l'articolo. Qui sotto trovate gli indicatori per controllare i vostri dispositivi e altri riferimenti.
Indicatori
Per chi vuole controllare il proprio HY300 o la propria rete. Il modulo hy300-clean è costruito sul mio esemplare; per un debloat già pronto c'è quello di micha102 tra le fonti. Gli indirizzi IP possono cambiare nel tempo.
| Indicatore | Cos'è |
|---|---|
com.htc.eventuploadservice |
telemetria (eventi 100001 e 100002) |
com.htc.storeos |
store che installa app da solo |
com.hotack.writesn |
identificazione del dispositivo, famiglia del malware proxy |
/system/bin/wifi, /system/bin/qw, /vendor/bin/dbgserver |
accessi di root di fabbrica |
| porta 65528 | dbgserver, root dalla rete di casa |
event-api, pb-api e store-api.aodintech.com |
API di eventi e store: 119.28.17.161, Tencent Cloud (Hong Kong) |
ota.triplesai.com, otaapi.triplesai.com |
aggiornamenti: 111.230.36.129, Tencent Cloud (Cina) |
*.zeasn.tv |
launcher e pubblicità |
hm.baidu.com, hmma.baidu.com, mtj.baidu.com |
Baidu Analytics |
| 103.102.202.118, 152.32.240.141, 3.12.169.56, 40.160.68.219 | altri server del produttore |
backups/.SystemConfig/.cuid2 |
identificativo Baidu permanente |
Fonti
- micha102/hy300pro-debloat su GitHub: rimozione del proxy Hotack Silent SDK da un Magcubic HY300 Pro
- TuttoAndroid: il proiettore low cost e il proxy residenziale, che riprende l'analisi pubblicata da DDay.it
- ThreatCluster: malware di fabbrica nei proiettori Android, riassunto di un report su Reddit (SilentSDK e StoreOS)
- lolmam/HY300-H713-Research su GitHub: dump del firmware e root di un HY300 Pro+
- shift/sun50iw12p1-research su GitHub: ricerca sull'H713 e porting di Linux sull'HY300
- Magisk su GitHub (versione 30.7)
Commenti
via GitHub Discussions · giscusI commenti stanno su GitHub Discussions. Caricandoli, il tuo browser si collega a giscus.app e a GitHub.