Artificial truth

The more you see, the less you believe.

[archives] [latest] | [homepage] | [atom/rss]

Expected solution for "Jit in my pants" (Boston key party 2016)
Sun 06 March 2016 — download

Like last year, I gave a hand for the Boston Key party. I did support on irc, annoyed crowell with constant pings, managed to not fuck up the mysql database with my crazy sql-fu, learned that python+ncurses (for the scoreboard) doesn't scale well (eating 12Gb of RAM doesn't count as scaling) and also wrote two challenges:

An easy one about RSA named Bob's hat, that I'd like to dedicate to my former security/crypto teacher, for arguing that implementing textbook RSA was a great idea and not a trigger-happy bi-feet gun. You can find the script to generate it, and to solve it here. If you one day meet someone like my teacher, please throw the script as hard as you can in his face.

The second challenge was more interesting in my opinion, or at least I thought so. A friend of mine teached me to love side-channel attacks, and I try to use them as much as possible: I'm systematically launching my magic_sauce.py script against crackme in ctf, and if often yields the flag in a couple of seconds.

This is why I wanted to do a challenge that should be solved that way, with a twist:

#include <stdio.h>
#include "./jitter-amd64.c"
#include <string.h>
#include <math.h>

int main(int argc, char* argv[]){
    if (argc != 2){
        printf("Usage: %s flag\n", argv[0]);
        return 0;
    }

    int i = 0;
    const char flag[] = {0x46, 0x4d, 0x54, 0x45, 0x50, 0x42, 0x7d, 0x55, 0x33, 0x60, 0x5f, 0x59, 0x45, 0x6c, 0x30, 0x6a, 0x6a, 0x5f, 0x68, 0x59, 0x63, 0x70, 0x70, 0x30, 0x65, 0x6d, 0x75, 0x59, 0x63, 0x76, 0x5f, 0x59, 0x75, 0x34, 0x59, 0x33, 0x35, 0x35, 0x3c, 0x77, 0x0};
    const int flen = sizeof(flag)/sizeof(char); 

    for (i=0; flag[i]; i++){
        while(1) {  // wasting your time
            struct timeval tv;
            gettimeofday(&tv, NULL);  // hook this with LD_PRELOAD
            double x = tv.tv_usec % 10000;

            // I don't want sin(x) to appear in ltrace
            if (x < -M_PI)
                x += 2 * M_PI;
            else if (x > M_PI)
                x -= 2 * M_PI;

            if (x < 0)
                x = 1.27323954 * x + .405284735 * x * x;
            else
                x = 1.27323954 * x - 0.405284735 * x * x;

            if (x == 0.0)  // sin(x) == 0.0
                break;
        }
        if (flag[i] != ((argv[1][i] ^ 5) - 1))
            break;
    }

    if (flag[i] == 0)
        puts("You've got the flag!");
    else
        puts("Nope.");
    return 0;
}

The binary was packed with a private packer, to lift it in a jit'ed VM. Unfortunately, first I uploaded an old binary, that was way too easy, because one just had to put a breakpoint on memcpy, then run strings on the dump to get the flag. I had a much fancier jit implementation that I unfortunately didn't managed to find, so in a hurry, I encrypted the flag, hoping that the VM was strong enough to force people to find another way to get the flag. After all, this challenge was purposely worth only a few points.

The gettimeofday + homemade-sins part is here to counter trivial side-channel attacks, but if you ltrace it, it's obvious:

$ ltrace -c ./37e3e6d777a0e0c44019e19aad6ee7d2 pouet 
Nope.
% time     seconds  usecs/call     calls      function
------ ----------- ----------- --------- --------------------
 67.08    1.036385          52     19888 malloc
 27.05    0.417841          56      7456 gettimeofday
  5.85    0.090327          50      1778 free
  0.01    0.000118         118         1 puts
  0.00    0.000069          69         1 mprotect
  0.00    0.000057          57         1 sysconf
  0.00    0.000054          54         1 posix_memalign
  0.00    0.000054          54         1 memcpy
------ ----------- ----------- --------- --------------------
100.00    1.544905                 29127 total

So one just had to LD_PRELOAD an object implementing something like this:

struct timeval {
    long      tv_sec;     /* seconds */
    long tv_usec;    /* microseconds */
};

int gettimeofday(struct timeval *tv, struct timezone *tz){
    tv->tv_usec = 0;
    return 0;
}

And then, a quick run of the python script from my movfuscator article gets the flag: BKPCTF{S1de_Ch4nnel_att4cks_are_s0_1338}.

Unfortunately, I misconfigurated my Makefile, and some obfuscation options weren't activated, making the reversing process of the crackme way too easier, but since I already uploaded a wrong binary once, I did not want to upload a third one. So I guess a lot of team solved it in IDA radare2 instead.

I hope that this was still a fun challenge, and would really appreciate if you could publish some writeup about it, I'm wondering how people solved it.