diff --git a/config.yaml b/config.yaml index 7c20543..f313fd9 100644 --- a/config.yaml +++ b/config.yaml @@ -38,7 +38,7 @@ params: picture: https://avatars.githubusercontent.com/u/48451382?s=400&u=b2a10a33c3ec8062c8b99e9191ee85af061272ef&v=4 tags: [ 'web','misc','forensics','mobile' ] description: - - Backend Bandit & De-bug Demon + - Backend Bandit & De-bug Demon - Do not do it... - d293ISB5b3UgcmVhbGx5IGRlY29kZWQgdGhpcyA= - name: H0N3YP0T @@ -59,7 +59,7 @@ params: link: https://github.com/DalmatianuSebikk picture: https://avatars.githubusercontent.com/u/70603934?v=4 tags: ['web','forensics','crypto','misc'] - description: + description: - keeps injecting malicious code - games and sports enjoyer. - name: expnx @@ -87,6 +87,12 @@ params: tags: [ 'web', 'forensics', 'misc' ] description: - Exploring the depths of web security + - name: GabrielMajeri + link: https://github.com/GabrielMajeri + picture: https://avatars.githubusercontent.com/u/3010346?v=4 + tags: [ 'crypto', 'network', 'web', 'rev', 'misc' ] + description: + - I like breaking stuff excludedSections: - about - events @@ -107,3 +113,9 @@ menu: - name: About url: /about weight: 4 + +markup: + goldmark: + renderer: + # To allow including raw HTML tags in our Markdown input + unsafe: true diff --git a/content/HTB_University_2024/MuTLock.md b/content/HTB_University_2024/MuTLock.md new file mode 100644 index 0000000..d0029be --- /dev/null +++ b/content/HTB_University_2024/MuTLock.md @@ -0,0 +1,148 @@ +--- +title: MuTLock +date: 2024-12-22T12:53:45+02:00 +description: Writeup for MuTLock [HTB University 2024] +author: GabrielMajeri +tags: + - crypto +draft: false +--- + +--- + +## Challenge Description + +> The Frontier Board encrypts their secrets using a system tied to the ever-shifting cosmic cycles, woven with patterns that seem random to the untrained eye. To outwit their defenses, you'll need to decipher the hidden rhythm of time and unlock the truth buried in their encoded transmissions. Can you crack the code and unveil their schemes? + +We are given the Python source code for a custom cipher used to encode the secret flag, as well as an `output.txt` file with the encrypted output. + +## Initial Analysis + +Looking at the source code, we can easily tell that this is a pretty weak and broken cipher. The way it works is as follows: +1. Split the flag into two halves. +2. For each half: + 1. Generate two integers, a _key seed_ and an _XOR key_ (which will be used in the final encryption step). They are generated based on the parity of the current timestamp (in seconds). + 2. Expand the key seed into a string of random letters, of length 16 (using Python's `random.seed` and `random.choice`). + 3. Perform a polyalphabetic substitution on the plaintext, using the generated key. The result is then base64-encoded. + 4. XOR the resulting bytes with the XOR key. + 5. Store the resulting ciphertext in an array, **wait for one second** and then encrypt the other half of the flag. +3. Write the two encoded halves of the flag into a text file, as hex. + +This cipher is symmetric and reversible, _if_ we were to know the values of the key seed and the XOR key. The decryption works by applying the steps described above backwardly (i.e. read the encrypted data, XOR it with the key, perform the [polyalphabetic substitution](https://en.wikipedia.org/wiki/Polyalphabetic_cipher) in reverse). Since we don't have any way of determining what the values of the keys are (since we don't have any information on **when** the encryption code was run), we'll have to guess them. Fortunately, the [key space](https://en.wikipedia.org/wiki/Key_size) isn't very large: +* For even timestamps, the key seed is a random integer in the range $[1, 1000]$ and the XOR key is the constant $42$. +* For odd timestamps, the key seed is the constant $42$ and the XOR key is a random integer in the range $[1, 255]$. + +## Solution + +We are going to perform what is basically a [known-plaintext attack](https://en.wikipedia.org/wiki/Known-plaintext_attack), since we are certain that the decrypted flag will start with the string `HTB{`. + +The code used to perform the actual brute force attack is available below: + +```python +import random +import string +import base64 + + +# I've hardcoded the values of the ciphertext here, +# instead of reading it from the input file +flag_first_half = bytes.fromhex('00071134013a3c1c00423f330704382d00420d331d04383d00420134044f383300062f34063a383e0006443310043839004315340314382f004240331c043815004358331b4f3830') +flag_second_half = bytes.fromhex('5d1f486e4d49611a5d1e7e6e4067611f5d5b196e5b5961405d1f7a695b12614e5d58506e4212654b5d5b196e4067611d5d5b726e4649657c5d5872695f12654d5d5b4c6e4749611b') + +# We'll mount a known-plaintext brute force attack against this weak cipher +flag_start = 'HTB{' + + +## This function is taken from the original source code +def generate_key(seed, length=16): + random.seed(seed) + key = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(length)) + return key + +## This function is the `polyalphabetic_encrypt` function, but written in reverse +def polyalphabetic_decrypt(ciphertext, key): + key_length = len(key) + plaintext = [] + for i, char in enumerate(ciphertext): + key_char = key[i % key_length] + decrypted_char = chr((ord(char) - ord(key_char)) % 256) + plaintext.append(decrypted_char) + plaintext = ''.join(plaintext) + return plaintext + +## This is mostly the same as the original, +## but we're given the ciphertext as input (not the plain text) +def xor_cipher(ciphertext: bytes, key: int): + return ''.join(chr(c ^ key) for c in ciphertext) + +## This function performs the encryption steps in reverse +def perform_decryption(encrypted_bytes: bytes, key_seed: int, xor_key: int): + # Reverse the XOR cipher + decrypted = xor_cipher(encrypted_bytes, xor_key) + + # Reverse the base64-encoding + try: + decrypted_half = base64.b64decode(decrypted).decode() + except: + return '' + + # Generate a key based on the seed + key = generate_key(key_seed) + + # Perform the decryption + return polyalphabetic_decrypt(decrypted_half, key) + +## Brute force the cipher knowing that the decrypted message +## should start with a certain sequence of characters +def try_keys(encrypted_bytes: bytes, known_plaintext: str, key_seed: int, xor_key: int): + decrypted = perform_decryption(encrypted_bytes, key_seed, xor_key) + + if decrypted.startswith(known_plaintext): + print(decrypted) + return True + else: + return False + + +## Decrypt the first half of the flag +print("Attempting to crack first half of the flag...") +for i in range(1, 255): + key_seed = 42 + xor_key = i + if try_keys(flag_first_half, flag_start, key_seed, xor_key): + print("Found correct seed and XOR key!") + print("key_seed =", key_seed) + print("xor_key =", xor_key) + +print("Attempting to crack second half of the flag...") +for i in range(1, 1000): + key_seed = i + xor_key = 42 + if try_keys(flag_second_half, 'ion', key_seed, xor_key): + print("Found correct seed and XOR key!") + print("key_seed =", key_seed) + print("xor_key =", xor_key) +``` + +Output: + +```text +Attempting to crack first half of the flag... +HTB{timestamp_based_encrypt +Found correct seed and XOR key! +key_seed = 42 +xor_key = 119 +Attempting to crack second half of the flag... +ion_is_so_secure_i_promise} +Found correct seed and XOR key! +key_seed = 433 +xor_key = 42 +``` + +**Note**: When I first wrote the code, I didn't know whether the first half of the flag was encrypted using $42$ as the key seed and a random XOR key in the range $[1, 255]$, or if the key seed was a random integer in the range $[1, 1000]$ and the XOR key was 42. I had to switch around the last two `for` ranges in the code above before the encryption worked. + +Also, note that after I managed to decrypt the first half of the flag starting with the known plaintext `HTB{`, I've noticed that it ended with the word `encrypt`, so I guessed the second half must start with the known plaintext `ion`. + +### Flag + +`HTB{timestamp_based_encryption_is_so_secure_i_promise}` diff --git a/content/HTB_University_2024/_index.md b/content/HTB_University_2024/_index.md new file mode 100644 index 0000000..f573793 --- /dev/null +++ b/content/HTB_University_2024/_index.md @@ -0,0 +1,7 @@ +--- +title: HTB University 2024 +date: 2024-12-18T12:50:00+02:00 +description: Writeups for [HTB University 2024] +place: 256 +total: 1128 +--- diff --git a/content/ekoparty_2023/_index.md b/content/ekoparty_2023/_index.md index 30c1061..04072d9 100644 --- a/content/ekoparty_2023/_index.md +++ b/content/ekoparty_2023/_index.md @@ -1,6 +1,6 @@ --- title: Ekoparty 2023 -date: 2023-11-1T18:42:50+02:00 +date: 2023-11-01T18:42:50+02:00 description: Writeups for [Ekoparty 2023] place: 7 total: 550 diff --git a/content/umdctf_2024/tcache.md b/content/umdctf_2024/tcache.md index 26079e3..1ba5269 100644 --- a/content/umdctf_2024/tcache.md +++ b/content/umdctf_2024/tcache.md @@ -1,7 +1,7 @@ --- -title: Tcache +title: Tcache date: 2024-04-17T14:23:28+03:00 -description: Information about tcache +description: Information about tcache author: PineBel tags: - pwn @@ -46,7 +46,7 @@ typedef struct tcache_entry typedef struct tcache_perthread_struct { uint16_t counts[TCACHE_MAX_BINS]; // --> count of free chunks for each size (max 7 of the same size) [define TCACHE_MAX_BINS 64] - tcache_entry *entries[TCACHE_MAX_BINS]; // --> points to the entry for each size + tcache_entry *entries[TCACHE_MAX_BINS]; // --> points to the entry for each size } tcache_perthread_struct; ``` @@ -76,8 +76,8 @@ Let's see how **__libc_malloc** works with tcache: 3. Upon completion of step 2, it returns a tcache_perthread_struct. 4. If the tcache entries are not empty, it will invoke tcache_get(), which retrieves the first chunk from the entries list and decrements the count. (`GETTING CHUNK`) ```C - - tcache_get (size_t tc_idx) + + tcache_get (size_t tc_idx) { tcache_entry *e = tcache->entries[tc_idx]; @@ -91,7 +91,7 @@ Let's see how **__libc_malloc** works with tcache: --(tcache->counts[tc_idx]); // Get a chunk, counts decreases return (void *) e; } -``` +``` 5. When calling free(), it will call tcache_put() which adds the freed chunk. (`SETTING CHUNK`) ```C /* Caller must ensure that we know tc_idx is valid and there's room @@ -146,7 +146,7 @@ For other versions, the concept remains the same but may require adjustments. Fo I suggest experimenting a bit with the [how2heap](https://github.com/shellphish/how2heap) repository, it's very cool! ## Safe linking -Leaking from the heap isn't that easy anymore, since glibc > 2.32 there is something called 'safe linking' (fastbins and tcachebins) which make heap exploits a little harder. +Leaking from the heap isn't that easy anymore, since glibc > 2.32 there is something called 'safe linking' (fastbins and tcachebins) which make heap exploits a little harder. Safe-linking's goal is to prevent attackers from leaking heap addresses and arbitrarily overwriting linked-list metadata. Safe linking implementation: