Skip to content

Notes ·

JSON Is Cheap to Send and Expensive to Believe

A post on the Full Disclosure mailing list this morning describes an old denial-of-service technique with the wonderfully understated name "Commas of D00m." The proof of concept targets JSON deserializers by sending them enormous collections containing mostly null values. That sounds almost too simple. It is. That is also why it is interesting.

The attacker does not need malformed JSON, executable code, or some exotic parser exploit. They send something structurally similar to {"serviceTypes":[null,null,null,null,null,...]}, except the array is enormous. Then they do it repeatedly. What is cheap on one side of the connection may be considerably more expensive on the other.

When a web application receives JSON, it usually does not leave the request as a pile of characters. A deserializer turns that text into objects the application can work with. Make a collection enormous and the parser still has to process every separator, recognize every value, allocate the destination collection, and hand the resulting object to the application.

The attacker sends characters. The server creates state. That difference is the attack. A comma and the word null cost almost nothing to generate. Processing a million of them is not necessarily cheap. Send several of those requests at once and memory pressure rises, CPU gets consumed parsing garbage, worker threads stay occupied, and legitimate requests wait behind them. Eventually something may run out of memory, time out, or fall over. No clever remote-code-execution chain required—you just persuade the server to work much harder than you did.

I think this is the part people outside security may find surprising. We tend to think of malicious input as somehow invalid. This attack can use completely legitimate JSON. The problem is not can I parse this? The problem is should I agree to parse this much of it? Valid does not mean reasonable.

The Full Disclosure code creates a very large JSON array and sends copies concurrently, watching for 413, 429, and server-side 5xx errors. A 413 is encouraging—something rejected the request as too large. A 5xx under this kind of load can be much more interesting, because it may indicate the request made it far enough into the stack to start exhausting resources. Every layer that accepts unbounded work on behalf of an untrusted request becomes part of the attack surface.

"Insecure deserialization" usually makes security people think about gadget chains and code execution. The authors here are making a somewhat different argument. Deserialization can be insecure even when no unexpected code gets executed. Simply allowing an attacker to force the parser to construct absurdly large objects is enough. Unconstrained resource consumption during deserialization is the clearer description.

This family of attacks has been around a long time. XML had its famous versions; so did catastrophic regex backtracking and compression bombs. The recurring pattern is the same: make the victim spend substantially more resources processing the input than the attacker spent creating it. JSON is simply another place where that asymmetry can appear.

There is something almost elegant about filling the payload with null. Very little information in it—mostly punctuation and the absence of values. Yet the receiver still has to recognize all of it. Opening every package is the problem. Hence the commas.

There is no reason an ordinary API should accept arbitrarily large collections just because the programming language technically can. If an endpoint expects five service types, accepting 500,000 of them is not flexibility. It is missing validation. Limit request body sizes, limit items in collections, limit nesting depth, put boundaries around parsing, reject unreasonable inputs early. A generic JSON parser cannot know that an array called serviceTypes should probably contain fewer items than the population of Sacramento. The application does.

There is no zero-day here. No shellcode. It is a web request containing an unreasonable number of commas. Sometimes the dangerous property is simply how much of it we agreed to process. The Full Disclosure authors say they have been using variations of this technique for more than 15 years. The underlying mistake is remarkably durable: accept untrusted input, perform unbounded work, hope nobody notices. Someone always eventually notices. And sometimes all they need is a comma.

Read the Full Disclosure post on JSON deserializer resource consumption.

All notes