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.