Our sources told us that one of PALINDROME's lieutenants, XIPHEREHPIX, wrote a special computer program for certain members of PALINDROME. We have somehow managed to get a copy of the source code and the compiled binary. The intention of the program is unclear, but we think encrypted blob inside the program could contain a valuable secret.
The flag is expected to be displayed in the show_welcome_msg function, where AES GCM decryption is executed with some hardcoded arguments.
We can view the execution flow of the code in the main() function.
We can see that the program asks for an over 40 byte long password, which it passes into the initialise_key function.
Upon inspection of this function, we see that the password is not actually used in creating the key. This means that we can skip the password verification altogether.
The initialise_key function uses a hardcoded seed to generate the array of 20 SHA256 hashes. This means that the array of hashes used in the key generation is constant.
Moving on to the for loop, we can see that the password is only used for determining which of the arrays are XOR-ed to generate the key.
This means that the final key is generated solely from the same 20 SHA256 hashes, no matter what password is entered. Therefore we could bruteforce all combinations of all lengths of the SHA256 hashes until we find the flag.
int main(int argc, char **argv)
{
char password[MAX_PASSWORD_SIZE + 1] = { 0 };
int password_length;
unsigned char key[32];
printf("Hello PALINDROME member, please enter password sir:");
password_length = input_password(password);
if (password_length < 40) {
printf("The password should be at least 40 characters as per PALINDROME's security policy.\n");
exit(0);
}
if (!verify_password(password, password_length)) {
initialise_key(key, password, password_length);
show_welcome_msg(key);
}
else {
printf("Failure! \n");
exit(0);
}
}
void initialise_key(unsigned char *key, char *password, int password_length) {
const char *seed = "PALINDROME IS THE BEST!";
int i, j;
int counter = 0;
uint256_t *key256 = (uint256_t *)key;
key256->a0 = 0;
key256->a1 = 0;
key256->a2 = 0;
key256->a3 = 0;
uint256_t arr[20] = { 0 };
calculate_sha256((unsigned char *) arr, (unsigned char *) seed, strlen(seed));
for (i = 1; i < 20; i++) {
calculate_sha256((unsigned char *)(arr+i), (unsigned char *) (arr+i-1), 32);
}
for (i = 0; i < password_length; i++) {
int ch = password[i];
for (j = 0; j < 8; j++) {
counter = counter % 20;
if (ch & 0x1) {
accumulate_xor(key256, arr+counter);
}
ch = ch >> 1;
counter++;
}
}
}
import hashlib
from Crypto.Cipher import AES
import binascii
import itertools
from Crypto.Util.number import long_to_bytes
def gcm_decrypt(ciphertext, key, iv):
cipher = AES.new(key, AES.MODE_GCM, iv)
plaintext = cipher.decrypt(ciphertext)
return plaintext
ciphertext = b"\xad\xac\x81\x20\xc6\xd5\xb1\xb8\x3a\x2a\xa8\x54\xe6\x5f\x9a\xad\xa4\x39\x05\xd9\x21\xae\xab\x50\x98\xbd\xe4\xc8\xe8\x2a\x3c\x63\x82\xe3\x8e\x5d\x79\xf0\xc6\xf4\xf2\xe7"
iv = b"PALINDROME ROCKS"
header = b"welcome_message"
tag = b"\xbd\xfc\xc0\xdb\xd9\x09\xed\x66\x37\x34\x75\x11\x75\xa2\x7a\xaf"
seed = "PALINDROME IS THE BEST!"
# generate the constant array
arr = [None for _ in range(20)]
arr[0] = hashlib.sha256(seed.encode()).digest()
for i in range(1, 20):
arr[i] = hashlib.sha256(arr[i-1]).digest()
# convert to int for XOR
arr = [int.from_bytes(x, "big") for x in arr]
# bruteforce all combinations
def bruteforce():
for size in range(1, len(arr) + 1):
for comb in itertools.combinations(arr, size):
key = 0
for i in comb:
key ^= i
key = long_to_bytes(key)
key = key.ljust(32, b"\x00")
plaintext = gcm_decrypt(ciphertext, key, iv)
if b"TISC" in plaintext:
print(f"{plaintext=}")
print(f"{key=}")
return