Monday 22 May 2017

CONFidence conference impression + Random(crypto 100) write-up

Standard
On 18-19 May CONFidence conference took place in Cracow. This was my third IT conference and second cybersecurity con in my life. CONFidence is a con with a long tradition, the biggest cybersecurity meeting of specialist in Poland every year.

Venue

This year it gathered around 800 people, which was awesome on one hand and pretty frustrating at times on the other. There were two tracks, most of the topics were pretty interesting. The problem was that sometimes rooms, especially the one for Track 2, couldn't handle amount of people, considering amount of space and AC. That, and lines for lunch were the only things that were not ideal. Talks were great, I enjoyed the one by @AdamLangePL the most. It was about JavaScript battle between banks and black hats.






Community Corner

On the side of the main tracks, there was something called community corner, it consisted of lightning talks mostly done by http://sekurak.pl and http://niebezpiecznik.pl. They had their stands with riddles and miniCTF, which I was able to solve and win a t-shirt from sekurak on the second day(yey!). 

Chill zone
niebezpiecznik's riddles - you could have won a YubiKey




CTF

As always on CONFidence, there was CTF organised by DragonSector, unfortunately taking part in CTF means that you are not able to watch talks and enjoy everything else about the conference. That's normal due to the fact that solving CTF challenges is really hard and time consuming.
I looked at some of the tasks during the lunch breaks, after first day downloaded some of the files, binaries to look at them in the hotel. The only task that I managed to solve was Random, which is probably on of the easiest tasks in the entire CTF, you can find write-up below. Level of challenges was really high. The most amazing task was probably the one when you were able to hack into drone that was standing inside the cage near the contestants.







To sum up I think that visiting such conferences can really profit in your skill set, as well as motivation to further work and research, eg. right now I'm working on RPi Zero to behave like HID device with OS fingerprinting. There were some guys that wrote pretty complex tool to do exactly that, and a lot more!
Also I got motivated to find/create CTF team for an offline events, maybe we'll join the CTF contest on CONFidence next year, see you then!

Random write-up

We were given random.cpp file and an IP to connect with the netcat to.

random.cpp
#include <fstream>
#include <iomanip>
#include <iostream>
#include <random>
#include <string>

using std::cin;
using std::cout;
using std::endl;
using std::ifstream;
using std::string;

[[noreturn]] void fatal_error(const string& msg)
{
 cout << msg << endl;
 exit(0);
}

class RandStream
{
 std::random_device rd;
 std::mt19937 gen;

public:
 RandStream() : rd(), gen(rd()) {}

 unsigned int NextUInt()
 {
  return gen();
 }
};

int main()
{
 cout << "Let's see if you can predict Mersenne Twister output from just"
      << " six values!" << endl;
 cout << "btw. You have only 5 seconds." << endl;

 RandStream rand{};
 for (int i = 0; i < 6; i++)
  cout << std::hex << std::setfill('0') << std::setw(8) << rand.NextUInt()
       << endl;

 for (int i = 0; i < 5; i++)
 {
  unsigned int num;
  cin >> std::hex >> num;
  if (num != rand.NextUInt())
   fatal_error("Wrong!");
 }

 cout << "Good work!" << endl;

 ifstream f("flag.txt");
 string flag;
 f >> flag;
 if (f.fail())
  fatal_error("Reading flag failed, contact admin");
 cout << flag << endl;
}



After examining the file we see what the task is, given 6 numbers we have to predict what the next 5 will be generated from the Mersenne Twister(19937)

We can determine the state of Mersenne Twister given 624 consecutive numbers, here we are provided with only 6, there is no way we can get the whole state of the generator.
The one thing we need to know is that std::mt19937 can be seeded in two ways, one of them is a single unsigned int, the other one is std::seed_seq, as it runs out, std::random_device is not perfect for seeding, the name is a little misleading given that the std::random_device can be implemented to produce the same input on every run, and can be fully deterministic. You can read more here: http://www.pcg-random.org/posts/cpps-random_device.html

It turns out we have only 32bits of entropy which means 2**32 of different seeds. This is bruteforceable to same extent, generating all the 6 number sequences would take around 188GB. We don't need all of them to solve the task. I've generated only 10% of the 32bit range and run the get_flag.py many times.
Generating numbers took around 2hours, running get_flag.py, was successful after ~20 tries or so.

gen_solution.cpp - generate 10% of the range and save it
#include <random>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cstdio>
#include <unistd.h>

int main()
{
 int f = open("generated.txt", O_CREAT | O_APPEND | O_WRONLY);
 unsigned int tab[6*10000];
 int k =0;
 for (unsigned int i = 0; i < 429596799; i++)
 {
  std::mt19937 gen(i);
  if(k==10000) 
  { 
   write(f, &tab, sizeof(tab));
   k = 0;
  }
  for(unsigned int j=0;j<6;j++)
  {
   tab[j + k*6] = gen();
  }
  k++;

 }
}

find_match.c - fast finding matching six numbers (grep, bgrep, ... were to slow)
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#define cacheSize (24*1000*1000)
char fileBuf[cacheSize];

int main()
{
 int pattern = open("pattern", O_RDONLY);
 int file = open("generated.txt", O_RDONLY);

 unsigned int fileSize = lseek(file, 0, SEEK_END);
 unsigned int off = 0;

 char patternBuf[24];

 read(pattern, patternBuf, sizeof(patternBuf));
 unsigned int patternFirst = *((unsigned int*)patternBuf);

 while(off < fileSize)
 {
  lseek(file, off, SEEK_SET);
  read(file, fileBuf, cacheSize);
  for(int i=0; i<cacheSize/24;i++)
  {
   if (*((unsigned int*)(fileBuf + i*24)) != patternFirst) {
    off+=24;
    continue;
   }
   if (memcmp(fileBuf+i*24, patternBuf, 24) == 0)
   {
    printf("%d", off);
    return 0;
   }
   off = lseek(file, off, SEEK_CUR);
  }
 }
 puts("no match");
}

find_next_5.cpp - given the seed, discard 6 and generate next 5 numbers from the PRNG
#include <fstream>
#include <iomanip>
#include <iostream>
#include <random>
#include <string>

using std::cin;
using std::cout;
using std::endl;
using std::ifstream;
using std::string;

int main(int argc, char *argv[])
{
 unsigned int seed = atoi(argv[1]);

 std::mt19937 gen(seed);
 for(int i=0;i<6;i++) gen();
 for(int i=0;i<5;i++) printf("%x\n", gen());
}

get_flag.py - wrapper on all of the above, and communication with the server
from pwn import *
import sys
import random
from subprocess import check_output
import binascii
import random

def bintohex(s):
    return ''.join([ "%x"%string.atoi(bin,2) for bin in s.split() ])

r = remote("random.hackable.software", 1337)
r.recvline()
r.recvline()

out = []
for i in range(6):
    number = int(r.recvline()[:-1], 16)
    out.append(p32(number))

sixn = b"".join(out)
open("pattern", "wb").write(sixn)
o = check_output(['./find_match'])

if "no" in o:
 sys.exit(0)

seed = int(o)/24
print "Seed: %d" % seed


o = check_output(['./find_next_5', str(seed)])

r.sendline(str(o))
r.interactive()

After running:
while true; do python get_flag.py; done
and waiting like 10 seconds, we got the flag, unfortunately I don't have it right now.