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 at their 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.