Binary Leetness Walkthrough (DefCon CTF 2008 Qualifiers)
This category tests diagnostic and pattern-finding skills. It tends to be a giant time-suck.
100: I can has libc?
First step is to shove the binary through a disassembler to see what it's doing:
$ ndisasm -u reversing100-4b4a40e30fc3cadf2e2a05bbd7acf1f9 
00000000  89C7              mov edi,eax
00000002  89DE              mov esi,ebx
00000004  89CA              mov edx,ecx
00000006  C1E902            shr ecx,0x2
00000009  F3                db 0xF3
0000000A  A5                movsd
0000000B  89D1              mov ecx,edx
0000000D  81E104000000      and ecx,0x4
00000013  F3                db 0xF3
00000014  A4                movsb
00000015  C3                ret
What is happening here is that source (esi) and destination (edi) are set up, and the size is saved before being divided by 4 (shr 2). Then that many 4-byte chunks are copied (movsd). Finally the remainder from the division is used to copy 1-byte chunks (movsb). This routine is simply copying a given size of memory from one place to another in an efficient manner.
First guess was "memcpy", which was the right answer.
200: Stupid firewall tricks
Disassembly showed the binary simply making a connection on port 1234 and printing whatever it received. When trying to actually run the binary, it didn't appear to work. It acted like the connection had been refused.
On more careful examination of network traffic, we discovered that the TCP connection was being reset from our side. Wireshark had colorful things to say about the packet from the remote server:
# tshark -Vn port 1234
Frame 1 (54 bytes on wire, 54 bytes captured)
...
Internet Protocol, Src: 1.2.3.4 (1.2.3.4), Dst: 209.195.0.124 (209.195.0.124)
...
Transmission Control Protocol, Src Port: 31337 (31337), Dst Port: 1234 (1234), Seq: 0, Len: 0
...
    Flags: 0x02 (SYN)
...
Frame 2 (60 bytes on wire, 60 bytes captured)
...
Internet Protocol, Src: 209.195.0.124 (209.195.0.124), Dst: 1.2.3.4 (1.2.3.4)
...
Transmission Control Protocol, Src Port: 1234 (1234), Dst Port: 31337 (31337), Seq: 558752047, Ack: 16777216, Len: 0
...
    Acknowledgment number: Broken TCP.
...
    Flags: 0x12 (SYN ACK)
...
Frame 3 (54 bytes on wire, 54 bytes captured)
...
Internet Protocol, Src: 1.2.3.4 (1.2.3.4), Dst: 209.195.0.124 (209.195.0.124)
...
Transmission Control Protocol, Src Port: 31337 (31337), Dst Port: 1234 (1234), Seq: 16777216, Ack: 558752048, Len: 0
...
    Flags: 0x04 (RST)
...
In a normal TCP session, you send SYN, get back SYN ACK, and send ACK. After that, data starts flowing. In this case, the SYN went out, the SYN ACK came back with a broken sequence number, and our side drops the connection with RST. Each side of the TCP connection is supposed to acknowledge the other side's sequence number, plus 1. If we send SYN(seq:5), we expect to get SYN-ACK(ack:6,seq:1000), and then we send ACK(ack:1001,seq:6). In the captured traffic the acknowledged sequence number was totally wrong.
After playing with various initial sequence numbers, a pattern started to be discovered. If we sent a seq of 0, we'd get back an ack of 16777216 (where we expected 1). A seq of 1, ack of 33554432 (where we expected 2). Looking at the numbers in hex, it was immediately obvious that the acks were being endian flopped: 0x00000001 into 0x01000000 == 16777216, etc.
The solution was to first stop our RST packet:
# iptables -I OUTPUT -p tcp --tcp-flags RST RST -j DROP
Then to generate traffic with carefully controlled sequence and acknowledgement values, matching the endianness breakage:
#!/usr/bin/env python
from scapy import *
import struct

def flop(x):
    return struct.unpack("<L", struct.pack(">L", x))[0]

p = IP(dst="209.195.0.124")/TCP(dport=1234, flags="S", seq=0, sport=31337)
send(p)
ps = sniff(timeout=5, filter="port 1234")

for r in ps:
    if TCP in r and r.sport == 1234:
        a = IP(dst="209.195.0.124")/TCP(dport=1234, flags="A", sport=31337, seq=flop(r.ack), ack=flop(r.seq)+1)
        send(a)
        ans = sniff(timeout=5, filter="port 1234")
        for r in ans:
            if TCP in r and r.sport == 1234:
                print r.load
