HUNTING QUEST NATIVE BUGS

Posing toward memory corruption

@datalocaltmp

bottomimg

$whoami

  • Luke a.k.a. @datalocaltmp
  • Mobile reverse engineer & vulnerability researcher
    • Particularly focused on Android
  • Virtual Reality Enthusiast

bottomimg

Overview

  • What is the Android native layer?
  • Where does the Quest interact with the native layer?
  • Approaching native research (ghidra, frida, gdb/lldb, ASAN, etc.)
  • Bug Examples:
    • Glyph parsing to Guardian DOS
    • Device permanent lock-out (Whoops)
    • Remote memory corruption in libovravatar2p.so

What is the Android native layer?

bottomimg

Android Architecture

bottomimg

Android Architecture

bottomimg

What does that mean for the Quest?

bottomimg

Quest Processes & Services

bottomimg

com.oculus.vrshell Native Libraries

bottomimg

Native Library Exported Functions

bottomimg

bottomimg

Questions?

bottomimg

bottomimg

Native Research - Attack Surfaces

  • Quest Attack Surface
    • 3rd Party Apps
    • Bluetooth Companion App (com.oculus.twilight)
    • Remote Spaces / Horizons
  • Choose an attack surface and enumerate entry points and goals
    • Entry point: OpenXR API calls to native VR Runtime Service
    • Goal: Modify guardian safety zone

bottomimg

Native Research - Tooling

  • Android Studio - Building OpenXR apps
    • Explore OpenXR/VRAPI attack surface from malicious 3rd party app
  • Jadx - Decompiling Java components of Quest apps/services
  • Ghidra - Decompiling Quest native libraries
  • lldb - Debug Quest native execution
  • Android NDK - libasan.so to help locate memory corruption
    • Helpful for finding location of OOB write when it occurs

bottomimg

Less talk. More do.

  • Bug #1: Glyph parsing in xrChangeVirtualKeyboardTextContext

Why OpenXR API?


bottomimg

Reminder - Privileged Service


bottomimg

Start with OpenXR API Samples


bottomimg

bottomimg

Playing with Virtual Keyboards

  • XrVirtualKeyboard Sample App
    • "Demonstrates Virtual Keyboard where an application has control over keyboard positioning, interaction, and rendering."
  • Initial starting point - Easy to approach and begin interacting with the OpenXR API
    • Includes functionality for presetting keyboard input via xrChangeVirtualKeyboardTextContext function call
      • i.e. Set initial keyboard input to a user's name or email

What can we render?

  • What if we fuzz the buffer passed to xrChangeVirtualKeyboardTextContext?
    • Simple C++ fuzzer - no coverage involved - just generates random byte arrays
  • Results in finding Glyph Sequence that crashes the vrruntimeservice
    • Attempts to call a method on an unitialized native object
    • Can be delayed to cause crashes in other 3rd party apps
  • Provide Meta with patch file for OpenXR Sample App with report

bottomimg

bottomimg

Crash Dump


bottomimg

Questions?

bottomimg

How about a bug we can trigger remotely?

  • Bug #2: Deserialization to OOB Write in libovravatar2p.so

bottomimg

What should we target?

  • Exploitation over messaging applications generally involve memory corruption

    • Voice & Video streams are popular targets
  • What is novel about Quest's attack surface?

    • Well in addition to our voice, we also stream our avatar's position / pose
    • Ways we can send our avatar include the built-in Social app or Horizons

bottomimg

bottomimg

Sending Avatar Poses

  1. Social app queries ShellEnv process for a avatar pose
  2. ShellEnv calls ovrAvatar2Streaming_SerializeRecording in libovravatar2p.so and gets a serialized buffer containing the avatar pose.
  3. ShellEnv passes the serialized avatar buffer back to the Social Platform app to send to recipient.

bottomimg

Rendering Avatar Poses

  1. Recipient's Social app provides the received avatar buffer to the ShellEnv process
  2. ShellEnv calls ovrAvatar2Streaming_DeserializeRecording in libovravatar2p.so and renders the avatar pose in the environment

bottomimg

Let's Fuzz That - Avatar Buffers

  • Using a simple Frida script
    • Modify the serialized avatar buffer returned from ovrAvatar2Streaming_SerializeRecording
  • Fuzzing for Dummies (i.e. datalocaltmp):
    Array.from({length: 0x10}, () => Math.floor(Math.random() * 0xff));
  • Pairing this with Memory.writeByteArray(...) is very potent

Example Random Avatar Buffer


bottomimg

Example Random Avatar Buffer


bottomimg

bottomimg

And... Crash!

bottomimg

bottomimg

Unfortunately ...

  • We deserialize our own malformed avatar buffer before we send it
  • Any crashing avatar buffer we would send; first crashes our own process and prevents sending :'(
  • Perhaps we could patch out that call to ovrAvatar2Streaming_DeserializeRecording?
    • Maybe there is an easier way....

There is - Tape Patch!

bottomimg

Tracking Lost - No Deserialization!

bottomimg

bottomimg

Solution

  • When we get into the "Lost Tracking" state we still serialize and send our own untracked avatar
    • But...
  • We do not deserialize any avatar buffers because our environment cannot be created - and thus we cannot render an avatar within it
  • What happens now that we can send that malformed avatar buffer?

Huzzah - Remote Crash!


bottomimg

Wait - there's another...


bottomimg

and more...


bottomimg

and more...

bottomimg

and more...


bottomimg

bottomimg

What's happening here?

The same input is causing multiple different crashes in various locations

bottomimg

Reversing Time

  • Where should we start? We can load libovravatar2p.so into Ghidra but which backtrace should we use?
  • Assumption: the crashes are happening all over the place because random heap objects are being corrupted via an OOB write
  • Solution - use the NDK's libasan.so:
    setprop wrap.com.oculus.shellenv LD_PRELOAD=/data/local/tmp/libclang_rt.asan-aarch64-android.so
  • Forces the process to use ASAN and crash when the OOB write occurs

libasan.so Back Trace


bottomimg

bottomimg

OOB Write

  • Able to reverse this back trace to perform root cause analysis
  • Memory corruption is occuring when a 0-filled buffer is copied into a smaller buffer
  • This memcpy corrupts adjacent heap objects with 0's
  • May be interesting to see if we can groom an object into an exploitable spot...

bottomimg

But that's where this story ends.

  • Reported the bug to Meta with the associated PoC
  • Bug also affected Horizon - allowing for remotely crashing all users in a world

bottomimg

Final Thoughts

  • When should we stop our native research? When we have a Root-cause? When we can get RCE?
  • How do you feel about native research? Hopefully this sheds some insights
  • Native research is really difficult - don't get discouraged

bottomimg

Questions?

bottomimg

Thank you!

Hi everyone, here today to talk about my experience hunting native bugs in the Quest. Hopefully the subtitle "Posing towards memory corruption" is clearer in a moment when we get into the slides :).

My name is Luke - but I go by @datalocaltmp on twitter and dm's are always open I'm a mobile reverse engineering and vulnerability researcher - specifically focused on Android And I am also a virtual reality enthusiast - so this project was really a special opportunity.

So today we're going to talk about native bugs in the Quest; and I don't want to assume everyone necessarily knows what that means - so here is the general flow of my talk today. First we'll go through; what are these native bugs and why are they important? Then we'll go through where that intersects with the Quest. I'll cover what tools I use when I do this research and perhaps that is useful for yourselves in your research. primarily these tools are decompilers like Ghidra; dynamic instrumentation tools like frida, and debuggers like lldb and gdb Finally, I'll go through three unique bugs I've found in the quest - one of which was remotely triggerable. These bugs also represent three different attack scenario's and I think they make good examples for other researchers.

So what is the Android native Layer? I imagine some are familiar but perhaps not everyone. Note - The quest VR Headset is based on the Android Operating System so many of the paradigms for Android security research apply here.

Here is an image pulled straight from Google's Android documentation. The highlighted portion of this architecture diagram is the native layer. Though with all abstractions there is quite a bit that is being hidden. But for the most part - this is showing how we have Java API's/System Apps/3rd Party Apps at the layer above the native one. Keep in mind these native libraries are most-often going to be written in memory unsafe languages - this is where there is a real risk of potent vulnerabilities.

Then then Java apps can talk to the Native layer through the Java native interface (note that all JNI calls will start with Java_) Note that these libraries can be packaged within the app itself or may be provided by the operating system.

That's all well and good; but what does that mean for the Quest in particular.

Well from the Quest we can see a bunch of native services as well as application process running; if we use frida we can inspect these processes more deeply to see additional native libraries loaded within them.

Foreshadowing later portions of this talk. Lets inspect the ShellEnv process; we can use the process keyword to find native libraries like `libovravatar2p.so` are loaded into the process.

And more foreshadowing. We can see exported function calls from the `libovravatar2p.so` native library that can be called by other native libraries or Java directly (kinda a lie because it isn't named Java_X for JNI definition).

Finally we can kill some of the native services in Quest and see the affects for users - more foreshadowing - if we are able to crash the vr runtime service we can see the Guardian boundary, which is responsible for user safety, can be killed.

That's a super high-level of the Android native layer and where it intersects with the Quest. Happy to take as many questions now before I continue on with approaching Native research and some case studies.

Great way of starting native research is understanding the attack surface; for quest there are three that I was interested in: 3rd party apps, the bluetooth companion app, and remote messaging components like the built-in social platform and Horizons. Once I picked an attack surface I would figure out all the ways I could get to native code.

Finally we can kill some of the native services in Quest and see the affects for users - more foreshadowing - if we are able to crash the vr runtime service we can see the Guardian boundary, which is responsible for user safety, can be killed.