Sunday, February 13, 2011

Taming the Beast

The recent cross-platform numerical parsing DOS bug has been named the "Mark of the Beast". Some claim that this bug was first reported as early as 2001.

This is a significant bug in (at least) PHP and Java. Similar issues have effected Ruby in the past. This bug has left a number of servers, web frameworks and custom web applications vulnerable to easily exploitable Denial of Service.

Oracle has patched this vuln but there are several non-Oracle JVM's that have yet to release a patch. Tactical patching may be prudent for environment.

Here are three filters that may help you tame this beast of a bug.

1) Ryan Barnett deployed a series of mod security rules and documented several options here

2) Bryan Sullivan from Adobe came up with the following Java-based blacklist filter. This rule is actually quite accurate in *rejecting input* in the DOSable JVM numeric range. This fix, while simple, does indeed reject a series of normally good values.

public static boolean containsMagicDoSNumber(String s) {
return s.replace(".", "").contains("2225073858507201");

3) The following data sanitization code came from Brian Chess at HP/Fortify. This approach detects the evil range before trying to call parseDouble and returns the IEEE official value for any double in this most evil range ( 2.2250738585072014E-308 ).

private static BigDecimal bigBad;
private static BigDecimal smallBad;

static {
BigDecimal one = new BigDecimal(1);
BigDecimal two = new BigDecimal(2);
BigDecimal tiny = one.divide(two.pow(1022));
// 2^(-1022) ­ 2^(-1076)
bigBad = tiny.subtract(one.divide(two.pow(1076)));
//2^(-1022) ­ 2^(-1075)
smallBad = tiny.subtract(one.divide(two.pow(1075)));

public static Double parseSafeDouble(String input) throws InvalidParameterException {

if (input == null) throw new InvalidParameterException("input is null");

BigDecimal bd;
try {
bd = new BigDecimal(input);
} catch (NumberFormatException e) {
throw new InvalidParameterException("cant parse number");

if (bd.compareTo(smallBad) >= 0 && bd.compareTo(bigBad) <= 0) {
// if you get here you know you're looking at a bad value. The final
// value for any double in this range is supposed to be the following safe #
//return safe number
System.out.println("BAD NUMBER DETECTED - returning 2.2250738585072014E-308");
return new Double("2.2250738585072014E-308");

//safe number, return double value
return bd.doubleValue();