{"slug": "hacking-the-meta-ray-ban-smartglasses", "title": "Hacking the meta ray-ban smartglasses", "summary": "A security researcher achieved arbitrary code execution on Meta's Ray-Ban smart glasses by exploiting firmware up to and including version 126, which did not verify that the Device Access Toolkit (DAT) APK was signed by Meta, allowing installation of a self-signed app. The researcher extracted the full firmware by hooking the Meta AI app's OTAArtifactInfo class with Frida to print the CDN download URL and flipping the GraphQL field device_firmware_full_update_only to true, then dumped payload.bin with payload-dumper-go. The researcher reported the flaw to Meta and agreed to the company's revised bug bounty payout.", "body_md": "Back in July I wanted to check out the pwn2own targets for 2026. Since the rules hadn’t been released yet, I decided I would check last year’s targets in the hope that it would be similar.\n\nImagine my surprise when I managed to get arbitrary code execution on the glasses and they weren’t in the wearables category anymore :’).\n\nIt’s fine though because I still reported it to meta, and after fighting the payout a few times, we settled (ok there was no settling but I agreed to the new payout).\n\nBefore attempting this, I remember telling my friend about it. They said “ooof good luck. These are usually pretty hardened and no-one attempted them last year at p2o.”\n\nMy response: Watch me.\n\n### Acquiring glasses because who’s paying €500 (not me)\n\nFirst step was acquiring the glasses. I am lucky that my brother-in-law had a pair he didn’t really use, so I could steal them for a few weeks.\n\n### First recon\n\nI installed the Meta AI app, which is the app to control the glasses (connecting them, synchronizing pictures, and so on).\n\nIn order to extract the firmware, I obviously wasn’t going to tear the glasses apart and start fiddling with probes n shit. So I went for extracting it using the OTA (over-the-air) update.\n\nWhen connecting the glasses for the first time, it immediately offered to update the firmware. So naturally, I was intrigued. Using JADX, I reversed the Meta AI app to see how the update is fetched.\n\n`OtaArtifactFetcher` prepares and sends the update request. The response is converted into an `OTAArtifactInfo` object containing metadata such as the device/build information.\n\n```\narrayListA0y2.add(new OTAArtifactInfo([...], downloadUri, [...]));\n```\n\nI then notice this assignment in `OTAArtifactInfo`():\n\n```\nthis.downloadUri = str3;\n```\n\nI found that I could just hook into `OTAArtifactInfo` and print `str3` which contained the CDN update URL :))) and then I just printed it.\n\n``` js\nJava.perform(function () {var OTAArtifactInfo = Java.use(\"com.facebook.wearable.common.ota.models.OTAArtifactInfo\");OTAArtifactInfo[\"$init\"].implementation = function (l, j, oTAArtifactType, ..., str3, ...) {   this[\"$init\"](l, j, oTAArtifactType, ..., str3, ...);    console.log(\"downloadUri:\");    console.log(str3);};});\n```\n\nThat URL contained a zip, so that looked like the right candidate. **HOWEVER**, this was around 156 MB and its payload depended on the device’s existing build. It was an incremental update… I wanted the full update.\n\n**Getting the full firmware update**\n\nDecompiling further code revealed a GraphQL input named `device_firmware_full_update_only`. The app normally populated it with false, so I hooked the generic method responsible for adding GraphQL fields and replaced the value with true only when the field name matched exactly. This allowed me to fetch the full firmware update URL.\n\nI was then able to retrieve the zip from the CDN. Unzipping it showed me that this was an Android update, including the **payload.bin**.\n\nUsing [payload-dumper](https://github.com/ssut/payload-dumper-go) on payload.bin, I could dump the images and mount them into a filesystem.\n\nFirmware was acquired but how do I get a shell on the glasses?\n\n### Device Access Toolkit\n\nThere is a developer mode on the Meta AI app that allows devs to write apps that can interact with the glasses. It is called DAT SDK (Device Access Toolkit).\n\nIn order to do that, you need to install the DAT APK on the glasses. The MetaAI app basically downloads it in real-time and fetches the DAT application to install it on the device. Do you see where I am going with this?\n\n### Installing self-signed app on the glasses\n\nUp until and including firmware 126, the firmware did not verify that the DAT APK was signed by Meta. So it was theoretically possible to install any app on the glasses because there was no verification as to if it was a legit app or not.\n\nAn easy-not-so-easy swap was possible. I could, using frida, swap out the legit DAT application for a self-signed application of my own. That meant I could run any app on the glasses.\n\n```\n// hook DAT Installerfield(this, \"$localFile\")  .set(this, replacement);field(this, \"$appName\")  .set(this, \"DAT Probe\");packageField.set(this, cachedPackage);// Meta AI sends my apkreturn invokeSuspend.call(this, result);\n```\n\nNow I have my own self-signed DAT apk running on the glasses! bosh!! 😎\n\n### How to run the DAT APK on the glasses\n\nThe DAT apk runs only when a DAT session is requested by the developer app to interact with the glasses.\n\nSo, in order to run the fake DAT apk, I installed a sample app from Meta called **Camera Access**, which would then create a DAT session with the app on the glasses.\n\nHowever, I couldn’t really check that it was running properly. It was difficult to debug.\n\n### Beep is the new printf\n\nThere is no screen on this pair of sunglasses. No console, no logs, no “yoo your code is running”. So how could I make sure my code was even executing? The answer is by *listening* to it.\n\nI used **android.media.ToneGenerator** in my DAT APK to signal which part of the code was executing:\n\n- 3 beeps: listener started, looking for a Wi-Fi Direct address\n- 5 beeps: found one (in wifi group 192.168.49.x)\n- 4 beeps: TCP 31337 bound, we’re in business\n- 6 beeps: something went wrong\n\nSo using this sequence, I managed to debug a big issue that I hadn’t considered: the Camera Access app was failing because it was expecting the real DAT apk on the glasses. When it couldn’t start DWA, `startDwaReadinessProbe()` would report `START_ERROR_DWA_UNAVAILABLE`.\n\nAnd every time it failed, it closed my connection.\n\n**The fix**\n\nI used frida to hook `startDwaReadinessProbe()` in order to disable the DWA timeout and keep the connection alive.\n\n```\nstartDwaReadinessProbe.implementation =function () {// No operation};\n```\n\n### Bind shell\n\nWhen the Meta AI app wants to sync photos & videos from the glasses, it will create a WiFi Direct group on subnet 192.168.49.x. The glasses will connect to that group and they get an IP address.\n\nIn order to get the glasses to connect to the Wi-Fi Direct group, I used Frida to hook into the CameraAccess app and request the “high link lease” from the Meta AI app.\n\n```\nsub = device.createLinkLease(2, 1, cb); highLeaseSubscriptions.push(sub);\n```\n\nMy implanted app bound a listener on TCP 31337 on that address, which dropped me into an interactive shell.\n\nSo from the phone I could just nc’d into it and sent commands, like `id`, to get shell-level execution in the app’s context.\n\n```\nServerSocket listener = new ServerSocket(31337, 4, address))\nnc <glasses-ip> 31337# iduid=10097(u0_a97) gid=10097(u0_a97) groups=3003(inet) ...great power comes great opsec\n```\n\n**great power comes great opsec**\n\n### Great power requires great opsec\n\nI had this uncontrollable power for about 5 days, and while I was searching for a privesc on the glasses, my shell access had disappeared.\n\nUnfortunately, I either had terrible opsec back then (possible) or unluckily they looked into it and patched it.\n\nBut fear not !!\n\n### Mobile Config\n\nFirmware 127 started enforcing signature verification of the DAT apk, so self-signing my own DAT apk wasn’t working anymore.\n\nThe check was done by `wss_appmanager.signature_verification_enabled` in the MobileConfig file.\n\nThe glasses ask the phone for their config, so the phone fetches it from Meta’s servers and forwards it back over Airship. It controls a bunch of feature flags on the glasses without requiring a firmware update.\n\nOne of those flags requires the firmware to check that apps are signed by Meta before installing them.\n\nIt arrives on the glasses from the paired phone, and the device loads it only after verifying the file is *structurally* correct, not that it hadn’t been tampered with.\n\nThat makes every flag in it, including `signature_verification_enabled`, modifiable by the phone and accepted by the glasses.\n\nSo by flipping a byte in the `0.mctable` (which is the file that holds the values) from `0x80` to `0x00` (enabled to disabled), I was able to **bypass** the signature verification that they were originally enforcing, and re-install my self-signed APK on the glasses. \n\nWe’re soooo back.\n\n### Where to from here\n\nThis was all reported to Meta through their bug bounty platform. They have approved my blogpost which makes me assume it is now fixed.\n\nNow that I had shell access, it was a lot easier to start mapping the attack surface. That, my friends, is a story for another time!\n\nThanks for reading!", "url": "https://wpnews.pro/news/hacking-the-meta-ray-ban-smartglasses", "canonical_source": "https://p0pcycle.com/2026/11/06/hacking-the-meta-ray-ban-smartglasses/", "published_at": "2026-11-06 21:20:48+00:00", "updated_at": "2026-11-06 21:50:19.920375+00:00", "lang": "en", "topics": ["ai-products", "ai-tools", "artificial-intelligence"], "entities": ["Meta", "Ray-Ban", "Meta AI", "Device Access Toolkit", "Frida", "JADX", "payload-dumper-go", "Pwn2Own"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/hacking-the-meta-ray-ban-smartglasses", "markdown": "https://wpnews.pro/news/hacking-the-meta-ray-ban-smartglasses.md", "text": "https://wpnews.pro/news/hacking-the-meta-ray-ban-smartglasses.txt", "jsonld": "https://wpnews.pro/news/hacking-the-meta-ray-ban-smartglasses.jsonld"}}