Dissecting LLVM Obfuscator Part 1

| Kareem El-Faramawi | Toshi Piazza

LLVM Obfuscator is an industry-grade obfuscator which we have encountered frequently in the past few years of CTFing. This blog post documents our work in understanding the design of the obfuscator itself, as well as any possible weaknesses in the implementations of the obfuscation passes. We use this work to automate the task of emitting cleaned and working binaries via Binary Ninja.

Introduction

The open source LLVM Obfuscator manifests as 3 relatively disjoint LLVM passes, each implementing some sort of obfuscation that obscures the CFG or arithmetic computation of the original program in some way.

Also note that, due to the fact that these passes operate over LLVM IR, it supports almost every architecture under the sun.

Information for each of the three passes can be found here:

These are simply the documentation pages maintained by the authors for each respective pass. However, if the documentation is deficient, the source is also an obvious ground truth.

Unfortunately, the llvm-obfuscator repo maintains multiple branches, one for each version of LLVM that that branch targets, with a clone of the entire LLVM repository, so it’s easy to get lost in the code. The passes for the 4.0 branch can be found here, in the lib/Transforms folder, as is customary for LLVM passes.

This blog post will focus on the control flow flattening pass, as we find it to be one of the most interesting passes in the LLVM Obfuscator ecosystem, as well as the most effective.

Control Flow Flattening

We can visualize the effects of the Control Flow Flattening pass as the following CFG transformation:

  1. Collect all of the original basic blocks in a CFG
  2. Lay them down flat “at the bottom” of the CFG, and remove all original edges
  3. Add …
...Read more

HXP 2017 pdf.pdf

| Lense

Challenge description

RTFM! and a link to pdf.pdf.

Our approach

The PDF contained a bunch of different types of content, like text, a vector drawing, a bitmap image, embedded fonts, internal and external links, annotated rectangles around the links, table of contents metadata, and presumably the flag. It ended with a link to the PDF spec, and we took that to mean that we should write a PDF parser.

PDF file format overview

It turns out that while rendering PDFs is extremely painful, just parsing a PDF is only moderately painful, and the spec isn’t as bad as I was expecting.

File sections

Header

The first line is a comment (starting with %) with the PDF version number. The challenge used version 1.5. The second line is a comment with 4 non-ascii bytes, designed to keep text viewers from trying to render PDFs as plaintext.

Body

The body is a series of objects. They aren’t rendered implicitly, but they can be referenced by other objects and the XRef table at the end.

Footer

The footer can have a whole bunch of different types that render the PDF differently, but since we were just trying to parse the PDF, we didn’t deal with it much. At a high level, the footer contains a cross-reference table that points to byte-offsets in the file of objects.

Object types

In this PDF, the body consisted entirely of indirect objects. Those indirect objects contained streams of compressed data, which contained one or more direct objects of arbitrary type. I’m sure there are many other ways that PDFs can be structured, but we just had to deal with this.

Indirect objects

Format:

<object number> <generation number> obj
<contents (stream)>
endobj

In our PDF, generation number was always 0 and the contents …

...Read more

CSAW Quals 2017 FuntimeJS

| pernicious

FuntimeJS

This challenge was formally Part 2 of LittleQuery (Web). Description:

JavaScript is memory safe, right? So you can’t read the flag at physical address 0xdeadbeeeef, right? Right?

This was a very interesting challenge from CSAW Quals 2017 (although whether a funtime was had is still questionable…). We are given a web page where we can submit javascript, and a link to the open source project that will run it, runtime.js, an “operating system…that runs JavaScript.” Because running javascript in ring 0 is just what this world needs… This writeup is a bit long, skimming is not discouraged.

Step 1: Arbitrary Read/Write

Finding a bug was a lengthy process of going through the source and trying things out. The syscalls seemed a good place to start, especially a few:

// runtime.js syscalls: Low level system access
DECLARE_NATIVE(BufferAddress);       // Get buffer physical address
...
DECLARE_NATIVE(GetSystemResources);  // Get low-level system resources

console.log(__SYSCALL.bufferAddress(new Uint8Array(17))) => [ 17, 510626304, 0, 0, 0, 0 ]

Huh, that second entry looks suspicously like a memory address… Looking a bit into the source for getSystemResources (some stuff is cut out):

NATIVE_FUNCTION(NativesObject, GetSystemResources) {
  LOCAL_V8STRING(s_memory_range, "memoryRange");
                                       //     vvvvv   memoryRanges's type
  obj->Set(context, s_memory_range, (new ResourceMemoryRangeObject(Range<size_t>(0, 0xffffffff)))
           ->BindToTemplateCache(th->template_cache())
           ->GetInstance());
}

