Archive for the ‘Solutions’ Category

Millions of PDF invisibly embedded with your internal disk paths

Sunday, November 22nd, 2009

I found an interesting privacy issue while analyzing PDF files. This bug occurs when you are using Internet Explorer to print locally saved web pages as PDF and affects all IE versions including IE8. It does not matter which PDF generation software you are using like Adobe Acrobat Professional, CutePDF, PrimoPDF, etc as long as you are invoking it from inside the IE print function. In Windows, even when your default browser is not IE and if you right click a file to select the PRINT from the context menu, then by default it invokes the IE print handler. So, you will still see this issue in the generated PDF.

This bug is NOT ABOUT the local disk path appearing in the FOOTER of your pdf since it is clearly visible and already known by most people. This is easy enough to hide by just going File -> Page Setup -> Change the Footer value from “URL” to “-Empty-”. After doing that, you will not expect your internal disk path being put anywhere else. However, that does not happen.

The privacy issue arises from the fact that your local disk path gets invisibly embedded inside your PDF in the title attribute. Only when you open the file in an Editor like Notepad, you will see it. Currently, there is no option in IE to disable it. The only workaround is to manually nullify this value by editing the PDF file. Note that this problem does not occur when using other browsers such as Firefox and Chrome. In fact, Chrome handles the other footer issue intelligently as well by showing your disk path as “…”, rather than exposing it.

Proof of Concept:

Steps to reproduce:
1. Pick a .HTM or .HTML or .MHT file on your local computer.
2. Open this file in IE and click Ctrl-P.
OR Right-click the file in explorer and select PRINT from context menu.
4. Select any PDF writer as Printer such as Adobe PDF / CutePDF / PrimoPDF / etc.
5. Click Print. When the PDF writer asks for a filename, provide any name.
6. Open the generated pdf in notepad, and search for “file://” without quotes.

Search for this on your favorite search engine (Google/Bing)

filetype:pdf file c (htm OR html OR mhtml)

Google Search 1 (for drive C) – 4 million results
Google Search 2 (for drive D) – 13 million results
and so on…. (I added till drive letter J and total was more than 50 million….)

So, out of 280 million pdfs accessible on the internet, more than 20% look to be exposing internal disk paths which is a huge number. I have contacted the Microsoft and Adobe Security Teams about this issue. Microsoft has plans to fix this in IE9, while Adobe has opened the case but hasn’t planned the timelines yet.

Examples:

http://www.eda.gov/PDF/EDA_vol1;%20Issue10.pdf

<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 4.0-c316 44.253921, Sun Oct 01 2006 17:14:39">
   <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
      <rdf:Description rdf:about=""
            xmlns:dc="http://purl.org/dc/elements/1.1/">
         <dc:format>application/pdf</dc:format>
         <dc:creator>
            <rdf:Seq>
               <rdf:li>LewtasS</rdf:li>
            </rdf:Seq>
         </dc:creator>
         <dc:title>
            <rdf:Alt>
               <rdf:li xml:lang="x-default">file://C:\Documents and Settings\lewtass\Desktop\eda newsletter</rdf:li>
            </rdf:Alt>
         </dc:title>
      </rdf:Description>

http://www.oregon.gov/OMD/OEM/plans_train/grant_info/fy2009_hsgp_investment_justification.pdf

<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="3.1-701">
   <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
      <rdf:Description rdf:about=""
            xmlns:pdf="http://ns.adobe.com/pdf/1.3/">
         <pdf:Producer>Acrobat Distiller 7.0.5 (Windows)</pdf:Producer>
      </rdf:Description>
      <rdf:Description rdf:about=""
            xmlns:xap="http://ns.adobe.com/xap/1.0/">
         <xap:CreatorTool>PScript5.dll Version 5.2.2</xap:CreatorTool>
         <xap:ModifyDate>2009-03-18T15:07:10-07:00</xap:ModifyDate>
         <xap:CreateDate>2009-03-18T15:07:10-07:00</xap:CreateDate>
      </rdf:Description>
      <rdf:Description rdf:about=""
            xmlns:dc="http://purl.org/dc/elements/1.1/">
         <dc:format>application/pdf</dc:format>
         <dc:title>
            <rdf:Alt>
               <rdf:li xml:lang="x-default">mhtml:file://O:\fema\shsp_2009\draft ijs\fy 2009 investment jus</rdf:li>
            </rdf:Alt>

