NEO is a non-profit, community-based blockchain project. It is a distributed network that uses blockchain technology and digital identity for asset digitization. It is also an intelligent management of digital assets using intelligent contracts to create “Smart Economy”. At present, NEO’s market capitalization ranks fifteenth in the world in coinmarket, being one of the remarkable blockchain projects. We found a Denial of Service vulnerability in the NEO smart contract platform which attacker could use to instantly crash the entire neo network.
The NEO Smart Contract Platform provides the contract with a system call (System.Runtime.Serialize) to certain object on the serialized virtual machine stack. This call processes the contract request without considering the nesting of the array, which will cause crash of the smart contract system platform. Neo currently has 7 master nodes responsible for verifying and packaging the entire network transaction. Malicious users can post malicious contracts that exploit the vulnerability to the neo network. While parsing the malicious contract, the 7 master nodes will crash. Furthermore, it will cause denial of service across the entire neo network. The details of the vulnerability are as follows:
The system call (System.Runtime.Serialize) pops the user-executable elements that are at top of the stack and then calls the SerializeStackItem function for serialization. The SerializeStackItem function is:
The general idea is: when the contract calls the System.Runtime.Serialize, it will pop out the first element (parameter StackItem item) on the virtual machine stack, then serialize it with function StackItemItem and write it to the binarywriter writer. SerializeStackItem cheks the element type and then performs a corresponding serialization operation.
There are many types of StackItem. If it is an array, the array size and child elements will all be serialized again. The array here is customized defined by NEO. Originally, it is a List. One scenario is not took into consideration here that an attacker might add array a as a child element to array a, i.e., a.Add(a). If you de-serialize a at this time, you will enter an infinite loop, until the program stack space is exhausted and triggers stack overflow exception (StackOverflowException).
In fact, in the NeoVM code, we can see that the outer layer of the virtual machine execution has set state catch for any exception:
However, the exception catch cannot handle the StackOverflowException. A StackOverflowException in .net will cause the entire process to exit and fail to catch the exception. This in turn causes the entire neo node process to crash directly.
Attack virtual machine commands: push(a), dup(), dup(), appen(), System.Runtime.Serialize() can cause a stack overflow exception and the program will crash directly. [Similarly, the stuct structure and map structure can also use this vulnerability].
It is worth mentioning that within 7 minutes after we emailed the NEO official to notify this vulnerability, Erik Zhang, one of the founders of NEO, replied directly to confirm the existence of the vulnerability and submitted the bug fix within an hour. Their efficiency is quite amazing. The official fix for this vulnerability is very thorough, preventing this iterative reference by adding a List of serialized elements. Please see the picture below for details:
Vulnerability timeline:
2018/8/15 15:00 Found and tested the vulnerability
2018/8/15 18:57 Mailed the details to NEO
2018/8/15 19:04 NEO officially confirmed the existence of the vulnerability
2018/8/15 20:00 The founder of NEO Erik Zhang released bug fixes
PoC:
The running result is as below: