EOS platform has created much hype in the blockchain community by offering a high transaction rate and providing an operating system like architecture for managing distributed resources. Companies and developers are quickly adopting and moving on to build their next dapp on EOS. Since the smart contracts supported by EOS are converted to web assembly, C++ was the language of choice for developing them.
The ecosystem around EOS is still in its early stages, and the tools, technologies and practices around developing EOS smart contracts have yet to mature. Smart contracts generally involve transfer of funds, thus it becomes very important to audit them extensively before they become live. Unique design of EOS and it’s choice of language (C++) makes the audit process of the smart contracts fundamentally different from other popular platforms like Ethereum. Through this article, we intend to list various language and platform based parameters that must be checked during the smart contract audit process for EOS.
These parameters about EOS contracts are the consequence of using C++ to write smart contracts. While programming with C++, we need to manage allocation and deallocation of memory for the instances that are created dynamically during runtime. Dangling pointers (and references) refer to the memory locations that was previously held by some objects which are now deallocated. This part of the memory may be reallocated to some other object, which can cause the original pointer (or reference) to now point at this reallocated data.
This can cause unpredictability in our application. Usage of dangling pointers inside code can at very least cause undesirable behaviour, in some cases corruption of data, and can even be exploited maliciously. Dangling pointers can be targeted by adding malicious code to the memory locations pointed to by them, so that during their further use in application logic, malicious data is introduced in the system and may cause undesirable behavior.
Buffer overflow is a flaw where while writing data to a buffer, the program exceeds the limit of the buffer and overwrites the memory locations adjacent to the buffer boundaries. This can happen when we have an assumption on the size of inputs to the buffer for a certain operation. If the input happens to be larger than our assumed value, it may start to overwrite on the memory locations adjacent to the the memory locations of the buffer.
Exploiting buffer overflow is a known security vulnerability. Basic idea is to provide inputs with malicious data that are large enough to cause buffer overflow, and overwrite other variables of the application logic. This way, by influencing data in the input, attacker can try to influence the program into producing desirable outputs. Further if poorly designed smart contract code is made publically available, it will give attackers an ample opportunity to analyze the structure and perform buffer overflows on the vulnerable parts of the contract.
In EOS, the publically available executable functions of the smart contract are called actions. Each action can be called upon by the accounts interacting with the smart contract. These actions may have some permission level associated with them, which can be easily handled while using the default permissions (owner or active). Mapping of actions to the permissions is handled by the dispatcher, which is the first entry point inside the contract. It is specified using EOSIO_DISPATCH macro.
However, many times we may need to add custom permissions in our application. In that case we need to write a custom dispatcher, that maps the actions to the custom permissions. A poorly mapped action to a permission level may leave the smart contract code open to malicious actors, that can have unauthorized access to the actions. Proper permissions and authorization management inside an operation is a general practice that must be followed in developing smart contracts on any platform.
This is especially true for betting games type contracts that depend on some condition like value of some random number to decide the winner and transfer funds to the winner account. In a well known incident, attackers made an identical contract and were able to generate the winning numbers beforehand. This led them to place correct bets each time and transferred funds to themselves.
It should be noted that it is not possible to obtain non deterministic values in a deterministic environment. Blockchain frameworks like EOS have to be deterministic in nature as a transaction on one EOS node must be identical on every other node in order to ensure consensus. However there are ways to generate random numbers as explained here.
EOS provides the RAM as the resource for fast access and persisting application data. The access to the RAM is provided by the EOS library in form of multi index tables. These tables are capable of storing any kind of data specified through a struct object passed while instantiating them. These tables are generally used to store important contract specific data like balances or whitelisted accounts.
So in the absence of proper checking during arithmetic operations over this data can cause the values to overflow, an hence result in data loss. This can especially be dangerous if the records being corrupted points to the balances of accounts. As a good practice, always use the asset structure provided by the eosio library. It overloads all the basic arithmetic operators and its usage is prefered over extracting balances and then performing operations.
The EOS smart contracts provide the functionality to respond to notifications from other contracts. Thus when there is no proper checking on the context of notifications, a fake notification can be designed to trigger the action on the victim contract. In a famous betting game hack, fake notifications were used to trigger transfer funds action of the contract, and the attackers were able to transfer funds to themselves by providing fake notifications to the victim contract. To avoid this, we should check all the parameters from the notifications and respond only to those notifications that are of interest to the current contract.
As mentioned above, RAM is a critical resource and cost for storing data on the RAM can be a big hurdle when trying to scale our application. In this regard, for the application to scale and be cost effective, it is very important that we can optimize usage of RAM in our Dapp.
There are two aspects when it comes to optimizing RAM usage. Firstly, we need to determine when it is really important to allocate RAM for storing data. To avoid costs, one would try to structure data in such a way, that the required data can be ‘derivable’ from the stored data, and that the stored data is minimalistic in nature. Secondly, we should try to identify opportunity where we can release the RAM so as to free usage for storing new data. Keeping these both points in mind, it would be possible to write smart contracts that are cost effective, and consequently scalable. Same principle should be applied while auditing contracts.
In EOS, the transaction is finalized when majority of block producers (more than 2/3rd of 21) approves them. Until then, the transaction is not finalized and could be “rolled back”. The block producers maintains a list of blacklisted accounts that can not do transactions (blacklisted due to violation of rules or malicious behaviour).
So the blacklisted account could initiate a transaction to a victim contract, which may reach a full node such that full node is not a block producer and does not have a blacklist, for example a betting contract so as to make it reveal some number that could be used to transfer funds, or to detect an inline action from which an outcome can be inferred, which could be used maliciously. Since the transaction initiated by the blacklisted account will eventually fail (or rolled back) once it reaches block producer, this would cause the victim contract to reveal information and at the same time, the transaction initiated by blacklisted account will not happen. To avoid such circumstances, one should make sure that some “reveal” feature if available, must only be called after some verifiable event. Also we could make use of “deferred” actions, which are those actions that are executed after some time. In such case, the victim contract can be made aware beforehand, if the malicious transaction was rolled back before the time of deferred action.
In this article, we covered some general hotspot areas inside the EOS smart contracts that must be inspected while auditing them. This list is by no means exhaustive, and we will continue to discover and index more vulnerable areas about EOS smart contracts as it matures. Further speaking, it is highly advisable to get your contracts audited by trusted 3rd party companies offering auditing services. At QuillAudits, we have a proven track record of developing and auditing smart contracts on major blockchain platforms.
We hope that this guide has been useful. Thanks for reading!
Stay tuned for the next part…
Part 2 — EOS Smart Contracts Audit checklist to Keep In mind Before Development
Part 3- Understanding fundamental concepts for writing dApps on EOS.
Part 4- Analysing EOS standard token contract .
Part 5- Develop a basic crowd sale application with EOS .
Part 6- EOS for high performance dApps — Games on EOS!
Disclaimer: The views expressed by the author above do not necessarily represent the views of EOS GO. EOS GO is a community where EOS GO Blog being a platform for authors to express their diverse ideas and perspectives. To learn more about EOS GO and EOS, please join us on our social medias.
EOS GO is funded by EOS ASIA and powered by YOU. Join the community and begin contributing to the movement by adding eos go to your name and joining the EOS GO telegram group.