# RageQuit - BalCCon2k20 CTF (rev, 497p, 1 solved)## Introduction
RageQuit is a reversing task.
An archive containing a Linux ELF file, its output and an encrypted file isprovided.
The output contains references to `xchacha20-poly1305` :> send the payment reference below to poly1305@cnc-admin.com
> able to afford some x-Cha-Cha-Cha dancing lessons
## Reverse engineering### InitializationThe main function first does a weird dance to call a function while ensuringthere is only one argument :```cfptr[argc](0, buffer);```
If argc is not 1, the call will crash.
The function does pretty much nothing if the first argument is 0 : it onlyprints a obfuscated message.
Then, the program prepares a regular expression : `^.+\.flag$`. It is used toensure only files ending with `.flag` are encrypted.
The program calls a function that calls 10 other functions... fortunately thefirst one uses assertions and gives away its name : `sodium_crit_enter`.
By looking for cross-references to `sodium_crit_enter` in the source code ofLibsodium, it becomes clear that this first function is in fact `sodium_init`.
The next function cannot be easily identified (it is`crypto_aead_xchacha20poly1305_ietf_keygen`). But the one after is a POSIXfunction : `ftw` (file tree walk). It receives a callback that is called forevery file in the current directory recursively.
The callback ensures the filename matches the regex and encrypt the file if itdoes.
### Encryption routine
The encryption can be roughly decompiled to :```cFILE *fp_in  = fopen(filename, "rb+");FILE *fp_out = fopen(outname, "wb");FILE *fp_rng = fopen("/dev/urandom", "r");char buffer[0x1000]
unlink(filename);
while(1) {	size = fread(buffer, 1, sizeof(buffer), fp_in);	if(feof(fp_in))		break;
	/* encrypt and write the output */	encrypt(buffer);	fwrite(buffer, size, 1, fp_out);
	/* rewind and overwrite with garbage */	fseek(fp_in, -size, SEEK_CUR);	fread(buffer, size, 1, fp_rng);	fwrite(buffer, size, 1, fp_in);
}
/* encrypt and write the output */encrypt(buffer);fwrite(buffer, size, 1, fp_out);
/* rewind and overwrite with garbage */fseek(fp_in, -size, SEEK_CUR);fread(buffer, size, 1, fp_rng);fwrite(buffer, size, 1, fp_in);```
By using a mix of Libsodium source code, documentation and assumption, it ispossible to identify the exact name of the encryption function.
The ransomware encrypts files using`crypto_secretstream_xchacha20poly1305_push`, just like in the[Encrypted streams and file encryption](https://doc.libsodium.org/secret-key_cryptography/secretstream)chapter.
It is therefore safe to assume that the key is generated in the `main` functionby the `crypto_aead_xchacha20poly1305_ietf_keygen` function and is stored at`0x000387c0`.
The nonce (header) is generated randomly and is stored in the first 0x18 bytesof the encrypted file :```ccrypto_secretstream_xchacha20poly1305_init_push(&state, header, key);fwrite(header, 0x18, 1, fp_oput);```
### Reference generation
Once every files matching the regular expression have been encrypted, theprogram calls once again the weird `fptr` function with different arguments :`fptr[argc](1, key = buffer)`.
When the first argument is 1, the function does something entirely different.
It first starts by copying the key in a local buffer, and shift every byte leftaccording to a lookup-table :```cint keyLocal[sizeof(key)];int shifts[8] = {...};
for(i = 0; i < sizeof(key); i++) {	keyLocal[i] = key[i];	keyLocal[i] = keyLocal[i] << (shifts[j % 8] & 0x1f);}```
It then calls 3 functions in this order, and with these arguments :```cf_2640(keyLocal, 4);f_3d60(keyLocal, 1);
f_2640(keyLocal, 2);f_3d60(keyLocal, 6);
f_2640(keyLocal, 5);f_3d60(keyLocal, 4);
f_4450(keyLocal);```
`f_2640` calls differents functions through trampolines. All of these functionstake two arguments : an `int*` (always `keyLocal` and an index. This indexgoes from `0x00` to `0x10`.
All these functions add the `arg2`th number of a look-up table to `arg1[idx]`.`idx` increases by one for each function.
The `f_2640` function effectively adds a look-up table to the key.
`f_3d60` is much more straightforward because it does not use trampolines andnew functions :```clocalKey[0] = localKey[0] * LUT_mul[start + 0 & 0xf];localKey[1] = localKey[1] * LUT_mul[start + 1 & 0xf];localKey[2] = localKey[2] * LUT_mul[start + 2 & 0xf];localKey[3] = localKey[3] * LUT_mul[start + 3 & 0xf];// ...```
This function does the same action but multiplies instead of adding. The look-uptable is different.
`f_4450` is also straightforward : it xors `localKey[0x01..0x1F]` with`localKey[0x00..0x1E]`
The full algorithm has been reimplemented in the `check.php` script present inthe appendices of this writeup.
### Undoing the transformation
The last xor operation can be reverted.
Unfortunately the multiplication operation cannot be reverted because 8 is apossible factor because there is no multiplicative inverse of 8 mod 2^32.
The multiplication, addition and shift operations only work on a specific index.This means that once the xor operation has been reverted, it is possible tobruteforce each byte of the key (256 possibilities) independently.
The code to recover the key is in `pwn.php`
The encryption key is `AF 51 23 A0 B0 14 C3 CC CF D4 8B 47 6D E9 08 98 54 DB C88C 49 1E 54 44 35 C4 D5 3B FA 8E FD 3A`
Once the key is recovered, it is possible to decrypt the `ragequit.flag.rgq`file. This file contains the flag.
**Flag**: `BCTF{s0m3t1m2s_r4g3_1s_4ll_y0u_n33d}`
## Appendices### check.php```php 0; $i--)	$key[$i] ^= $key[$i - 1];
// bffor($i = 0; $i < 0x100; $i++) {	$check = [		$i, $i, $i, $i, $i, $i, $i, $i,		$i, $i, $i, $i, $i, $i, $i, $i,		$i, $i, $i, $i, $i, $i, $i, $i,		$i, $i, $i, $i, $i, $i, $i, $i,	];
	$check = shl($check);	$check = add($check, 4);	$check = mul($check, 1);	$check = add($check, 2);	$check = mul($check, 6);	$check = add($check, 5);	$check = mul($check, 4);
	for($j = 0; $j < sizeof($check); $j++)		if($key[$j] === $check[$j])			$result[$j] = $i;}
for($i = 0; $i < sizeof($result); $i++)	printf("%02X", $result[$i]);
printf("\n");```
### crypto.php```