Solution to TUCTF 2017 Cookie Duty

30/11/17 — capitol



Cookie Duty






We were presented with a page that had a simple form to set a name. The server set a cookie named not_admin to 1 when the form was posted.

To get the flag we simply changed the cookie value to 0 and requested the page again, like this:

curl '' -H 'Host:' -H 'Cookie: not_admin=0; user=dGVzdA%3D%3D'

flag was TUCTF{D0nt_Sk1p_C00k13_Duty}

Solution to TUCTF 2017 High Source

29/11/17 — capitol



High Source






This was easier than trivial, only 2 steps:

  • Look at source, get password; I4m4M4st3rC0d3rH4x0rsB3w43
  • enter password be redirected to this:

curl ‘’

flag was TUCTF{H1gh_S0urc3_3qu4ls_L0ng_F4ll}

Solution to TUCTF 2017 The Neverending Crypto

28/11/17 — capitol



The Neverending Crypto






We where presented with a server that encrypted strings with a substitution cipher.

You where also able to give up to twenty characters that the server gave the solution for.

The server didn’t use the complete byte range and it was a hassle to find out what characters that was included in the substitution. The server leaked the plain texts from time to time, and the number of plain text strings that was encrypted was small, only nine different ones.

This meant that we could write some simple heuristics to decide which of the strings to send back, the same plain text character always encrypts to the same encrypted character, so we could look at what characters in the ecrypted text are equal to determine what plain text to send back.

Implemented like this:

import socket
import re
import string
sobj = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
re_question = re.compile(r"is (?P<question>.*) decrypted")
re_match = re.compile(r"a encrypted is (?P<chr>.)")

def workit():
    hint =  sobj.recv(1024)
    print "hint", repr(hint)
    c = re_match.match(hint).groupdict()["chr"]
# What is :BB7RJBE>^R@BE8 decrypted?
    question = re_question.findall(hint)[0]
    print "Question is", question
    answer = ""
    if question[1] == question[10]:
        answer = "how many more??"
    if question[11] == question[13]:
        answer = "something here."
    if question[1] == question[5]:
        answer = "you got lucky.."
    if question[12] == question[13]:
        answer = "you have skills"
    if question[1] == question[2]:
        answer = "good work, more"
    if question[1] == question[9]:
        answer = "you crypto wiz!"
    if question[13] == question[14] and question[1] == question[10]:
        answer = "how many more??"
    if question[1] == question[13]:
        answer = "welcome, hacker"
    if question[1] == question[6] and question[1] == question[13] and question[3] == question[10]:
        answer = "dont forget to "
    if answer == "":
        print "I DO NOT KNOW"
        import sys
    print answer
    sobj.send(answer + "\n")

while True:
    print sobj.recv(1024)

Solution to TUCTF 2017 Future task

27/11/17 — capitol









We received a small c program that implemented a simple hash funktion.

In the program was a string that represented a hashed password and a simple hash implementation, our task was to find out what input string hashed into the byte array named pass.

The hash algorithm consisted of two steps.

  • A reordering of the original input string into a array in memory. Implemented in the function genMatrix.
  • The creation of the hashed by adding different bytes from the original string together. Implemented in the function genAuthString.

This was the source code of the given problem:

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

