DCTF 2021 - Just In Time

Using frida to get decrypted flag.


Don't fall in (rabbit) holes


We get a binary which just prints Decryption finished.


Using ghidra, we can analyse the binary.

Inside the main of the binary we can see, that their is some binary content and multiple functions called with strncpy in between.

undefined8 main(int argc,char **argv)
  char *key_text;
  char *key_buffer;
  long lVar1;
  undefined8 cipher_0;
  undefined8 cipher_8;
  undefined8 cipher_16;
  undefined8 cipher_24;
  undefined4 cipher_32;
  undefined2 cipher_36;
  undefined cipher_38;
  char *buffer;
  char *key;

  key_text = (char *)malloc(8);
  key_buffer = (char *)read_key(*argv);
  cipher_0 = 0x486765792038261b;
  cipher_8 = 0x754b623167242872;
  cipher_16 = 0x747d4e603566227b;
  cipher_24 = 0x252f764e31333323;
  cipher_32 = 0x46313160;
  cipher_36 = 0x3123;
  cipher_38 = 0;
  cipher_text = (char *)malloc(0x27);
  puts("Decryption finished.");
  buffer = (char *)malloc(0x27);
  lVar1 = FUN_001011c5((char *)&cipher_0,key_text);
  buffer = (char *)FUN_001011c5(buffer,key_text);
  return 0;

I already renamed function and variables, to make it more readable for me. Basically, in the beginning the first 8 bytes from the binary are loaded, then our 'cipher' is initialized, and some XOR operations are done. After the output, their are again multiple XOR operations.

Sadly I tried to reimplement all the XOR operations and print the states of all the variables, between each step. But I didn't get the flag in time for the competition. After the competition I realized using GDB with PEDA, I could just debug. Because PEDA tries to print all parameters at calls, I get the args for every strncpy printed. And the third call had the flag.

Because I was interested, if this could be done more easily I tried to use frida. I found two methods, where I could get the flag pretty fast.

Using frida-trace

frida-trace ./justintime -i 'strncpy'

Or writing my own script and execute it using frida ./justintime -l exploit.js --no-pause

'use strict';

var baseAddr = Module.findBaseAddress('justintime');
var strncpy = Module.findExportByName(null, "strncpy");

Interceptor.attach(strncpy, {

    onEnter: function (args) {
        console.log('[+] Called strncpy @' + strncpy);
        console.log('[+] Dest: ' + args[0]);
        console.log('[+] Src: ' + args[1]); 
        console.log('[+] Len: ' + args[2]); 
        console.log('[+] Src Content: ' + Memory.readCString(ptr(args[1])));

All three methods lead me to the flag, which was.