code hacking, zen coding

HackYou CTF – PPC 200 – Oscaderp Forensic Writeup

Don’t you love challenge with README ?

We need your help, soldier!

Your goal today is to help us obtain the access to Oscaderp Corp mainframe.
Our intelligence has managed to install a keylogger and a formgrabber on some bad person's work laptop. You don't need his name to do your job.
Everything worked as planned, the victim visited mainframe's authentication page, https://authen.intranet/, and started to type in the password.
But when he had a couple characters left, the keylogger got busted and hard-killed by him.

Present intelligence evidence:
[*] The password that's being used is 1,048,576 characters long.
[*] According to our calculations, our keylogger managed to capture 1,048,568 password keystrokes.
[*] Formgrabber remained unnoticed, and in a few hours we've got the logs with successful mainframe authentication.
    The only major problem: they use client-side MD5 to protect the password from being eavesdropped.
[*] We also managed to acquire the source code of the authentication mechanism

You can find all the necessary files in the archive.

YOUR GOAL: obtain the password to the mainframe, and post its SHA1 hash as the flag.

So to summarize:
– We have a partial password (1,048,568 password keystrokes)
– We know the md5 of the whole password: 287d3298b652c159e654b61121a858e0
– We need to bruteforce in a smart way the 8 missing bytes

To solve this challenge we will use a property of MD5: we can hash chunks of the message in succession as long we respect the block size of the algorithm: 64 bytes

Our strategy:
– Init MD5
– Hash 1048568 – 1048568 % 64 = 1048512 bytes
– Bruteforce the missing bytes, using MD5_Update to update the partial hash, much faster than hashing the whole string from scratch for each brute force round

#include <sys/types.h>
#include <sys/uio.h>
#include <openssl/md5.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#define HASH_TARGET "287d3298b652c159e654b61121a858e0"

char *byte_to_hex(unsigned char *buffer)
  static char hex[MD5_DIGEST_LENGTH * 2];
  int c;

  for(c=0 ; c < MD5_DIGEST_LENGTH ; c++)
    sprintf(hex + c*2, "%.2x", buffer[c]);

  return hex;

void hex_to_byte(unsigned char *buffer, unsigned char *digest)
  int c;
  unsigned char number[3];

  for(c=0 ; c < (MD5_DIGEST_LENGTH << 1) ; c += 2)
    memcpy(number, buffer + c, 2);
    number[2] = 0;
    sscanf(number, "%x", &digest[c >> 1]);

int main(int argc, char *argv[])
  FILE *f;

  unsigned int i;

  static unsigned char guess_digest[MD5_DIGEST_LENGTH];
  static unsigned char target_digest[MD5_DIGEST_LENGTH];

  MD5_CTX md5init;

  hex_to_byte(HASH_TARGET, target_digest);
  printf("Target hash=");

  f = fopen("password", "r");
  fread(password, 1, PASSWORD_PARTIAL_LENGTH, f);

  MD5_Update(&md5init, password, PASSWORD_INIT_LENGTH);

  for(i = 0 ; i < pow(10, PASSWORD_MISSING_LENGTH) ; i++)
     MD5_CTX md5update;

     memcpy(&md5update, &md5init, sizeof(MD5_CTX));

     sprintf((char *)password + PASSWORD_PARTIAL_LENGTH, "%i", i);

     MD5_Update(&md5update, password + PASSWORD_INIT_LENGTH, MD5_CBLOCK);
     MD5_Final(guess_digest, &md5update);

     if (memcmp(guess_digest, target_digest, MD5_DIGEST_LENGTH) == 0)
        printf("WIN %s %08u\n", byte_to_hex(guess_digest), i);

  return 0;


$ gcc -O3 -o crack crack.c -lcrypto
$ ./crack
Target hash=287d3298b652c159e654b61121a858e0
WIN 287d3298b652c159e654b61121a858e0 69880983

We add those missing keystrokes to the keylogger content and we get the SHA1: