Hacking the meta ray-ban smartglasses 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. 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. Imagine my surprise when I managed to get arbitrary code execution on the glasses and they weren’t in the wearables category anymore :’ . It’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 . Before 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.” My response: Watch me. Acquiring glasses because who’s paying €500 not me First 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. First recon I installed the Meta AI app, which is the app to control the glasses connecting them, synchronizing pictures, and so on . In 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. When 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. OtaArtifactFetcher prepares and sends the update request. The response is converted into an OTAArtifactInfo object containing metadata such as the device/build information. arrayListA0y2.add new OTAArtifactInfo ... , downloadUri, ... ; I then notice this assignment in OTAArtifactInfo : this.downloadUri = str3; I found that I could just hook into OTAArtifactInfo and print str3 which contained the CDN update URL : and then I just printed it. js Java.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 ;};} ; That 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. Getting the full firmware update Decompiling 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. I was then able to retrieve the zip from the CDN. Unzipping it showed me that this was an Android update, including the payload.bin . Using payload-dumper https://github.com/ssut/payload-dumper-go on payload.bin, I could dump the images and mount them into a filesystem. Firmware was acquired but how do I get a shell on the glasses? Device Access Toolkit There 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 . In 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? Installing self-signed app on the glasses Up 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. An 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. // 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 ; Now I have my own self-signed DAT apk running on the glasses bosh 😎 How to run the DAT APK on the glasses The DAT apk runs only when a DAT session is requested by the developer app to interact with the glasses. So, 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. However, I couldn’t really check that it was running properly. It was difficult to debug. Beep is the new printf There 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. I used android.media.ToneGenerator in my DAT APK to signal which part of the code was executing: - 3 beeps: listener started, looking for a Wi-Fi Direct address - 5 beeps: found one in wifi group 192.168.49.x - 4 beeps: TCP 31337 bound, we’re in business - 6 beeps: something went wrong So 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 . And every time it failed, it closed my connection. The fix I used frida to hook startDwaReadinessProbe in order to disable the DWA timeout and keep the connection alive. startDwaReadinessProbe.implementation =function {// No operation}; Bind shell When 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. In 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. sub = device.createLinkLease 2, 1, cb ; highLeaseSubscriptions.push sub ; My implanted app bound a listener on TCP 31337 on that address, which dropped me into an interactive shell. So 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. ServerSocket listener = new ServerSocket 31337, 4, address nc