2008-12-17

WebLogic and Non-English Character Sets

I want to discuss more in depth about the vulnerability Matt Presson and I have been working on. Apparently if you have a Weblogic server  and you accept international characters but you have the page encoding set to ISO 8859-1 then you are vulnerable to a whole mess of xss attacks and any script listed in my previous blog post will execute. What seems to happen, we are still trying to find the exact cause, is that the web server will truncate the upper bytes when returning the request. so if you entered 0x013C the response will contain 0x3C which is of course '<' and freakin vulnerable ;) This will get past the default output encoding that is done in <bean:write which uses the function ResponseUtils.filter() to actually do all of its output encoding. If your use StringEscapeUtils.escapeHtml() from apache commons lang then all your output will be encoded correctly and this is demonstrated by the war file Matt Presson released.

I also modified Matt's code so that the regular POST is also vulnerable. The ajax post was vulnerable because it called encodeURIComponent from javascript. If you set the form to accept-charset="UTF-8" then the data will be encoding the same as with encodeURIComponent.

   <form name="dataForm" id="form" method="POST" action="/International/execute/Display" accept-charset="UTF-8">


Below is a video of the the attack being exploited both in ajax and in a regular post.



I have currently tested this on tomcat and glassfish and was unable to get the same results so it seems to primarily be a weblogic issue.


I would like to thank Gareth Heyes for giving me a hacker tag in hackvertor to perform this expoit.!!!!

2008-10-23

Why not to use Blacklists.

I was looking at Matt Presson's Blog article about executing scripts with foreign char sets and decided to write my own JSP to generate every XSS that could be executed with foreign characters. This is a perfect example of why not to use blacklist. A simple whitelist or better proper output encoding (mentioned in my last post) will thwart these attempts.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@page import="org.apache.commons.lang.StringEscapeUtils" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "
http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%
for(long i=0; i< 0x100; i++)
{
    long lt = 0x3C;
    long gt = 0x3E;
    long order = i << 8;
    long LT = order | lt;
    //out.println(Long.toHexString(LT) + " : ");
    long GT = order | gt;
    //out.println(Long.toHexString(GT) + "<BR>");
    String theScript = (char)LT + "script" + (char)GT + "alert(" + i + ");" +(char)LT + "/script" + (char)GT;
    out.println( theScript + "<br>");
}

%>
</body>
</html>

 

This will generate 256 different versions of javascript to bypass blacklist input validation attempts. Here are a few examples. They may not display properly on this blog so i recommend running it on your own tomcat server.

ļscriptľalert(1);ļ/scriptľ
ȼscriptȾalert(2);ȼ/scriptȾ
̼script̾alert(3);̼/script̾
мscriptоalert(4);м/scriptо
ԼscriptԾalert(5);Լ/scriptԾ
ؼscriptؾalert(6);ؼ/scriptؾ
ܼscriptܾalert(7);ܼ/scriptܾ
࠼script࠾alert(8);࠼/script࠾
़scriptाalert(9);़/scriptा
਼scriptਾalert(10);਼/scriptਾ
଼scriptାalert(11);଼/scriptା
఼scriptాalert(12);఼/scriptా
഼scriptാalert(13);഼/scriptാ
฼script฾alert(14);฼/script฾
༼script༾alert(15);༼/script༾
ြscriptှalert(16);ြ/scriptှ
ᄼscriptᄾalert(17);ᄼ/scriptᄾ
ሼscriptሾalert(18);ሼ/scriptሾ
ጼscriptጾalert(19);ጼ/scriptጾ
ᐼscriptᐾalert(20);ᐼ/scriptᐾ
ᔼscriptᔾalert(21);ᔼ/scriptᔾ
ᘼscriptᘾalert(22);ᘼ/scriptᘾ
᜼script᜾alert(23);᜼/script᜾
ᠼscriptᠾalert(24);ᠼ/scriptᠾ
᤼script᤾alert(25);᤼/script᤾
ᨼscriptᨾalert(26);ᨼ/scriptᨾ
ᬼscriptᬾalert(27);ᬼ/scriptᬾ
᰼script᰾alert(28);᰼/script᰾
ᴼscriptᴾalert(29);ᴼ/scriptᴾ
ḼscriptḾalert(30);Ḽ/scriptḾ
ἼscriptἾalert(31);Ἴ/scriptἾ
‼script‾alert(32);‼/script‾
ℼscriptℾalert(33);ℼ/scriptℾ
∼script∾alert(34);∼/script∾

2008-10-20

What do you mean no Java output encoding !!!

