The Request With Too Many Parameters — Java Bug Hunt

Modelled on the hash-collision denial-of-service advisory of December 2011 (oCERT-2011-003), presented by Alexander Klink and Julian Wälde at the 28th Chaos…

  • Language: Java
  • Layer: Backend
  • Difficulty: Easy
  • Concepts: Security, Parsing, Limits
  • Modelled on: Hash-collision DoS · oCERT-2011-003
  • Visible tests: a normal form parses; a body over the cap is refused
  • Reward: 50 XP for a complete fix

Briefing

Modelled on the hash-collision denial-of-service advisory of December 2011 (oCERT-2011-003), presented by Alexander Klink and Julian Wälde at the 28th Chaos Communication Congress. The hash tables that web platforms — PHP, Java application servers, Python, ASP.NET and others — used to hold request parameters could be pushed into their worst case by a request carrying very many parameters, so parsing a single POST body could tie up a CPU for a long time. Among the mitigations, PHP 5.3.9 added max_input_vars (default 1000) and Tomcat added a maximum parameter count: stop accepting parameters past a cap.

This reconstruction's form parser has a maxParams argument but never enforces it.

Fix FormParser.parse so a body with more parameters than the cap is rejected, while normal parsing stays exactly as it is.

Bug report

BUG-HASHDOS · Priority: High · Reported by: platform security

FormParser.parse(body, maxParams) -> LinkedHashMap in first-seen order:

  • the body is split on "&"; empty segments are ignored and do not count
  • each segment splits at its FIRST "=": key before, value after (the value may itself contain "="); a segment with no "=" is a key with value ""
  • a repeated key overwrites the earlier value but still counts as a parameter
  • if the number of counted parameters exceeds maxParams, throw TooManyParametersException (the whole request is refused; exactly maxParams parameters is fine)
  • parse(body) uses Limits.MAX_INPUT_VARS (1000)

Observed: a body with 200,000 parameters was parsed in full.

Logs

[http] POST /login body=4.1MB params=200000
[http] worker 7 busy for 94s parsing form body

The code as shipped

src/http/FormParser.java (editable)

class FormParser {
    // Parses an application/x-www-form-urlencoded body (this service does no
    // percent-decoding). Later duplicates overwrite earlier ones.
    static Map<String, String> parse(String body, int maxParams) {
        Map<String, String> params = new LinkedHashMap<>();
        for (String pair : body.split("&")) {
            if (pair.isEmpty()) continue;
            int eq = pair.indexOf('=');
            String key = eq < 0 ? pair : pair.substring(0, eq);
            String value = eq < 0 ? "" : pair.substring(eq + 1);
            params.put(key, value);
        }
        return params;
    }

    static Map<String, String> parse(String body) {
        return parse(body, Limits.MAX_INPUT_VARS);
    }
}

Read-only context: src/http/Limits.java.

Open the hunt to edit the files, run the visible tests and submit against the hidden ones. More Java bug hunts.