Here we are given a file chall
which is a Linux executable; so we can throw it into Ghidra and checkout what it's doing.
Here in the main
function, we see that the program expects a user input of length 63 and calculates some number to set as the random seed. Next we have this more interesting section where we are validating against a data field, target
. The critical piece of code here is: ((int)cVar1 ^ iVar2 % 0x100) != *(uint *)(target + (long)local_6c * 4)
.
Here, we see cVar1
is a character of our user input and iVar2
is a random integer, denoted by rand()
. We know that modulus (%
) and exclusive or (^
) are commutative, so we can use the given data in target
to determine the random numbers, but first we need to extract out our data from Ghidra. I chose the simple approach of copy-pasting into my text editor and making it an integer array in my C program:
From this: (notice the length!)
to this:
int target[] = {0x33,0x84,0x3D,0x3F,0x2A,0x93,0x7B,0x82,0x1A,0xAC,0x8E,0xF4,0xB1,0xCB,0x8D,0x21,0xE,0xB7,0x67,0x96,0x2C,0x81,0xD3,0xBC,0x29,0x6C,0x4B,0xD,0x0,0xED,0xFD,0xEE,0x56,0x40,0x52,0xD5,0x5,0x6D,0x90,0x3E,0x7A,0x1B,0x69,0x23,0x1F,0xB6,0x1D,0xBC,0x98,0xD1,0xA6,0x83,0xE9,0xEB,0x13,0x21,0x3D,0xF8,0x2B,0x79,0x53,0x4F,0xA1};
Then from there I wrote a C program to brute-force the seed, but it turned out to be fruitless effort because the seed was 0! I should've checked for this first, but I didn't want to assume that this was the case. So here's the program in its entirety anyways:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
void print_target_numbers(char *str, int *arr, int size) {
printf("Outputting numbers for '%s'::", str);
printf("[");
for (int i = 0; i < size; i++) {
if (i == size - 1)
printf("%d", arr[i]);
else
printf("%d,", arr[i]);
}
puts("]\n");
}
int main() {
int target_seed;
int output_numbers[63];
int target_first_numbers[5];
int target[] = {0x33,0x84,0x3D,0x3F,0x2A,0x93,0x7B,0x82,0x1A,0xAC,0x8E,0xF4,0xB1,0xCB,0x8D,0x21,0xE,0xB7,0x67,0x96,0x2C,0x81,0xD3,0xBC,0x29,0x6C,0x4B,0xD,0x0,0xED,0xFD,0xEE,0x56,0x40,0x52,0xD5,0x5,0x6D,0x90,0x3E,0x7A,0x1B,0x69,0x23,0x1F,0xB6,0x1D,0xBC,0x98,0xD1,0xA6,0x83,0xE9,0xEB,0x13,0x21,0x3D,0xF8,0x2B,0x79,0x53,0x4F,0xA1};
char user_input[] = "TBTL{"; // }
for (int i = 0; i < 5; i++) {
target_first_numbers[i] = target[i] % 0x100 ^ (int) user_input[i];
}
print_target_numbers(user_input, target_first_numbers, 5);
for (int i = 0; i < 2147483647; i ++) {
srand(i);
int is_good = true;
for (int x = 0; x < 5; x++) {
int rn = rand() % 0x100;
if (rn != target_first_numbers[x]) {
is_good = false;
break;
}
}
if (is_good) {
printf("Seed: %d\n\n", i);
target_seed = i;
break;
}
}
srand(target_seed);
for (int i = 0; i < 63; i++) {
output_numbers[i] = rand() % 0x100;
}
print_target_numbers("Output Numbers", output_numbers, 63);
return 0;
From there, I moved on to Python and just copy-pasted the output from the last print_target_numbers
. And now that we have the random numbers, it's as simple as doing the math provided in the Ghidra output with our random numbers, 0x100
, and target
data fields.
Here's the quick script I wrote to solve that:
target = [0x33,0x84,0x3D,0x3F,0x2A,0x93,0x7B,0x82,0x1A,0xAC,0x8E,0xF4,0xB1,0xCB,0x8D,0x21,0xE,0xB7,0x67,0x96,0x2C,0x81,0xD3,0xBC,0x29,0x6C,0x4B,0xD,0x0,0xED,0xFD,0xEE,0x56,0x40,0x52,0xD5,0x5,0x6D,0x90,0x3E,0x7A,0x1B,0x69,0x23,0x1F,0xB6,0x1D,0xBC,0x98,0xD1,0xA6,0x83,0xE9,0xEB,0x13,0x21,0x3D,0xF8,0x2B,0x79,0x53,0x4F,0xA1]
random_numbers = [103,198,105,115,81,255,74,236,41,205,186,171,242,251,227,70,124,194,84,248,27,232,231,141,118,90,46,99,51,159,201,154,102,50,13,183,49,88,163,90,37,93,5,23,88,233,94,212,171,178,205,198,155,180,84,17,14,130,116,65,33,61,220]
output_string = ""
for i in range(len(random_numbers)):
rn = random_numbers[i]
tgt = target[i]
output_string += chr(rn ^ tgt % 0x100)
print(output_string) # FLAG: TBTL{REDACTED}
And there you, the flag is printed in plain text!
nice! a combination of C and python to get what you need.