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

BCTF 2017 BOJ

| Toshi Piazza

This weekend we played in BCTF, though we took it a little easy in preparation for upcoming CTFs. However, we were able to solve a challenge with only one other solve, so we decided to write up our progress on it. It essentially boiled down to a sandbox escape.

Escaping the First Sandbox

When we first go to the link in the problem description, we see a web form which presumably compiles and runs C code which we submit. Initially, it does not seem like we can get any output from the server other than “Compilation Error” or “Wrong Answer.” However, it does not seem that there are many security restrictions on the binary; initial tests show that we can’t execve, but we can at least open a socket to create a writeback. First, we attempt to learn a bit more about our environment. In particular, we write our uid to our connect back. It seems we’re running as root! Let’s get a directory structure listing:

/etc/{group,passwd}
/usr/sbin/chroot
/usr/bin/{whoami,id}
/lib64/ld-linux-x86-64.so.2
/root/scf.so
/root/1492329438123677.c.bin
/lib/x86_64-linux-gnu/(lots of shared libs)
/bin/{cp,mkdir,chmod,bash,cat,sh,ls}

At this point, we’re interested by a few things related to the directory structure; first, there’s no flag!? No sweat, we’re probably in a chroot (the flood of messages on IRC from people asking where the flag is confirms this theory). More alarming perhaps is the scf.so file that we see in the same directory as the file that was compiled for us. We dump it to our writeback, and immediately disassemble it.

Initially, it’s apparent that this shared object file is meant to be used as an LD_PRELOAD; normal shared …

...Read more

Boston Key Party 2017 hiddensc

| Mike Macelletti

This is a writeup for the binary exploitation challenge “hiddensc” we solved during Boston Key Party 2017.

hiddensc [200]

Boston Key Party 2017 pwn 200

Description

I know I hid that pesky shellcode page somewhere… (the hidden page will change every ~~10~~ 20 minutes). Please don’t brute force the hundreds of GB of space…

First Look

Running the Binary

We’re given two files, hiddensc which is the binary, and poop.sc, which is the shellcode that runs /bin/sh. Looking at the challenge description, it appears the program puts the shellcode somewhere in memory.

Running the binary locally on port 9001 gives us the following output:

$ ./hiddensc 9001

seeding with 942fa402
[!] shellcode is at 0x2fa5a2820000
[!] page size is 0x1000
[!] pid is 29166
[+] listening on 0.0.0.0 9001

Running subsequent times shows the shellcode does move around and is exactly one page (0x1000 bytes) long. Presumably we want somehow return to the shellcode which will give us a shell on the server.

Main Menu

After we have the binary running on a port we can interact with it. The program is seemingly quite simple

$ nc localhost 9001

[a]lloc, [j]ump : 

We have two options, alloc and jump. Testing each option shows alloc will malloc an array of the size we request and jump will set RIP to the address we give. We also have the option to free the array we allocate, but if we choose not to we cannot free it later.

[a]lloc, [j]ump : a
sz? 1234
free? n

[a]lloc, [j]ump : j
sz? 1094795585
Stopped reason: SIGSEGV
0x0000000041414141 in ?? ()

Running the binary in gdb and connecting shows the program does let us jump to any address we want. Problem is the shellcode moves around every 20 minutes on the remote server …

...Read more