Root shell on a Yandex Station Mini 2 from your laptop — no disassembly, no soldering, five minutes on the same Wi-Fi.
A full reproduction writeup of the eMudhra CA incident (Mozilla Bugzilla 2083449) against a personally owned Yandex Station Mini 2, demonstrating that a compromised third-party CA key is not just a browser problem: any appliance that validates its cloud config against the system trust store inherits the compromise.
Status: reproduction complete, root shell obtained.
The Mini 2 fetches its configuration from quasar.yandex.net over TLS,
validating only against the system CA bundle. One of the CAs in that bundle —
the eMudhra intermediate "EM OV TLS CA - G3C" (cross-signed by Comodo AAA
Certificate Services) — has a publicly recoverable private key (ECDSA P-384,
private scalar = 1, Mozilla Bugzilla 2083449). A laptop on the same Wi-Fi can
therefore impersonate the configuration backend, feed the device an arbitrary
configuration, and that configuration can enable an unauthenticated,
root-privileged ADB daemon on the network. Five minutes later: root shell.
ARP + DNS hijack → forged TLS for quasar.yandex.net → poisoned get_sync_info
→ adbd (root, :5555, no auth) → adb connect → uid=0(root)
- Forged the leaf certificate for
quasar.yandex.netfrom the leaked intermediate key. Chain validates against the stock trust store (openssl verifyreturn 0). - Intercepted the LAN path. ARP-poisoned the gateway entry on the
station; discovered en route that plain UDP/53 DNS hijacking is the
most reliable interception point on macOS (pf
rdr+ hairpin NAT is fragile here — see "findings" below). - Served a poisoned
get_sync_infowhosesystem_config.adbdblock contains{"remoteActivation": true, "network": true}— after which the device writes/data/quasar/activate_network_adband starts a rootadbdon0.0.0.0:5555with no ADB authentication. - Connected:
adb connect 192.168.1.82:5555→uid=0(root). The device even ships/bin/su.
Affected: Yandex Station Mini 2, firmware 0.369.1.46 (yandexmini_2,
Unisoc SoC, glibc 2.30 armhf userland on aarch64 kernel). Also reproduced
end-to-end on a Yandex Station Lite — firmware 0.369.1.48 (yandexmicro,
Realtek SoC, aarch64 kernel) — see the Lite variant section
for what changes. The root cause — trusting a publicly compromised CA without
pinning — plausibly affects the whole Quasar-family fleet.
The overall chain follows bitracker's write-up (credited below), but these operational details were learned by hand and are worth recording:
- The config endpoint round-robins over ≥6 frontends
(
213.180.204.80,213.180.193.{76,226,230,45},93.158.134.32, …). Redirecting only some of them lets the boot sync slip through to the real backend. Intercept DNS, not individual IPs — the device resolves fresh at boot and follows whatever you tell it. - The station requests
/get_sync_infodirectly (host not encoded in the path) once it talks to your backend over a direct TLS connection — bitracker's proxy-style/quasar.yandex.net/get_sync_infopath handling must also match the plain form. - A persistent TLS session is kept to one frontend (35-byte keepalives every 5 s); killing the flow forces a re-resolve, which your hijacked DNS answers — no reboot needed to re-hook the device.
- The rootfs remounts rw (
mount -o remount,rw /succeeds as root), so root is also persistent access:/bkupgradeis a writable, surviving partition where a dropbear server can live behind a single/etc/init.d/S99custom-sshhook. - The device runs glibc 2.30 armhf userland on an aarch64 kernel and
ships both loaders, so Debian armhf binaries run as-is (just bundle
libtomcrypt/libtommath/libgmp/libz/libxcrypt).
The same chain reproduces on the Station Lite (yandexmicro, Realtek SoC,
firmware 0.369.1.48) with three deltas — the poisoned get_sync_info
payload, the root adbd on :5555, and the /bkupgrade+S99 persistence
all work unchanged:
- The Lite talks to more backends and prefers IPv6. Besides
quasar.yandex.netit resolvesuniproxy.alice.yandex.net(same frontend pool) and a long tail of*.yandex.nettelemetry hosts, and it asks AAAA first — the router hands out a real global AAAA, and a v6 sync bypasses ARP poisoning entirely. Answer every*.yandex.netlookup: A records → your IP, AAAA → empty answer (device falls back to A). That is whatpoc/dns-spoof-lite.pydoes. - Its DNS timeout is ~1 s. Forwarded replies from the real resolvers
race yours and often arrive after the device already gave up (the device
then emits ICMP port-unreachable for its own query ports). Dropping all
UDP/53 replies to the device with pf makes your spoofed answer the only
one — see the pf snippet in
mitm-setup.md. - It probes before it talks. The Lite TCP-connects to a whole list of
frontend IPs (
213.180.193.{76,226,230},213.180.204.80,93.158.134.32) and closes each without payload — connect-only reachability probes. Don't misread them as failed TLS; watch for the actual/get_sync_info?platform=yandexmicrorequest in the backend log. - Mint the leaf cert with multi-SAN coverage (
quasar.yandex.net,uniproxy.alice.yandex.net,*.alice.yandex.net, …) so whichever endpoint it dials validates. The chain andS99custom-sshinstall are byte-for-byte the Mini 2 procedure.
The root cause — trusting a publicly compromised CA without pinning — is not platform-specific, and neither is the fix status. Confirmed third-party reproductions, by platform and build:
| Platform | Firmware | Status | Source |
|---|---|---|---|
yandexmini_2 (Station Mini 2) |
0.369.1.46 | vulnerable, reproduced here | this repo |
yandexmicro (Station Lite) |
0.369.1.48 | vulnerable, reproduced here | this repo |
yandexmidi (Station 2, YNDX-00051) |
0.369.1.50 | vulnerable — chain works, but see caveats | derangedabbot/yandex-station-2-airplay, issue #3 |
Notes from the yandexmidi report worth reading before trying .50:
- The ADB activation window is short (~30–60 s) and opens only during
initial setup — after a factory reset. If the station gets to finish its
first OTA + reboot with the real cloud config, the window never opens. This
is likely the main source of "not reproducible" reports on
.50builds. - The activation flags in
/data/quasarare revoked by the cloud config after the window on this platform (they persist longer on Mini 2/Lite). - Its
.50trust store (/system/vendor/quasar/certs/ca-certificates.crt, 122 certs) has noAAA Certificate Servicesroot — yet it still accepts a leaf signed by the leaked intermediate. So "fixed in.50" does not hold foryandexmidi; check the platform, not just the version number. - Router-level DNS records (static A + empty AAAA) work instead of ARP spoofing on that setup.
See also ciclos5258/yandex-station-security-research for a Mini 2 attempt where the vector did not reproduce — consistent with the setup-window caveat above.
poc/
fake-quasar.py # TLS backend serving the poisoned get_sync_info payload
dns-spoof.py # answers the station's DNS queries with your IP
dns-spoof-lite.py # Lite variant: all *.yandex.net A→you, AAAA→empty
lite-mitm.py # Lite variant: scoped ARP MITM + pf NAT + backend
grab-shell.sh # polls :5555 and runs the root-shell evidence grab
S99custom-ssh # dropbear persistence hook (both devices)
mitm-setup.md # the full operator walk-through (macOS-specific bits)
vulnerability-report.md # structured report (as submitted to Yandex Security)
All scripts run from a machine on the same network. The station needs a power cycle to re-run its boot sync.
sudo python3 poc/dns-spoof.py # answers quasar.yandex.net → your IP
sudo python3 poc/fake-quasar.py 443 # serves the forged cert + config
./poc/grab-shell.sh # polls :5555, grabs root shellThe forged server certificate and CA key are not included in this repo — the CA key is already public (see the Fluepke/emudrahahahahaha repository and the Mozilla Bugzilla entry). Generate your own leaf cert from that key to reproduce.
- The attack requires LAN presence: put IoT/untrusted devices on a separate SSID/VLAN, or at minimum isolate the speaker.
- Cert pinning or DNSSEC on the device is the firmware-level fix (proposed in the report to Yandex).
- If you already have root:
/etc/init.d/is editable after a remount — but an OTA update reflashes the rootfs, so freeze updates before customizing (and know how to restore the stock OTA binary when you're done).
- 2026-10-21: reproduction completed on personally owned hardware; report submitted privately to Yandex Security.
- Public publication of this reproduction: 2026-10-21.
- bitracker — the original chain concept and config payload shape (iamtsunde.re/blog/yamini2adventures)
- v3rt3p/quasar-server — endpoint behavior reference
- derangedabbot — independent
yandexmidi/.50reproduction and hardening notes (repo) - Fluepke — CA key recovery tooling (emudrahahahahaha)
- Mozilla Bugzilla 2083449 — the CA incident itself
MIT — see LICENSE.