I hear all the time that java does not have any good encoding libraries but have used some frameworks that do seem to encode the output properly. If anyone uses the struts framework and implements it properly then all your output is encoded properly if you use the following methods to output data.

 

<bean:write name="secData" property="username"/><br>
    <html:link href="./test.jsp" paramId="test" paramName="secData" paramProperty="username" > <bean:write name="secData" property="username"/></html:link><br>
    <html:hidden name="secData" property="username" /> <br>

The tag libraries bead:write, html:hidden, and html:link will all output either url encoded output for html:link (which creates an anchor tag) and html:hidden or will entity encode your output for bean:write.

 

What if your not using struts you ask?

Well this is great if you are using struts 1 and struts 2 (output tags are different for struts 2) but what you are not or you are writing servlets that generate html dynamically. This is not a problem. I dug a little deeper into struts to see what struts was doing and if there is a way to leverage this for other sites that do not use this framework. The URL encoding is simply URLEncode.encode( your string, your format). Its uses a default java class that has been around since 1.5. Just import java.net.URLEncoder.

 

Example:

String output = URLEncoder.encode("your String" , "UTF-8");

 

Struts 1 is using an entity encoder built specifically into the struts framework. The entity encoding is using a struts class called ResponseUtils. So you could import this class and still not use the struts MVC architecture.

Example:

String output = org.apache.struts.util.ResponseUtils.filter("your string");

 

Struts 2 does entity encoding by using a class TextUtils from WebWork. Import org.opensymphony.xwork2.util.TextUtils

Example:

String output = org.opensymphony.xwork2.util.TextUtils.htmlEncode("your string", true);

 

Proper Encoding with international Chars!!!!

There are other encoding options provided by the apache commons lang library. This is library is probably the most useful. To get it to properly encode international chars to the screen you need to first unescape the html before to escape the html. import org.apache.commons.lang.StringEscapeUtils;

 

Example:

String Output = StringEscapeUtils.escapeHtml(StringEscapeUtils.unescapeHtml(input));

 

Below is an example screenshot of the all above encodings in a simple servlet. Notice that StringEscapeUtils (the last one) will properly encode the xss attempt plus the Chinese  character set. This will solve the problem all you java developers have with output encoding international characters and can quit complaining that there isn't any good encoding options for java. =) My source code is attached here: http://my-security-projects.googlecode.com/files/testEncoding.war 

 

I hope this finally answers everyone's concerns for international character encoding in java. I'll let you all out there in the hacker community see if you can find a way to get past this model. Let me know if you do

 

image

 

Here is the page source:




