Nobody likes it when they’re prevented from getting what they want, and this is exactly what a denial of service (DoS) attack does. DoS attacks prevent users from accessing a particular service while also preventing the provider of that service from serving its customers.
Unfortunately, even blockchain technology isn’t immune to DoS attacks. In this post, we talk about the NEO DoS vulnerability that was recently discovered in NEO smart contracts. We give details about the NEO DoS [Denial of Service] vulnerability and show how to recreate it.
NEO is a smart economy platform aimed at digitizing real world assets with smart contract technology. The NEO network is in active development, so it’s fine if a few bugs are found and fixed along the way. A recent bug in NEO smart contracts (the NEP-5 token bug) allowed attackers to edit variables in the smart contract’s persistent storage by providing invalid transaction input. Our previous post contains more details about the NEO network in general and the NEP-5 token bug in particular. The most important thing about that bug is that it wasn’t a major issue. It has the potential to become a serious vulnerability if a complex contract were affected, but for the currently affected NEO smart contracts (most of which are NEP-5 tokens), the risk from this issue is negligible.
But in this article we focus on a more serious issue – the DoS vulnerability bug on the NEO blockchain.
Unlike any previous vulnerabilities in the NEO network, the most recent vulnerability, which was discovered by Zhiniang Peng from Qihoo 360 Core Security on August 15, 2018, could affect the entire network. This problem could result in a full-force denial of service attack. This attack would originate from a malicious smart contract and could be activated by invoking the contract. As the transaction was processed by the network nodes, each node would crash, eventually leading to the total collapse of the network.
Let’s take a look at the vulnerable code and the issue hidden in it. The problem lies within the smart contract platform, specifically in the System.Runtime.Serialize system call.
This is what the implementation of this system call looks like:
The DoS vulnerability hides in line 328 of the code shown above. There, recursion is triggered for an array of items. The same serialization function will be triggered for each element of the array. However, if the array contains a reference to itself, then the function will be called again for that same array. This will result in an infinite loop which eventually will trigger a stack overflow exception.
In most cases, such stack overflow exceptions are properly caught and handled, so a single exception can’t affect the entire program. But in this case, unfortunately, the stack overflow isn’t handled and the program crashes.
To get a better understanding of this problem, let’s try to recreate the NEO smart contract DoS vulnerability. In their original post, Qihoo 360 provided a proof of concept (PoC) program that would trigger the exception. However, that program isn’t a smart contract and simply uses the NEO library to demonstrate the crash.
We can provide a more accurate reproduction of the vulnerability by creating a malicious smart contract. This contract has to perform five actions:
- Create two arrays
- Add a reference to the first array into the second array
- Add a reference to the second array into the first array
- Put either of the two arrays on the program’s stack
- Execute the System.Runtime.Serialize system call
The first four steps of this algorithm are simple. This is how you can implement a smart contract that creates a cyclic reference in an array and pushes it onto the stack:
The problem is in the fifth step. There’s no straightforward way to call the serialize function and there’s no way to add inline assembly in NEO smart contracts. So to add the call, we need to edit the compiled smart contract file and insert the needed opcode into it.
First, we need to compile a smart contract without any optimizations. We don’t apply any optimizations at this point for one reason – they may remove some of the necessary steps from the algorithm described above. For instance, the last line that pushes the array onto the stack for us can be removed.
Here’s the resulting opcode from the smart contract above:
Now we need to add the SYSCALL opcode instead of the final DROP opcode. The parameter for SYSCALL is the name of the function. The opcode will take parameters from the stack and execute the vulnerable function. To add the opcode, you can edit the hex representation of the compiled contract or open the avm file in any hex editor and edit the contract bytecode.
Now the malicious smart contract is ready and can be deployed just like any other contract. To exploit the vulnerability, you need to invoke the contract. However, most wallets and command-line interface tools require testing the contract invocation before actually sending the transaction. During the test, the function is executed to estimate gas usage, and the wallet will crash due to the same exception.
To send the transaction directly to the network, you can use a remote procedure call. The easiest way to do that is to use Postman to send the request.
Here’s what the request that would start the exploit looks like:
In this request, the only parameter needed to launch the contract (in the params array) is the script hash of the smart contract itself. You can find more information about this method of invoking a smart contract here.
Propagating this transaction will result in consensus nodes crashing one by one as they attempt to process the request. Eventually, it’ll halt the entire network for as long as the transaction remains pending and the nodes keep attempting to process it.
The NEO network is a rather new yet promising smart contract platform. And even though certain bugs and security issues are discovered every now and then, the NEO team is working hard to fix those problems and improve their platform.
In the case of the denial of service vulnerability described in this post, the NEO team submitted the bug fix within a few hours of its discovery, which is an impressive response time for any development team. However, knowing the details about the NEO DoS vulnerability is beneficial for anyone who wants to ensure a high level of security and protection of their smart contracts on the NEO network.
Are you looking for a team of experienced blockchain professionals who can help you build a secure and bug-free blockchain-based solution? Get in touch with us and we’ll get back to you shortly!