Tags:
Originally posted as Taming the Beast
The recent multi-language 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 approaches that may help you tame this beast of a bug.
1) ModSecurity Rules
Ryan Barnett deployed a series of ModSecurity rules and documented several options at http://blog.spiderlabs.com/2011/02/java-floating-point-dos-attack-protection.html
2) Java-based Blacklist Filter
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) Double Parsing Code
The following Java "safe" Double parsing code was based on a proof of concept by Brian Chess at HP/Fortify. This approach detects the evil range before trying to call parseDouble and returns the IEEE official value ( 2.2250738585072014E-308 ) for any double in the dangerous range.
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(); }