<html><body>
<h1> Servlet Test </h1>
<br><b>Input String: </b> <script>alert('xss');</script> &#29615;&#29699;&#32463;&#27982;&#19981;&#26223;
&#27668;&#20013;&#22269;&#32463;&#27982;&#22686;&#38271;&#25918;&#32531; &#20013;&#22269;&#32463;&#27982;
&#22312;&#29615;&#29699;&#19981;&#26223;&#27668;&#19979;&#21576;&#29616;&#25918;&#32531;&#24577;&#21183;
&#65292;&#26368;&#26032;&#22269;&#20869;&#29983;&#20135;&#24635;&#20540;&#22686;&#38271;&#20026;9.9%&#65292;
&#36890;&#32960;&#25345;&#32493;&#25918;&#32531;&#12290; &#20013;&#22269;&#20061;&#26376;&#36152;&#26131;
&#30408;&#20313;&#21019;&#32426;&#24405; &#35775;&#35848;&#65306;&#20013;&#22269;&#22806;&#36152;&#21069;
&#26223;&#19981;&#23481;&#20048;&#35266; &#37329;&#34701;&#39118;&#26292;&#65306;&#20013;&#22269;&#38754;
&#20020;&#30340;&#21361;&#26426;&#19982;&#26426;&#36935;
<br>
<b>java.net.URLEncoded: </b>%3Cscript%3Ealert%28%27xss%27%29%3B%3C%2Fscript%3E+++
%26%2329615%3B%26%2329699%3B%26%2332463%3B%26%2327982%3B%26%2319981%3B%26%2326223%3B
%26%2327668%3B%26%2320013%3B%26%2322269%3B%26%2332463%3B%26%2327982%3B%26%2322686%3B
%26%2338271%3B%26%2325918%3B%26%2332531%3B+%26%2320013%3B%26%2322269%3B%26%2332463
%3B%26%2327982%3B%26%2322312%3B%26%2329615%3B%26%2329699%3B%26%2319981%3B%26%2326223
%3B%26%2327668%3B%26%2319979%3B%26%2321576%3B%26%2329616%3B%26%2325918%3B%26%2332531
%3B%26%2324577%3B%26%2321183%3B%26%2365292%3B%26%2326368%3B%26%2326032%3B%26%2322269
%3B%26%2320869%3B%26%2329983%3B%26%2320135%3B%26%2324635%3B%26%2320540%3B%26%2322686
%3B%26%2338271%3B%26%2320026%3B9.9%25%26%2365292%3B%26%2336890%3B%26%2332960%3B%26
%2325345%3B%26%2332493%3B%26%2325918%3B%26%2332531%3B%26%2312290%3B+%26%2320013%3B
%26%2322269%3B%26%2320061%3B%26%2326376%3B%26%2336152%3B%26%2326131%3B%26%2330408
%3B%26%2320313%3B%26%2321019%3B%26%2332426%3B%26%2324405%3B+%26%2335775%3B%26
%2335848%3B%26%2365306%3B%26%2320013%3B%26%2322269%3B%26%2322806%3B%26%2336152%3B%26
%2321069%3B%26%2326223%3B%26%2319981%3B%26%2323481%3B%26%2320048%3B%26%2335266%3B+
%26%2337329%3B%26%2334701%3B%26%2339118%3B%26%2326292%3B%26%2365306%3B%26%2320013%3B
%26%2322269%3B%26%2338754%3B%26%2320020%3B%26%2330340%3B%26%2321361%3B%26%2326426%3B
%26%2319982%3B%26%2326426%3B%26%2336935%3B+
<br>
<b>(apache commons lang) org.apache.commons.lang.StringEscapeUtils: </b>&lt;script&gt;alert('xss');&lt;/script&gt;
&amp;#29615;&amp;#29699;&amp;#32463;&amp;#27982;&amp;#19981;&amp;#26223;&amp;#27668;&amp;#20013;&amp;#22269;&amp;#32463;
&amp;#27982;&amp;#22686;&amp;#38271;&amp;#25918;&amp;#32531; &amp;#20013;&amp;#22269;&amp;#32463;&amp;#27982;&amp;#22312;
&amp;#29615;&amp;#29699;&amp;#19981;&amp;#26223;&amp;#27668;&amp;#19979;&amp;#21576;&amp;#29616;&amp;#25918;&amp;#32531;
&amp;#24577;&amp;#21183;&amp;#65292;&amp;#26368;&amp;#26032;&amp;#22269;&amp;#20869;&amp;#29983;&amp;#20135;&amp;#24635;
&amp;#20540;&amp;#22686;&amp;#38271;&amp;#20026;9.9%&amp;#65292;&amp;#36890;&amp;#32960;&amp;#25345;&amp;#32493;&amp;#25918;
&amp;#32531;&amp;#12290; &amp;#20013;&amp;#22269;&amp;#20061;&amp;#26376;&amp;#36152;&amp;#26131;&amp;#30408;&amp;#20313;
&amp;#21019;&amp;#32426;&amp;#24405; &amp;#35775;&amp;#35848;&amp;#65306;&amp;#20013;&amp;#22269;&amp;#22806;&amp;#36152;
&amp;#21069;&amp;#26223;&amp;#19981;&amp;#23481;&amp;#20048;&amp;#35266; &amp;#37329;&amp;#34701;&amp;#39118;&amp;#26292;
&amp;#65306;&amp;#20013;&amp;#22269;&amp;#38754;&amp;#20020;&amp;#30340;&amp;#21361;&amp;#26426;&amp;#19982;&amp;#26426;
&amp;#36935;
<br>
<b>(struts-core-1.3.8.jar) org.apache.struts.util.ResponseUtils: </b>&lt;script&gt;alert(&#39;xss&#39;);&lt;/script&gt;
&amp;#29615;&amp;#29699;&amp;#32463;&amp;#27982;&amp;#19981;&amp;#26223;&amp;#27668;&amp;#20013;&amp;#22269;&amp;#32463;
&amp;#27982;&amp;#22686;&amp;#38271;&amp;#25918;&amp;#32531; &amp;#20013;&amp;#22269;&amp;#32463;&amp;#27982;&amp;#22312;
&amp;#29615;&amp;#29699;&amp;#19981;&amp;#26223;&amp;#27668;&amp;#19979;&amp;#21576;&amp;#29616;&amp;#25918;&amp;#32531;
&amp;#24577;&amp;#21183;&amp;#65292;&amp;#26368;&amp;#26032;&amp;#22269;&amp;#20869;&amp;#29983;&amp;#20135;&amp;#24635;
&amp;#20540;&amp;#22686;&amp;#38271;&amp;#20026;9.9%&amp;#65292;&amp;#36890;&amp;#32960;&amp;#25345;&amp;#32493;&amp;#25918;
&amp;#32531;&amp;#12290; &amp;#20013;&amp;#22269;&amp;#20061;&amp;#26376;&amp;#36152;&amp;#26131;&amp;#30408;&amp;#20313;
&amp;#21019;&amp;#32426;&amp;#24405; &amp;#35775;&amp;#35848;&amp;#65306;&amp;#20013;&amp;#22269;&amp;#22806;&amp;#36152;
&amp;#21069;&amp;#26223;&amp;#19981;&amp;#23481;&amp;#20048;&amp;#35266; &amp;#37329;&amp;#34701;&amp;#39118;&amp;#26292;
&amp;#65306;&amp;#20013;&amp;#22269;&amp;#38754;&amp;#20020;&amp;#30340;&amp;#21361;&amp;#26426;&amp;#19982;&amp;#26426;
&amp;#36935;
<br>
<b>(xwork) com.opensymphony.xwork2.util.TextUtils with spec chars: </b>&lt;script&gt;alert('xss');&lt;/script&gt;
&amp;#29615;&amp;#29699;&amp;#32463;&amp;#27982;&amp;#19981;&amp;#26223;&amp;#27668;&amp;#20013;&amp;#22269;&amp;
#32463;&amp;#27982;&amp;#22686;&amp;#38271;&amp;#25918;&amp;#32531; &amp;#20013;&amp;#22269;&amp;#32463;&amp;#27982;
&amp;#22312;&amp;#29615;&amp;#29699;&amp;#19981;&amp;#26223;&amp;#27668;&amp;#19979;&amp;#21576;&amp;#29616;&amp;#25918;
&amp;#32531;&amp;#24577;&amp;#21183;&amp;#65292;&amp;#26368;&amp;#26032;&amp;#22269;&amp;#20869;&amp;#29983;&amp;#20135;
&amp;#24635;&amp;#20540;&amp;#22686;&amp;#38271;&amp;#20026;9.9%&amp;#65292;&amp;#36890;&amp;#32960;&amp;#25345;&amp;
#32493;&amp;#25918;&amp;#32531;&amp;#12290; &amp;#20013;&amp;#22269;&amp;#20061;&amp;#26376;&amp;#36152;&amp;#26131;
&amp;#30408;&amp;#20313;&amp;#21019;&amp;#32426;&amp;#24405; &amp;#35775;&amp;#35848;&amp;#65306;&amp;#20013;&amp;
#22269;&amp;#22806;&amp;#36152;&amp;#21069;&amp;#26223;&amp;#19981;&amp;#23481;&amp;#20048;&amp;#35266; &amp;#37329;
&amp;#34701;&amp;#39118;&amp;#26292;&amp;#65306;&amp;#20013;&amp;#22269;&amp;#38754;&amp;#20020;&amp;#30340;&amp;#21361;
&amp;#26426;&amp;#19982;&amp;#26426;&amp;#36935;
<br>
<b>apache commons unescape then escape with StringEscapeUtils: </b>&lt;script&gt;alert('xss');&lt;/script&gt;
&#29615;&#29699;&#32463;&#27982;&#19981;&#26223;&#27668;&#20013;&#22269;&#32463;&#27982;&#22686;&#38271;&#25918;
&#32531; &#20013;&#22269;&#32463;&#27982;&#22312;&#29615;&#29699;&#19981;&#26223;&#27668;&#19979;&#21576;&#29616;
&#25918;&#32531;&#24577;&#21183;&#65292;&#26368;&#26032;&#22269;&#20869;&#29983;&#20135;&#24635;&#20540;&#22686;
&#38271;&#20026;9.9%&#65292;&#36890;&#32960;&#25345;&#32493;&#25918;&#32531;&#12290; &#20013;&#22269;&#20061;
&#26376;&#36152;&#26131;&#30408;&#20313;&#21019;&#32426;&#24405; &#35775;&#35848;&#65306;&#20013;&#22269;&#22806;
&#36152;&#21069;&#26223;&#19981;&#23481;&#20048;&#35266; &#37329;&#34701;&#39118;&#26292;&#65306;&#20013;&#22269;
&#38754;&#20020;&#30340;&#21361;&#26426;&#19982;&#26426;&#36935;
<br>
<form action="AscetikServlet" method="POST" >
<input type="text" id="input" name="input" >
<input type="submit" >
</form>
</body>
</html>


References:
Apache Commons Lang: http://commons.apache.org/lang/
Struts 1: http://struts.apache.org/
Struts 2: http://struts.apache.org/2.x/
WebWork: http://www.opensymphony.com/webwork/
Mycode: http://my-security-projects.googlecode.com/files/testEncoding.war
My code is written for tomcat 6 with java 1.6 and all the above libraries.

2008-10-10

God doesn't play dice. Quantum Crypto is Closer.

A quantum crypto device has finally been tested and demonstrated to the BBC. This will be interesting to see how the devices hold up. Quantum Crypto is based on an odd principle of quantum theory called the Heisenberg Uncertainty Principle. It states that the act of observing an experiment changes the outcome. The current model is communicating over fiber optic cable and can communicate with 5 locations in Vienna. Due to the above mentioned principal, if someone where to perform a MiTM attack or sniff the traffic it would change the outcome of the desired result. The system would then produce a very high rate of errors and once this is detected the system will automatically shut itself down to prevent the interception of any confidential data. They are claiming that this will be an un-breakable encryption and i agree if it truly conforms to the Heisenberg Uncertainty Principle then it should be unbreakable. It will be interesting to see how many government allow this technology within its borders once its perfected. Most governments want at least a backdoor or limit the encryption strength. They have made the system to be robust and to reroute itself incase a quantum link breaks down so not to loose data between the two links.



"We are constantly in touch with insurance companies and banks, and they say it's nearly better that they lose 10m euros than if the system is down for two hours, because that might be more damaging for the bank," said Dr Huebel.



But one thing I have learned in security .. never say you are unbreakable. It would be interesting to see if hackers find a flaw in quantum theory that physicists including Albert Einstein have overlooked for years.


Source: http://news.bbc.co.uk/2/hi/science/nature/7661311.stm

Ghetto Input validation.

I was consulting on a project a few months ago that had very little budget but kept getting hacked weekly. The application was in ASP but that does not really matter for the point that I'm about to make. Since they had very little money and very little time and I wanted to perform very strict input validation I came up with a solution that I am surprised that I have never seen before. Why not just validate the entire query string instead of individual parameters. The entire site has very few post parameters and kept getting hacked through all the GET parameters so I wrote a simple ASP script that I could add to the beginning of every page. If that validation failed then the whole site would redirect to error otherwise execute the page code. I validated the post params individually since there where so few. I know that really what these guys needed to do was use bindable queries but there was aaaaallloottt of SQL and they only accepted alpha numeric, upper and lower case letters. Can anyone think of a reason how this could be exploitable. One quick statement and we stopped all XSS and SQL injection attacks against this site.  These guys where also a very small business that could not afford to be down for days while the code was being developed. For an enterprise I would prolly would not recommend this but for a small startup or local business then I think this could really help.

 

Here is my Classic ASP code.

Validation.asp

Function ValidateQueryString( input)
    Dim re
    Set re = New RegExp
    ' alphanumeric regular expression
    re.global = True
    re.Pattern = "^[a-zA-Z0-9\=\&\ ]+$"
    re.Test(input)
    if(re.Test(input) or input = "") then
        ValidateQueryString = True
    else
        ValidateQueryString = False
    end if
End Function

 

every page will include this line at the top...

<!--#include file="Validation.asp" -->
<%
if(ValidateQueryString(request.QueryString) = False) then
    response.redirect("error.asp")
end if
%>

2008-10-09

No longer confident.

This is something that has really started to annoy me ever since I realized it. It came to me while I was playing with ettercap filters. For those who don't know you can use ettercap to perform ARP spoofing and MiTM attacks. etterap filters give you the added functionality to modify any traffic going to and from the victim or victims that you are ARP Spoofing. This has been fun to do things like replace all the images in webpages that your co-worker has been viewing with your own image that you host. Like the following.


image


Thats fun and all but now I have noticed something that ALOT of websites including many financial institutions are doing. They are trying (i assume) to make their home pages load faster by not SSL'ing their home page but still providing login functionality on this page. They usually have a JavaScript fuction that actually submits your credentials over SSL or it could be in a form. So it occurred to me that anyone who happens to be on my same subnet can ARP spoof me and change the javascript . The javascript could be modified to intercept my credentials, send them to another server, and still log me in and I would never know it. In fact i'm not sure of anything that most users can do to know that they are not a victim.




This is a poor coding practice that takes away all the visual cues that browsers are putting in place to ensure that you are sending your credentials over a secure link and training users not too look for them. Its not that hard to have a button that says "click here to sign in" to redirect you to an SSL'd login page. I have seen some sites that are ssl's but load some of their javascript from non-ssl'd sources which could allow for session stealling from sites that do set their cookies securly. My recomendations to developers are:

1. Only allow your users to enter crendentials on an SSL'd page.

2. Once users are on a secure section of the site then load all your javascript, images, or any other included content from only ssl'd sources as well.


image

image

2008-06-12

CSRFGuard Take 2

Well I been doing a little more experimentation with CSRFGuard and realized I have a flawed configuration in my last post. I figured that the initial page (say your login page) had to be outside your CSRF protected filter because you do not have a session and therefore no csrf token and the filter would fail. But it fails open. At first I thought this was a flaw but this makes perfect sense. You can now protect all your content with one csrf filter and not have to keep a special directory unprotected for your login pages and the like.

 

Another trick that CSRFGuard does is to automatically add your csrf token to your links in most cases. This makes it easier to integrate csrfguard into an existing application and makes it trivial to ensure that every request is sending the csrftoken without you having to code around it. I found that for some dynamically generated code like response.sendRedirect("index.jsp"); this will not work without a little help.

 

There are a few different response handlers that you can set in the csrfguard.properties for CSRFGuard to automatically add your token to your html. They are:

org.owasp.csrfguard.handlers.HTMLParserHandler will automatically parse the html response for a urls to attach the csrf token. This is performed server side.

org.owasp.csrfguard.handlers.RegExHandler will allow you to specify a regular expression that be searched in the html response and the append the token to the match. This action again is performed on the server side. I have not tried this one yet.

org.owasp.csrfguard.handlers.JavaScriptHandler will include javascript to your response (csrf.js). This will append csrf tokens on the client and save you some processor time on the server.

 

The Example

I will walk you through my example that consists of a login page and 2 csrf protected areas.

Here is my basic login page. It does 3 things. (1)If there isn't a session then you are presented with the logon page.(2) if you are submitting your credentials then it checks your credentials then it adds the generated csrf token and your userid to the session. The later is solely to represent a user and nothing to do with csrf guard. Ideally this app would query a sql database or ldap but i'm trying to keep it simple. (3) If you are logged in already and access the logon page then you are redirected back to the csrfProtectedArea1.do

 

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<jsp:directive.page import="org.owasp.csrfguard.util.*"/>
<jsp:directive.page import="javax.servlet.http.*"/>

<%@ page session="true" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>LogIn Page</title>
</head>
<body>

<%

String login ="";
String username = "";
String  password = "";

login = request.getParameter("Login");

// Login is set to 1 when the credentials are sent. not really necessary.

if(login != null && login.equals("1"))
{
    username = request.getParameter("username");
    password = request.getParameter("password");
    if(username != null && password != null && username.equals("Guest") && password.equals("Guest1"))
    {
        HttpSession sess = request.getSession(true);
        sess.setAttribute("uid", "1111");
        response.sendRedirect("/CSRFTest/csrfProtectedArea1.do?OWASP_CSRFTOKEN=" + request.getParameter("OWASP_CSRFTOKEN") );
    }
    else  // login failed
    {
        out.write("<h3> Error: Please Log in again </h3><br>");
        out.write("<h1>Please Login Below</h1>");
        out.write("<form method=\"POST\" action=\"index.jsp\">");
        out.write("<br> User Name: <input type=\"text\" name=\"username\">");
        out.write("<br> Password:  <input type=\"password\" name=\"password\">");
        out.write("<input type=\"hidden\" name=\"Login\" value=\"1\">");
        out.write("<br> <input type=\"submit\" name=\"loginBtn\" value=\"Login\" >    ");
        out.write("</form>");
    }
}
else if (request.getSession().getAttribute("uid") != null && request.getSession().getAttribute("uid").equals("1111"))
{

//session is active that the userid matches.
    HttpSession sess = request.getSession(true);
    response.sendRedirect("/CSRFTest/csrfProtectedArea1.do?OWASP_CSRFTOKEN=" + sess.getAttribute("OWASP_CSRFTOKEN") );
    //response.sendRedirect("/CSRFTest/csrfServlet.do");
}
else
{

// normal login when a session is not present

        out.write("<h1>Please Login Below.</h1>");
        out.write("<form  method=\"POST\" action=\"index.jsp\">");
        out.write("<br> User Name: <input type=\"text\" name=\"username\">");
        out.write("<br> Password:  <input type=\"password\" name=\"password\">");
        out.write("<input type=\"hidden\" name=\"Login\" value=\"1\">");
        out.write("<br> <input type=\"submit\" name=\"loginBtn\" value=\"Login\" >    ");
        out.write("</form>");
}

%>

</body>
</html>

 

Automatic Generation of Tokens

Now in the above code you will notice the following:

response.sendRedirect("/CSRFTest/csrfProtectedArea1.do?OWASP_CSRFTOKEN=" + request.getParameter("OWASP_CSRFTOKEN") );

I'm calling the request.getParamter but my form submission looks like this with no csrf token parameter because the filter will add it to the html automatically.

        out.write("<h1>Please Login Below.</h1>");
        out.write("<form  method=\"POST\" action=\"index.jsp\">");
        out.write("<br> User Name: <input type=\"text\" name=\"username\">");
        out.write("<br> Password:  <input type=\"password\" name=\"password\">");
        out.write("<input type=\"hidden\" name=\"Login\" value=\"1\">");
        out.write("<br> <input type=\"submit\" name=\"loginBtn\" value=\"Login\" >    ");
        out.write("</form>");

 

My generated html on the client for the the login page looks like this:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>LogIn Page</title>
</head>
<body><h1>Please Login Below.</h1><form  method="POST" action="index.jsp"><br> User Name: <input type="text" name="username"><br> Password:  <input type="password" name="password"><input type="hidden" name="Login" value="1"><br> <input type="submit" name="loginBtn" value="Login" >    <INPUT type=hidden name=OWASP_CSRFTOKEN value=eKKqjn7Pqm6GLvc7bCMoYCwPbpFhXDcQSIKVnvLutX8T></form>

</body>
</html>

 

As you can see the <INPUT type=hidden name=OWASP_CSRFTOKEN value=eKKqjn7Pqm6GLvc7bCMoYCwPbpFhXDcQSIKVnvLutX8T> is automatically generated by the HTMLParseHandler

Now lets look at the csrfProtectedArea1.java. This page is session protected and csrf protected.

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import org.apache.jasper.runtime.*;

public class csrfProtectedArea1 extends HttpServlet {
    /**
     *
     */
    private static final long serialVersionUID = -6429166168752177032L;

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
    {

        HttpSession sess = request.getSession();
        if(sess.getAttribute("uid")!= null && sess.getAttribute("uid").equals("1111"))
        {
            response.setContentType("text/html");
            PrintWriter out = response.getWriter();
            out.println("<html>");
            out.println("<body>");
            out.println("You Made it<br>");
            out.println("<a href=\"csrfProtectedArea2.jsp\" > Click Here </a>");
            out.println("<a href=\"index.jsp\" > Click Here </a>");
            out.println("</body>");
            out.println("</html>");
        }
        else
        {
            response.sendRedirect("/CSRFTest/error.jsp");
        }
    }
}

The generated html from this looks like the following.

<html>
<body>
You Made it<br>
<a href="csrfProtectedArea2.jsp?OWASP_CSRFTOKEN=eKKqjn7Pqm6GLvc7bCMoYCwPbpFhXDcQSIKVnvLutX8T&OWASP_CSRFTOKEN=eKKqjn7Pqm6GLvc7bCMoYCwPbpFhXDcQSIKVnvLutX8T" > Click Here </a>
<a href="index.jsp?OWASP_CSRFTOKEN=eKKqjn7Pqm6GLvc7bCMoYCwPbpFhXDcQSIKVnvLutX8T&OWASP_CSRFTOKEN=eKKqjn7Pqm6GLvc7bCMoYCwPbpFhXDcQSIKVnvLutX8T" > Click Here </a>
</body>
</html>

I have two links in this response. One goes back to the login page (index.jsp) and the other goes to another csrf protected area. As you can see the response handler automatically updates all links found in the html with the csrf token. One error I keep getting is that the first link gets 2 tokens added to it. I have not figured this out yet but it does not prevent the application for functioning properly.

 

CSRFGuard Handles the Attack

One other cool thing about csrfGuard is that if you submit a request with a missing or incorrect token CSRFGuard will invalidate your session and redirect you to the error page defined in the csrfguard.properties file.

One problem I have found with this is in the logging. For instance here is an example log file from a failed csrf request.

 

Jun 12, 2008 12:12:31 AM org.apache.catalina.core.ApplicationContext log
INFO: [CSRFGuard] the following properties were loaded into CSRFGuard
     Debug:            true
     ResponseHandler:    org.owasp.csrfguard.handlers.HTMLParserHandler
     Token Name:        OWASP_CSRFTOKEN
     Token Length:        32
     PRNG:            SHA1PRNG
     Action Count:        3
        Action(0)    org.owasp.csrfguard.actions.Redirect
        Action(1)    org.owasp.csrfguard.actions.Log
        Action(2)    org.owasp.csrfguard.actions.Invalidate

Jun 12, 2008 12:14:28 AM org.apache.catalina.core.ApplicationContext log
INFO: [CSRFGuard] the following properties were loaded into CSRFGuard
     Debug:            true
     ResponseHandler:    org.owasp.csrfguard.handlers.HTMLParserHandler
     Token Name:        OWASP_CSRFTOKEN
     Token Length:        32
     PRNG:            SHA1PRNG
     Action Count:        3
        Action(0)    org.owasp.csrfguard.actions.Redirect
        Action(1)    org.owasp.csrfguard.actions.Log
        Action(2)    org.owasp.csrfguard.actions.Invalidate

Jun 12, 2008 12:14:59 AM org.apache.catalina.core.ApplicationContext log
INFO: [CSRFGuard] caught CSRF attack (IP: 0:0:0:0:0:0:0:1 Method: GET URI: %2FCSRFTest%2FcsrfProtectedArea1.do Referer:  Parameters: OWASP_CSRFTOKEN%3DPzEIce4raEWbtC97i8oMgf3Y2yJjf1A1XriZ7GcWaLU)
Jun 12, 2008 12:16:36 AM org.apache.catalina.core.ApplicationContext log
INFO: [CSRFGuard] caught CSRF attack (IP: 0:0:0:0:0:0:0:1 Method: GET URI: %2FCSRFTest%2F Referer:  Parameters: )
Jun 12, 2008 12:17:00 AM org.apache.catalina.core.ApplicationContext log
INFO: [CSRFGuard] caught CSRF attack (IP: 0:0:0:0:0:0:0:1 Method: POST URI: %2FCSRFTest%2Findex.jsp Referer: http%3A%2F%2Flocalhost%3A8080%2FCSRFTest%2Findex.jsp%3FOWASP_CSRFTOKEN%3DOX7CJcASSH10DsQFhHQy6nIBHbuYWeXRguxeCjLch5Iu Parameters: password%3DGuest1%2COWASP_CSRFTOKEN%3DOX7CJcASSH10DsQFhHQy6nIBHbuYWeXRguxeCjLch5I%2CLogin%3D1%2CloginBtn%3DLogin%2Cusername%3DGuest)

 

It will log all parameters from the failed request which in this case will log the username and password of the user. Something to consider if deploying CSRFGuard to a production system.

 

Additional Information

Below are the csrfguard.properties and the web.xml i used.

csrfguard properties file:

org.owasp.csrfguard.Debug=true
org.owasp.csrfguard.ResponseHandler=org.owasp.csrfguard.handlers.HTMLParserHandler
org.owasp.csrfguard.TokenName=OWASP_CSRFTOKEN
org.owasp.csrfguard.TokenLength=32
org.owasp.csrfguard.PRNG=SHA1PRNG
org.owasp.csrfguard.action.class.Log=org.owasp.csrfguard.actions.Log
org.owasp.csrfguard.action.class.Invalidate=org.owasp.csrfguard.actions.Invalidate
org.owasp.csrfguard.action.class.Redirect=org.owasp.csrfguard.actions.Redirect
org.owasp.csrfguard.action.class.Redirect.param.ErrorPage=error.jsp

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>CSRFTest</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
<filter>
  <filter-name>CSRFGuard</filter-name>
  <filter-class>org.owasp.csrfguard.CSRFGuardFilter</filter-class>
    <init-param>
      <param-name>config</param-name>
      <param-value>WEB-INF/csrfguard.properties</param-value>
    </init-param>
</filter>

<filter-mapping>
<filter-name>CSRFGuard</filter-name>
<servlet-name>csrfProtectedArea1</servlet-name>
</filter-mapping>

<filter-mapping>
<filter-name>CSRFGuard</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>

<filter-mapping>
<filter-name>CSRFGuard</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>

  <servlet>
      <servlet-name>csrfProtectedArea1</servlet-name>
      <servlet-class>csrfProtectedArea1</servlet-class>
  </servlet>
  <servlet-mapping>
      <servlet-name>csrfProtectedArea1</servlet-name>
      <url-pattern>/csrfProtectedArea1.do</url-pattern>
  </servlet-mapping>
  </web-app>