How $800k Evaporated from the PoWH Coin Ponzi Scheme Overnight

Eric Banisadr
4 min readFeb 1, 2018

--

Three days ago, 4chan’s /biz/ banded together and created a cryptocurrency Ponzi scheme: Proof of Weak Hands Coin. They advertised it as a legitimate pyramid scheme, and surprisingly its value quickly grew to over a million dollars, and over a thousand Ethereum. A few hours ago, 866 Ethereum vanished from the contract, however, due to a flaw in the code.

The code is mostly taken from Jochen Hoenicke’s original concept for a Ponzi token. It also barely conforms to the ERC-20 standard for Ethereum-based tokens, something which will eventually be exploited. (The specification is a short one too, containing six methods and two logged events.) Functions like withdraw, which are mandated by the standard, ignore their parameters, and others don’t always work in intended ways.

Here’s what went wrong:

A vulnerability was found in the part of PoWH’s implementation of ERC-20 that allows a person to “approve” another user to transfer tokens on their behalf. A malicious user could enable a second account to sell coins from the first account. However, the sold coins from the first account would be taken off the second account’s balance. The resulting (unsigned) integer underflow would leave the second account with an extremely large balance of PoWH Coins.

The flawed transfer functions

function transferFrom(address _from, address _to, uint256 _value)  public {
var _allowance = allowance[_from][msg.sender];
if (_allowance < _value)
revert();
allowance[_from][msg.sender] = _allowance - _value;
transferTokens(_from, _to, _value);
}

The first function doesn’t have any immediate issues; its first four lines just make sure the caller is authorized to spend someone else’s coins, then it calls transferTokens(address _from, address _to, uint256 _value) to do the heavy lifting.

function transferTokens(address _from, address _to, uint256 _value) internal {
if (balanceOfOld[_from] < _value)
revert();
if (_to == address(this)) {
sell(_value);
} else {
int256 payoutDiff = (int256) (earningsPerShare * _value);
balanceOfOld[_from] -= _value;
balanceOfOld[_to] += _value;
payouts[_from] -= payoutDiff;
payouts[_to] += payoutDiff;
}
Transfer(_from, _to, _value);
}

transferTokens has some unusual behavior, though. Specifically, if it’s called with a _to address belonging to the contract itself, it processes the transfer as a sale. Even so, this isn’t a problem yet.

function sell(uint256 amount) internal {
var numEthers = getEtherForTokens(amount);
// remove tokens
totalSupply -= amount;
balanceOfOld[msg.sender] -= amount;

// fix payouts and put the ethers in payout
var payoutDiff = (int256) (earningsPerShare * amount + (numEthers * PRECISION));
payouts[msg.sender] -= payoutDiff;
totalPayouts -= payoutDiff;
}

Here’s where things get hairy. At first glance you may not notice, but the _from argument is no longer being passed down the chain. Instead, the sell function assumes msg.sender to be the seller. Unfortunately, if sell is invoked by transferTokens it’s possible that msg.sender's balance is now being drained of coins that it doesn’t own. While that would usually make people upset, if an empty second account used to make this transfer, and only one PoWHCoin is sent, the second account’s balance will underflow to 1.1579E77 or 2^256 — 1.

Once in possession of an account with the maximum possible balance, it’s not too difficult to turn into Ethereum. (The actual attacker messed up once, though: they transferred their entire balance into the contract in an attempt to sell it. Unfortunately for them, that underflowed their balance right back around to maximum again.) On the second attempt, they turned only 100 of their 115 quattuorvigintillion PoWH into Ethereum, circumventing PoWH Coin’s all-or-nothing cashout system by transferring the smaller sum into the contract. Then, they issued one last request, to withdraw their 866 ETH.

Obviously, it’s a terrible idea to invest in a Ponzi scheme. It’s even worse to invest in an insecure one. The code behind this particular smart contract was laughably unreadable, yet many people expected it to be secure. It made ample use of magic numbers, was indented seemingly randomly and had functions named and written in 4chan’s characteristic style:

function getMeOutOfHere() public {
sellMyTokensDaddy();
withdraw(1); // parameter is ignored
}

PoWH Coin was a wild, three-day ride. To see its shill-site in all its glory, it looked like this. Also, here is a more organized list of the transactions used to compromise PoWH Coin. Interestingly enough, if you check the balance of the second account, 0xb9cd700b8a16069bf77ededc71c3284780422774, on Etherscan, it will still show the maximum possible amount of PoWH coins.

This same attack was repeated on many clones of PoWH Coin, and it represents another major Ethereum contract compromised by poor coding. In 282 lines of code, PoWH Coin managed to give away $800 thousand in Ethereum.

Connect with the Raven team on Telegram

--

--