Alex's blog

posts feed about

PlaidCTF 2013 writeup for three-eyed-fish (Binary 100)

22 Apr 2013

Summary: Makes calls to ioctl() in an attempt to blink Morse code on the keyboard LEDs

Key: and0u0didnt0even0need0an0arduino

I’m not sure how many people solved this challenge, but I thought it was neat, so I wrote it up. Comments and corrections are welcome.

What We Have

A 64-bit Linux binary. You can download it here.

Goal

Presumably, find a key from the binary somehow!

Getting the key

Running the binary results in no output - it’s doing something, we just weren’t sure what, exactly. Time to strace!

$ strace ./three_eyed_fish-ddeeb1eb3270b8e2c2bedabd3492bab5e83d584c 
execve("./three_eyed_fish-ddeeb1eb3270b8e2c2bedabd3492bab5e83d584c", ["./three_eyed_fish-ddeeb1eb3270b8"...], [/* 18 vars */]) = 0
brk(0)                                  = 0x603000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff9566f9000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=97919, ...}) = 0
mmap(NULL, 97919, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7ff9566e1000
close(3)                                = 0
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\320%\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1732824, ...}) = 0
mmap(NULL, 3845408, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ff95612e000
mprotect(0x7ff9562cf000, 2097152, PROT_NONE) = 0
mmap(0x7ff9564cf000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1a1000) = 0x7ff9564cf000
mmap(0x7ff9564d5000, 15648, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7ff9564d5000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff9566e0000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff9566df000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff9566de000
arch_prctl(ARCH_SET_FS, 0x7ff9566df700) = 0
mprotect(0x7ff9564cf000, 16384, PROT_READ) = 0
mprotect(0x7ff9566fa000, 4096, PROT_READ) = 0
munmap(0x7ff9566e1000, 97919)           = 0
ptrace(PTRACE_TRACEME, 0, 0, 0)         = -1 EPERM (Operation not permitted)
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0} ---
+++ killed by SIGSEGV +++
Segmentation fault

From the above, it’s pretty obvious that the binary is using the ptrace(PTRACE_TRACEME) trick to see if it is being debugged.

Typically, ptrace() is used by some process in order to trace, or debug, another process. Using ptrace() with PTRACE_TRACEME, however, does the inverse — it is called by the debugee to indicate that it is to be debugged by its parent. In this case, the call to ptrace(PTRACE_TRACEME) is used as a quick, easy way to detect the presence of a debugger — the call will fail (return something other than 0) if a debugger is already attached, allowing the binary to commit Seppuku before we can get a look at it.

Fortunately, there is a well-known and easy way around this: using LD_PRELOAD with a mocked-up implementation of ptrace(), as detailed in this blog post and probably elsewhere.

// fakeptrace.c
#include <stdio.h>

long ptrace(int x, int y, int z)
{
  puts("== ptrace called ==");
  return 0;
}

And then we invoke the three-eyed-fish executable with our stubbed-out ptrace():

$ gcc -shared -fPIC -o fakeptrace fakeptrace.c
$ strace -E LD_PRELOAD="./fakeptrace" ./three_eyed_fish

Doing this resulted in a ton of output. Many calls to usleep() and ioctl() — it appeared that the binary was attempting to (and failing) to turn the keyboard LED’s on and off! I disclosed my findings to my teammates, and the talented Mak Kolybabi suggested that it might be Morse code. As it turns out, he was right!

Annotated output of strace

If you read through the 649 lines (yes, I counted!) of strace output and carefully convert it to Morse code, you will end up with the key for the challenge: and0u0didnt0even0need0an0arduino