Running the binary results in no output - it’s doing something, we just weren’t
sure what, exactly. Time to strace!
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.
And then we invoke the three-eyed-fish executable with our stubbed-out ptrace():
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!
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