void genMatrix(char mat[5][5], char str[]) {
        for (int i = 0; i < 25; i++) {
                int m = (i * 2) % 25;
                int f = (i * 7) % 25;
                mat[m/5][m%5] = str[f];

void genAuthString(char mat[5][5], char auth[]) {
        auth[0] = mat[0][0] + mat[4][4];
        auth[1] = mat[2][1] + mat[0][2];
        auth[2] = mat[4][2] + mat[4][1];
        auth[3] = mat[1][3] + mat[3][1];
        auth[4] = mat[3][4] + mat[1][2];
        auth[5] = mat[1][0] + mat[2][3];
        auth[6] = mat[2][4] + mat[2][0];
        auth[7] = mat[3][3] + mat[3][2] + mat[0][3];
        auth[8] = mat[0][4] + mat[4][0] + mat[0][1];
        auth[9] = mat[3][3] + mat[2][0];
        auth[10] = mat[4][0] + mat[1][2];
        auth[11] = mat[0][4] + mat[4][1];
        auth[12] = mat[0][3] + mat[0][2];
        auth[13] = mat[3][0] + mat[2][0];
        auth[14] = mat[1][4] + mat[1][2];
        auth[15] = mat[4][3] + mat[2][3];
        auth[16] = mat[2][2] + mat[0][2];
        auth[17] = mat[1][1] + mat[4][1];

int main() {
        char flag[26];
        printf("What's the flag: ");
        scanf("%25s", flag);
        flag[25] = 0;

        if (strlen(flag) != 25) {
                puts("Try harder.");
                return 0;

        // Setup matrix
        char mat[5][5];// Matrix for a jumbled string
        genMatrix(mat, flag);
        // Generate auth string
        char auth[19]; // The auth string they generate
        auth[18] = 0; // null byte
        genAuthString(mat, auth);       
        char pass[19] = "\x8b\xce\xb0\x89\x7b\xb0\xb0\xee\xbf\x92\x65\x9d\x9a\x99\x99\x94\xad\xe4\x00";
        // Check the input
        if (!strcmp(pass, auth)) {
                puts("Yup thats the flag!");
        } else {
                puts("Nope. Try again.");
        return 0;

Looking at the genAuthString method, we noticed that all but two of the equations only added two bytes, and we guessed that the flag only contained ascii characters. Since two ascii characters added are never more than 256 we didn’t have to worry about overflows for those, and if we got an overflow on the bytes with 3 values then we could handle that manually.

This meant that the hashing algorithm could be represented as a system of linear equations, and we knew that the flag was on the format TUCTF{…} so we had a seven characters of plain text.

We wrote a small program that printed the equations:

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

void genMatrix(char mat[5][5], char str[]) {
  for (int i = 0; i < 25; i++) {
    int m = (i * 2) % 25;
    int f = (i * 7) % 25;
    mat[m/5][m%5] = str[f];

void printEquations(char mat[5][5], unsigned char auth[]) {
  printf("%c + %c = %u\n", mat[0][0], mat[4][4], auth[0]);
  printf("%c + %c = %u\n", mat[2][1], mat[0][2], auth[1]);
  printf("%c + %c = %u\n", mat[4][2], mat[4][1], auth[2]);
  printf("%c + %c = %u\n", mat[1][3], mat[3][1], auth[3]);
  printf("%c + %c = %u\n", mat[3][4], mat[1][2], auth[4]);
  printf("%c + %c = %u\n", mat[1][0], mat[2][3], auth[5]);
  printf("%c + %c = %u\n", mat[2][4], mat[2][0], auth[6]);
  printf("%c + %c + %c = %u\n", mat[3][3], mat[3][2], mat[0][3], auth[7]);
  printf("%c + %c + %c = %u\n", mat[0][4], mat[4][0], mat[0][1], auth[8]);
  printf("%c + %c = %u\n", mat[3][3], mat[2][0], auth[9]);
  printf("%c + %c = %u\n", mat[4][0], mat[1][2], auth[10]);
  printf("%c + %c = %u\n", mat[0][4], mat[4][1], auth[11]);
  printf("%c + %c = %u\n", mat[0][3], mat[0][2], auth[12]);
  printf("%c + %c = %u\n", mat[3][0], mat[2][0], auth[13]);
  printf("%c + %c = %u\n", mat[1][4], mat[1][2], auth[14]);
  printf("%c + %c = %u\n", mat[4][3], mat[2][3], auth[15]);
  printf("%c + %c = %u\n", mat[2][2], mat[0][2], auth[16]);
  printf("%c + %c = %u\n", mat[1][1], mat[4][1], auth[17]);
  printf("a = 84\n");
  printf("b = 85\n");
  printf("c = 67\n");
  printf("d = 84\n");
  printf("e = 70\n");
  printf("f = 123\n");
  printf("y = 124\n");

int main() {
  char* flag = "abcdefghijklmnopqrstuvwxy";
  char mat[5][5];// Matrix for a jumbled string
  unsigned char pass[19] = "\x8b\xce\xb0\x89\x7b\xb0\xb0\xee\xbf\x92\x65\x9d\x9a\x99\x99\x94\xad\xe4\x00";  
  genMatrix(mat, flag);
  printEquations(mat, pass);

After we had the equation system we just used this solver:

And got these results: a=84 b=85 c=67 d=84 e=70 f=123 g=53 h=121 i=53 j=55 k=t1 l=109 m=53 n=94 o=48 p=101 q=95 r=52 s=95 t=100 u=48 v=119 w=111 x=33 y=124

which translates to TUCTF{5y573m5_0f_4_d0wn!}

Performance problems in the java layer, Catastrophic Backtracking

15/07/17 — capitol


We discovered that we had a couple of requests that took extreme amounts of time and cpu power on our servers.

After dumping the thread stacks, we found out that the culprit was this regex:


When encountering strings that contains a large number of spaces, for example:

"SUNDEVEIEN                               A"

it uses very large amounts of cpu time to try to match.

After some testing, we determined that the execution time scaled in the order of O(2^n). As seen in this table.

spaces milliseconds
27 13915
28 26509
29 50512
30 106652
31 207174
32 409100
33 824363

The problem class is called catastrophic backtracking, and many regex engines are of the backtracking kind, also the standard one in java.

The problematic regex can be simplified to this in order to better highlight the problem:


What happens here is that the regex engine have multiple paths for matching the spaces it encounters, it can either add it to a the last space in the previous group or it can start a new group with it. When it encounters the ending A in the string it’s obvious for the human eye that the regex will never match, but the engine doesn’t know that without trying all combinations of groups of spaces, so it will backtrack in the string and try another way, which doesn’t work either, until all combinations are exhausted.

There is a number of ways to fix this problem, the most radical might be to replace the regex engine with one that guarantees linear execution, for example RE2J.

Or you could avoid nesting quantifiers (the pluses and stars), but this requires that you can restructure the problem.

Another way would be to rewrite the regex so that there isn’t a choice between different ways to match the string, for example by changing the first * into a +.