HTB Sekure Decrypt
Difficulty : Easy
Category : Reversing
The Challenge contains 3 files :
.
├── core
├── dec
└── src.c
A binary along with its source code and a final file named core, which is a core dump of the program.
Source code :
Looking at the main, we observe that the program requires a key fetched from an environment variable called KEY, aswell as a file called flag.enc.
int main(int argc, char* argv[]) // gcc src.c -o dec -lmcrypt -ggdb
{
char* IV = "AAAAAAAAAAAAAAAA";
char *key = getenv("KEY");
int keysize = 16;
char* buffer;
int buffer_len = 16;
void *ciphertext = read_file("flag.enc", buffer_len);
decrypt(ciphertext, buffer_len, IV, key, keysize);
printf("Decrypted contents: %s\n", ciphertext);
return 0;
}
If I try to simply run the program a custom KEY environment variable and a custom file, the program crashes.
~ $ echo -ne "0123456789abcdef" > flag.enc
~ $ KEY='BBBBBBBBBBBBBBBB' ./dec
[1] 27946 segmentation fault (core dumped) KEY='BBBBBBBBBBBBBBBB' ./dec
Core dump
I never really manipulated core files before, but both gdb and pwntools provide great APIs to work with them.
Retrieving the KEY
First thing would be to retrieve the KEY environment variable :
from pwn import *
if __name__ == "__main__":
core = Coredump('./core')
print("KEY=", core.getenv('KEY'))
# > KEY=VXISlqY>Ve6D<{#F
Understanding the crash
Let's now analyze where the program crashes.

Looking through the backtrace, the function fclose() calls __libc_fatal(), which is a libc routine to handle fatal exceptions. So we can now affirm that the crash origin is fclose().
void* read_file(char* filename, int len) {
FILE *fp = fopen(filename, "rb");
void* data = malloc(len);
fread(data, 1, len, fp);
fclose(&fp);
return data;
}
I'm not really sure why it crashed, but this isn't the most interesting happening in the function read_file(). We can see that the program first opens the file containing the flag and then read its 16 first bytes to finally write them into a memory region that has been malloc().
The most interesting part, is that if the core file is about the same bug, we can retrieve the encrypted flag from the memory of the core dump.
Finding the encrypted flag in memory
Looking at the mappings of the core dump using pwntools, I can't really tell where is the heap, even tho it's probably a rw memory region.

Since I don't remember where the heap is normally located, I will strace the program and look for the malloc syscalls :

Just before the crash and the sys_read, there are two brk() calls. brk() is used to change the size of the data segment (and, if I remember correctly, malloc() may later use that space). Those two calls increase the memory by 0x21000 bytes, so I should look for a region of that size.
In the following screenshots, the first two segments after the program are read-write and one is 0x21000 bytes long, followed by a 0x3000-byte segment. Could this be the heap ? - Let's dump it !
from re import VERBOSE
from pwn import *
from pwnlib.args import LOG_LEVEL
if __name__ == "__main__":
LOG_LEVEL(VERBOSE)
core = Coredump('./core')
m = core.mappings[5]
for i in range(0, m.size, 16):
data = core.read(m.address+i, 16)
if data != b'\x00' * 16 and data[-1] != 0:
print(data)
# > b'2&\x08\xdb\xef\x90\x0b\x1e\xbc\xd3\xa0Xq\x91H\x83'
We might have our encrypted flag.
Decrypting the flag
Looking at the original source code file, the decryption algorithm uses, rijndael-128 in cbc mode with an IV of AAAAAAAAAAAAAAAA.
int decrypt(void* buffer, int buffer_len, char* IV, char* key, int key_len) {
MCRYPT td = mcrypt_module_open("rijndael-128", NULL, "cbc", NULL);
int blocksize = mcrypt_enc_get_block_size(td);
if( buffer_len % blocksize != 0 ){
return 1;
}
mcrypt_generic_init(td, key, key_len, IV);
mdecrypt_generic(td, buffer, buffer_len);
mcrypt_generic_deinit (td);
mcrypt_module_close(td);
return 0;
}
from re import VERBOSE
from pwn import *
from pwnlib.args import LOG_LEVEL
from Crypto.Cipher import AES
IV = b"AAAAAAAAAAAAAAAA"
def decrypt_flag(flag, key, IV):
cipher = AES.new(key, AES.MODE_CBC, IV)
padded_data = cipher.decrypt(flag)
print(padded_data)
if __name__ == "__main__":
LOG_LEVEL(VERBOSE)
core = Coredump('./core')
m = core.mappings[5]
for i in range(0, m.size, 16):
data = core.read(m.address+i, 16)
if data != b'\x00' * 16 and data[-1] != 0:
decrypt_flag(data, core.getenv("KEY"), IV)
# > b'HTB{t1m_l3arn_C}'
And, here is our flag :)