Bypassing OWASP ESAPI XSS Protection inside Javascript

Thursday, August 20th, 2009

Everyone knows the invaluable XSS cheat sheet maintained by “RSnake”. It is all about breaking things and features all the scenarios that can result in XSS. To complement his efforts, there is an excellent XSS prevention cheat sheet created by “Jeff Williams” (Founder and CEO, Aspect Security). As far as I have seen, this wiki page provides the most comprehensive information on protecting yourself from XSS on the internet. It advises using the OWASP ESAPI api to mitigate any XSS arising from untrusted user input.

I was evaluating this ESAPI api and the recommendations given on the wiki to see if there are any potential flaws. Any weakness impacts a very large number of users since many developers are using it to strengthen their web applications throughout the world. This is my way of contributing back to the community, but can never match the immense efforts put by Jeff and other OWASP team members in developing this library.

I want to give you a little bit of background before diving into the real vulnerability. The XSS prevention cheat sheet classifies XSS protections by dividing them into broadly four buckets – HTML Body injection, HTML Attribute injection, Javascript injection and CSS injection. For each of these four buckets, there is an ESAPI function reference you can use for output escaping/encoding.

If you allow any untrusted user input into javascript functions document.write() OR eval(), it can still execute the XSS even after you do the scrubbing using the ESAPI encodeForJavaScript() function. The reason being that hex escaped chars are converted back into normal chars at the time of execution of these functions.

Here is the proof of concept jsp code:

<%@page import="org.owasp.esapi.*"%>

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!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>ESAPI XSS Protection Bypass</title>
    </head>
    <body>
        <h1>ESAPI XSS Protection Bypass</h1>
        <p id="tb1"/><br>
        <p id="tb2"/>
        <script>
            //in real scenario, these three strings come from request.getParameter or user input
            <%
                String vulstr1 = "-1';alert(0);";
                String vulstr2 = "<img src=x onerror=alert(1)>";
                String vulstr3 = "0,x setter=alert,x=2";
            %>   

            // you can safely use it in places like this
            // Ex. vulstr1 is completely encapsulated in a and alert(0) not executed.
            var a='<%= ESAPI.encoder().encodeForJavaScript(vulstr1) %>';
            alert(a);

            // However, you can bypass protection in places like these
            // Ex. vulstr2 gets written to html and alert(1) executes
            document.write("<%= ESAPI.encoder().encodeForJavaScript(vulstr2) %>");
            // Ex. part of vulstr3 get assigned to u, rest alert(2) executes
            eval("u=<%= ESAPI.encoder().encodeForJavaScript(vulstr3) %>");
        </script>
    </body>
</html>

Much thanks to Jeremiah Grossman and Jeff Williams for taking the time to review my idea and providing their insights. Jeremiah told me that he has seen such injections from time to time at WhiteHat and these do exist in the wild.

Jeff confirmed that some documentation changes will fix this. I agree that no esapi code change is required, because function themselves are not insecure.

But, if you are currently using esapi functions inside your javascript code, it is important that you re-review your javascript code and the places where your make calls to esapi functions.

If you use the esapi function encodeForJavaScript() inside document.write, it is advised that you change them with other appropriate esapi functions depending on the context where the data is ultimately landing. For example, if you have document.write(“<script>alert(‘XSS’)</script>”), you know the data is landing in html body context, so it is appropriate to use encodeForHTML() wrapper. Using user input inside eval is less common, but more disastrous. The reason for this is you can still begin another command context using , and (space) char and it won’t be encoded by function encodeForHTML(). So, it is better to avoid putting user input inside eval.

