# Hacking the meta ray-ban smartglasses

> Source: <https://p0pcycle.com/2026/11/06/hacking-the-meta-ray-ban-smartglasses/>
> Published: 2026-11-06 21:20:48+00:00

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 <glasses-ip> 31337# iduid=10097(u0_a97) gid=10097(u0_a97) groups=3003(inet) ...great power comes great opsec
```

**great power comes great opsec**

### Great power requires great opsec

I had this uncontrollable power for about 5 days, and while I was searching for a privesc on the glasses, my shell access had disappeared.

Unfortunately, I either had terrible opsec back then (possible) or unluckily they looked into it and patched it.

But fear not !!

### Mobile Config

Firmware 127 started enforcing signature verification of the DAT apk, so self-signing my own DAT apk wasn’t working anymore.

The check was done by `wss_appmanager.signature_verification_enabled` in the MobileConfig file.

The 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.

One of those flags requires the firmware to check that apps are signed by Meta before installing them.

It 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.

That makes every flag in it, including `signature_verification_enabled`, modifiable by the phone and accepted by the glasses.

So 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. 

We’re soooo back.

### Where to from here

This was all reported to Meta through their bug bounty platform. They have approved my blogpost which makes me assume it is now fixed.

Now 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!

Thanks for reading!