Finally we could transmit the traffic through the drunk Kenshoto router, fetching the first data-carrying packet after the TCP handshake:
# ./mangle-seq
Am I blacking out?
After quals, Kenshoto gave us a copy of the FreeBSD kernel module that they used to confuse the TCP session. This is handy for reproducing the challenge.
300: I got your libc function right here buddy!
Anyone got a good reason for the correct answer? Write-ups welcomed...
400: Knock knock...
To help curious hackers reproduce a viable environment for poking at this challenge, here is the prerequisite kernel module. If you don't want to spoil the fun, don't look at this module before playing. (Use "kldload -v ./captain_key.ko; kldload -v ./rev400.ko")
During the Quals, 1@stPlace didn't finish Binary Leetness 400 (but we did prevail afterwards). For the binary analysis, is with great thanks that we present the Binary Leetness 400 walk-through from Routards. Our own notes on producing the solution:
The module keeps a kind of record of the last set of syscalls that have been called in the (unused by freebsd6.3) upper bits of the p_sflag field in the process struct. It shifts the upper 16-bits of the p_sflag field left by two bits and xors in the lowest 3 bits of the syscall number every time a syscall is made. When those upper bits from the previous syscall record equal 0x5be9 and the current syscall is #1, it issues the uprintf call with "currentpid" as the arg.
In order to determine the right order for the syscalls:
#!/usr/bin/perl

my %syscalls = (
        EXIT => 1,
        READ => 3,
        WRITE => 4,
        OPEN => 5,
        CLOSE => 6,
        FCHDIR => 13,
        MKNOD => 14,
        CHMOD => 15,
        CHOWN => 16,
        RECVMSG => 27,
        SENDMSG => 28,
        RECVFROM => 29,
        ACCEPT => 30,
        GETPEERNAME => 31,
        GETSOCKNAME => 32,
);

my $syscall_target = 0x5be9;

my $syscall_history = 0;

for $syscall (@ARGV) {
        my $syscall_bits = $syscalls{$syscall} & 0x7;

        $syscall_history *= 4;
        $syscall_history &= 0xffff;

        $syscall_history ^= $syscall_bits;

        printf "Looking for %016b: Current = %016b: $syscall\n", $syscall_target, $syscall_history;
}

my $syscall_diff = $syscall_history ^ $syscall_target;
if ($syscall_diff == 0) {
        print "Matched!\n";
} else {
        my $diff_string = sprintf("%016b", $syscall_diff);
        $diff_string =~ tr/01/_X/;
        print "                                        $diff_string\n";
}
After doing a little experimentation:
$ ./rev400.pl OPEN READ CLOSE CHMOD READ CHMOD RECVFROM
Looking for 0101101111101001: Current = 0000000000000101: OPEN
Looking for 0101101111101001: Current = 0000000000010111: READ
Looking for 0101101111101001: Current = 0000000001011010: CLOSE
Looking for 0101101111101001: Current = 0000000101101111: CHMOD
Looking for 0101101111101001: Current = 0000010110111111: READ
Looking for 0101101111101001: Current = 0001011011111011: CHMOD
Looking for 0101101111101001: Current = 0101101111101001: RECVFROM
Matched!
Since only the lowest three bits of the syscall number are xored into the recorded value, many different syscall patterns will work to trigger the necessary code path. As an example, the RECVFROM syscall listed above could also be an OPEN syscall and the bit pattern is the same.
As determined, we need to do syscalls in the following order: open, read, close, chmod, read, chmod, open, exit. After that, we trigger the uprintf. The guess being that the (misdirectingly badly named) "currentpid" symbol is satisfied by the captain_kmod module since there aren't any existing symbols in the freebsd kernel that would match that. Viable syscall harness:
int main(int argc, char *argv[])
{
        open("foo", 1, 0x10);
        read(100, 0xabcdef0, 100);
        close(50);
        chmod("foo", 0x10);
        read(100, 0xabcdef0, 100);
        chmod("foo", 0x10);
        open("foo", 1, 0x10);
        exit(1);
}
Produces:
$ ./syscall-knocker
BODY MASSAGE!
500: Can you hear me now?
The Sexy Pandas did a great write-up, check it out! (We're also keeping a mirror of it locally just in case.)

ctf 2008 quals