A Primer on Predatory Auditing
TL;DR: Someone tried to accuse the Status project of being a scam and having godmode backdoors in their token contract.
In blockchain, predatory auditing is when a self-proclaimed software auditor finds bugs in smart contracts, then expects money in the form of a bounty (i.e. hush money) or future jobs doing "auditing".
This blackmail comes in many forms - sometimes the threat is private, and sometimes it's forcing the team to do damage control by doing exposure first. Additionally, when attacking established projects and organizations, these people are hoping to elicit an empathetic response from bystanders who witness a "David vs. Goliath" fight and deem the attacker a hero.
Here is an example.
Binod Nirvan published a "report" about SNT - the Status Network Token. For context, Status is a mobile and desktop application for blockchain-based chat and dapp use that had an ICO in 2017, raising $100 million. Status currently employs around 100 people, funding hackathons around the world and forwarding the ecosystem through innovation.
Disclaimer: I work for Status and have Binod among my connections, which is how I noticed this.
He has removed his post since, but for reference I'm including screenshots below.
Unprofessional hashtags, personal attacks and general attitude aside, the post refers to lines 723 and 740 in the SNT contract. The logic in question is:
/// @notice Generates `_amount` tokens that are assigned to `_owner`
/// @param _owner The address that will be assigned the new tokens
/// @param _amount The quantity of tokens generated
/// @return True if the tokens are generated correctly
function generateTokens(address _owner, uint _amount
) onlyController returns (bool) {
uint curTotalSupply = getValueAt(totalSupplyHistory, getBlockNumber());
if (curTotalSupply + _amount < curTotalSupply) throw; // Check for overflow
updateValueAtNow(totalSupplyHistory, curTotalSupply + _amount);
var previousBalanceTo = balanceOf(_owner);
if (previousBalanceTo + _amount < previousBalanceTo) throw; // Check for overflow
updateValueAtNow(balances[_owner], previousBalanceTo + _amount);
Transfer(0, _owner, _amount);
return true;
}
/// @notice Burns `_amount` tokens from `_owner`
/// @param _owner The address that will lose the tokens
/// @param _amount The quantity of tokens to burn
/// @return True if the tokens are burned correctly
function destroyTokens(address _owner, uint _amount
) onlyController returns (bool) {
uint curTotalSupply = getValueAt(totalSupplyHistory, getBlockNumber());
if (curTotalSupply < _amount) throw;
updateValueAtNow(totalSupplyHistory, curTotalSupply - _amount);
var previousBalanceFrom = balanceOf(_owner);
if (previousBalanceFrom < _amount) throw;
updateValueAtNow(balances[_owner], previousBalanceFrom - _amount);
Transfer(_owner, 0, _amount);
return true;
}
He claims that "This enables the contract owner (AKA controller) to mint/issue/print quadrillion amount of SNT tokens out of thin air." and that "This allows the contract owner to delete/burn/destroy anyone's coin, including but not limited to the ones on the exchange and/or wallets. It could be yours or someone else's tokens that could be deleted."
Technically, this is true.
However, as is the case with most beginner developers, Binod failed to look beyond the basics. His research amounted to basic pattern searching of Github code and jumping to conclusions.
Far be it from me having to defend an established company, but I feel like it's important to point out where his errors lie so that it becomes easier to ignore him in future blackmail attempts by literally doing more research than he did (which amounts to 5 minutes of work, at most).
The Controller
The Controller pattern in smart contract development is used when you need an "administrator" to be able to run certain functions in a smart contract and no one else. By adding the onlyController
modifier to the above functions, it is ensured that only this admin can execute them.
In one of the comments (screenshot below), Binod says:
For everyone's attention, they may say that the controller can be changed to zero address or even will exactly do that in the future and then entirely dismiss me, but right now the owner (controller as they refer to it) is an Ethereum wallet which indeed has god-like powers.
Let's verify this claim.
First, let's open the contract's "Read Contract" tab. Under "controller", we find 0x52ae2b53c847327f95a5084a7c38c0adb12fd302
as an address.
So far so good, he's right that there is a controller and that it's not 0x0. Let's inspect that address.
The first thing we notice is that this is a contract, not an Etheruem wallet, so here's the first instance of where he's wrong.
Further, upon inspecting the code, we notice that the contract "controlling" our previous SNT token is in fact the SNTPlaceHolder
contract from this address' code.
Let's inspect this contract. This contract does indeed have the ability to set a new controller via the changeController
method. But who can do this? onlyOwner
. Okay, let's check out who the owner of this SNTPlaceHolder
contract is by opening the Read Contract tab.
Hmm, the address 0xbbf0cc1c63f509d48a4674e270d26d80ccaf6022
. Let's take a look.
This address is also a contract, but what kind? Inspecting the Code
tab shows us that this is actually a multi-signature wallet controlled by the Status team.
In order to execute transactions from this wallet - which contains 2 billion SNT and has been dormant for over 400 days, 3 out of 5 signatures are needed. Which 5?
- 0x6b9ef02657339310e28a7a9d4b5f25f7c1f68d61
- last active 475+ days ago
- 0x904ef6ff8e82478c5604d99884eb9bcd7f73cc36
- last active 400+ days ago
- 0x02e3f16ca21cf0508835b190933ecbde2f7f14df
- last active 115+ days ago when it received an input from Kraken. Looks like someone's HODL address, as it holds 470 Eth.
- 0x4838eab6f43841e0d233db4cea47bd64f614f0c5
- actively used by someone who is likely a cofounder of Aragon, according to the original whitepaper
- 0xa646e29877d52b9e2de457eca09c724ff16d0a2b
- another multisig, this one with 513 million SNT and 238000 ether. One of its owners is the multisig we've been looking at, which means these two wallets loop permissions. The other two owners of this specific multisig seem to be two private addresses used only for voting - given that it needs 2/3 signatures to complete, the two are enough. This multisig seems to be what the Status team is using to pay out bounties, salaries, and sponsorships.
So, what does all this mean?
Here's a summary.
- the SNT token's controller can mint and burn at will, from and to any account, yes
- the SNT token's controller is currently a smart contract which is owned by a multi-signature wallet
- in order for the mint and burn functionality to be abused, the controller would first need to be changed to another address, which can only be done by the multi-signature owner of the controlling smart contract
- in order to execute this change, 3 of 5 of the multi-signature wallet's owners would have to agree to do this.
- one of them is another multi-signature wallet which needs 2 signatures, so this brings the total needed to 5 out of 7. 2 of the owners are dormant and empty and whether they still have their private keys is questionable. The others look like core team members.
- After changing the controller address to another one, only then can the controller start executing these mint and burn commands. If the new controller is a multi-sig wallet, it again needs approval of multiple parties.
In order to abuse the contract and steal or create tokens at will - which would crash the token's value and render the attack purely destructive - almost all the key holders would have to agree to irreparably destroy their own project.
Granted, this is still more permission than a team should have over its own distributed token, but Binod's claim that this is a shitcoin and a scam is unfounded, uninformed, and unfair considering everything Status have done so far and are doing still. Status is the definition of a proven long-term project and non-scam in the industry, and attempting to get their attention this way is beyond reprehensible.
Conclusion
In a desperate attempt to get attention, Binod accused a high quality project of something they did not do without doing the basic research required to disprove his own claims. He further attempted to notify none other than Changpeng Zhao, the CEO of Binance who I had the pleasure of meeting at Delta Summit, which is spectacularly disrespectful as it assumes that CZ wouldn't have his team do this basic audit first thing before listing the token.
This is not how auditing works.
When we at Audithor audit contracts, we do in-depth research and provide tests and exploit cases along with personal opinions, rather than attack the developer and assume guilt. We contact the team privately, discuss the issue, and then issue a public report if the issue goes on ignored for a given period of time. This is how all security auditing works.
Binod Nirvan has up until recently been on our list of auditing associates due to him having accurately called out some scams. We now believe those were a coincidence, but as we had not had the chance to have an auditing engagement together yet, his work was not easy to evaluate. However, having seen his lack of competency firsthand, we have removed him from our list.