Any more suggestions or discussion on fixes is highly welcome.

Hacking CSRF Tokens using CSS History Hack

Saturday, July 18th, 2009

Update: Security researchers Sirdarckcat and Gareth were kind enough to share the code for a pure CSS based CSRF token finder here . This is stealthier than my PoC below, which used a combination of both JS and CSS. So, it will still work even if you disable javascript and you are not safe anymore :( . To make this PoC more responsive to the client, you need to use multiple CSS stylesheets using the import command. The only problem I see with this pure CSS based approach is there will be network latency involved with large key spaces because your large CSS stylesheet will need to be downloaded by your browser.

I was thinking about the problem of Cross Site Request Forgery and current mitigation strategies used in the Industry. In many of the real world applications I have tested so far, I see the use of random tokens appended as part of url. If the request fails to provide any token or provide a token with incorrect value, then the request is rejected. This prevents CSRF or any cross domain unauthorized function execution.

Uptil now, it was considered infeasible for an attacker to discover your CSRF token using Brute Force Attacks on the server.

The reasons being:

  1. It generates lot of noise on the network and is slow. So most probably an IDS or Web App Firewall will pick up the malicious behavior and block your ip. For example, a Base16 CSRF token of length 5 characters (starting with a character) will generate approximately 393,216 requests.
  2. Many applications are programmed to invalidate your session after it detects more than a certain number of requests with invalid token values. E.g. 30.

I am going to change this belief by showing you a technique to quicky find csrf tokens without generating alerts. This technique is a client side attack, so there is almost no network traffic generated and hence, your server and IDS/Web App Firewalls won’t notice it at all. This attack is based on the popular CSS History Hack found by Jeremiah Grossman 3 years ago.

In this exploit, we discover the csrf token by brute forcing the various set of urls in browser history. We will try to embed different csrf token values as part of url and check if the user has visited that url. If yes, there is a good chance that the user is either using the same CSRF token in the current active session or might have used that token in a previous session. Once we have a list of all such tokens, we can just try our csrf attack on the server using that small list. Currently this attack is feasible for tokens with length of 5 characters or shorter. I tried it on a base16 string of length 5 and was able to brute force the entire key space in less than 2 minutes.

Some of the prerequisites for this attack to work are either

  1. CSRF token remains the same for a particular user session. e.g. csrf token=hash(session_id) OR
  2. CSRF token submitted in older forms for the same session is accepted. Many times, this is the case as it enhances user experience and allows using forward and back browser buttons.

Proof of Concept is available here.
Before running the PoC, you need to change the url and csrftoken paramater values.

For testing using the defaults, you need to first visit one of the following urls, e.g.

  1. /?param1=val1&csrftoken=b59fe [change b59fe to any 5-digit base 16 string starting with a character, i.e.greater than a0000]
  2. http://tinyurl.com/l2lwgd [which is 301 redirect to previous url].

Note: http://www.securethoughts.com and http://securethoughts.com are treated differently while storing in browser history.

A sample run will look like this –

CSRF Token using CSS History Hack

For making this attack unfeasible,

  1. Server-Side Solution (for developers):
    • Make your CSRF tokens long enough (8 or more chars) to be unfeasible for a CLIENT SIDE attack. The ever-increasing processing power will make this attack feasible for longer tokens as well.
    • Store your CSRF token as part of hidden form field, rather than putting in url.
    • Use a different random token for every form submission and not accept any obsolete token, even for the same session.
  2. Client-Side Solution (for your customers/users):
    • Use a browser plugin such as SafeHistory, which defends against visited-link-based tracking techniques.
    • Use the private browsing mode in your browser.

And last, but not the least, XSS obliterates all the CSRF protections possible. So, get rid of XSS first.

I would like to thank Jeremiah for providing his insightful feedback on this post.