and following the bread crumbs…

NATIVE_FUNCTION(ResourceMemoryRangeObject, Block) {
  auto base = static_cast<uint64_t>(arg0->NumberValue(context).FromJust());
  auto size = static_cast<uint32_t>(arg1->Uint32Value(context).FromJust());
  Range<size_t> subrange(base, base + size);
  if (!subrange.IsSubrangeOf(that->memory_range_)) {
    THROW_RANGE_ERROR("block: out of bounds");
  }                             //    vvvvv  return type of memoryRange.block()
  args.GetReturnValue().Set((new ResourceMemoryBlockObject(
                               MemoryBlock<uint32_t>(reinterpret_cast<void*>(base), size)))
                            ->BindToTemplateCache(th->template_cache())
                            ->GetInstance());
}

NATIVE_FUNCTION(ResourceMemoryBlockObject, Buffer) {
  PROLOGUE;
  void* ptr = that->memory_block_.base(); // <---- uses raw void* ???
  auto length = that->memory_block_.size();
  RT_ASSERT(ptr);
  RT_ASSERT(length > 0);
  auto abv8 = v8::ArrayBuffer::New …
...Read more

DEFCON Finals 2017 - Intro & Rubix

| Toshi Piazza

Over the weekend, July 28-31 2017 RPISEC competed with Lab RATs and Techsec in DEFCON Finals, one of the most important CTFs of every year. Only 15 teams in the world get to qualify for the event each year, and our team under Lab RATs was able to earn the right to compete among 14 other globally professional teams.

This writeup covers the first challenge presented at DEFCON Finals, which kept us on our toes throughout the competition. It was deceptively simple, and also served as an introduction to cLEMENCy, a terrifying 9-bit middle endian architecture, as well as to the toolchain that was used to create all future binaries.

The majority of this writeup will involve getting our feet wet with cLEMENCy while setting up a comfortable environment for reverse engineering the later challenges released at DEFCON.

DEFCON Challenge Format

A link to the challenge binary can be found here

For the CTF we are given only an emulator and a debugger to solve each of the challenges; these challenges are compiled for a 9-bit middle endian architecture, cLEMENCy. When we first run the rubix challenge under this emulator, we note that only garbage gets printed to the terminal. This output doesn’t change each run, and it’s puzzling what exactly this is. What if it’s the emulator printing out 9-bit ascii instead of 8? Indeed, when we transform from 9-bit to 8-bit between the debugger and our terminal, we end up with the following:

This service implements a rubix cube. Solve the cube and win.

Give me 54 Bytes(ie: 53,12,5,etc): <input 54 comma-delimited chars>

                Top

             R6 R7 B0
             L1 T4 R5
             A0 B7 R2

  Left         Front       Right       Back

L8 L7 L0     T0 A7 F0    R8 F3 L6    B6 T3 T6
T5 L4 …
...Read more

Google CTF Quals 2017 - Moon

| Toshi Piazza

This writeup is for the reversing challenge “Moon” we solved during 2017 Google CTF Quals. This writeup and 3 others were also submitted to the Google CTF Writeup Competition.

Dealing with GLEW

A big problem we noticed early on was the use of GL3W, which generates code to lazily load all OpenGL functions at runtime—at all places where an OpenGL function was used we would simply see a call to some offset in the data section. Unfortunately we couldn’t find a script to re-symbolize function calls, but we plan to make one soon-ish :)

We can see the huge routine which calls LoadLibrary on every OpenGL function, at sub_4032c0.

GLEW lazy loading functions

Instead of symbolizing (by hand) all of the symbols, we only bothered to load ones which had valid XREFs to them, saving a bit of time.

Running the Program

Unfortunately all the RPI-sold computers from our year are not yet reported to support OpenGL 4.3, so first and foremost we had to patch the OpenGL verification check from 4.3 to 4.2. Surprisingly, this “just worked” despite the code making use of Compute Shaders which I had thought to be introduced in OpenGL 4.3.

The program simply opens up a window, and asks for a password. After we’ve entered 32 characters, the program either responds “good” (presumably), or “Nope”.

Running the program

When we XREF the string Nope, we see that it is used when constructing the texture to be printed for this SDL event loop iteration. Not too far from “Nope” do we find “Good”, and we notice that “Good” is only selected if a particular global variable is set. We trace this back to the following code in main:

We want should_compute here …

...Read more