On 2019/04/11, 00:17 a.m., our data showed that the hacker’s account starting with TCX1Cay… created a TRC10 token named BTTx with token ID 1002278. Between 00:25 a.m. and 01:00 a.m., 40,000,000 BTTx tokens were transferred into multiple addresses which launched “fake BTT” attacks on BTTBank’s account starting with TXHFhq….

BTTBank

BTTBank, a.k.a. TronBank BTT, is an investment product of BTT token BitTorrent (BTT) - The token that will enable blockchain mass adoption which belongs to TronBank. As written on the official website:

TronBank BTT smart contract generates 3.6-6.6% dividends per day based on your investment and distribute your dividends to your balance. For example, if you invest in the 4.6% plan, then you will get over 100% of your first deposit in 21 days. The dividends generate every second and you can withdraw or reinvest your dividends each second. When you reinvest, the total investment would increase and you will get more dividends.

The UI of the product looks like this:

How does it work?

  1. The user purchases the investment product based on her choice of ROI and investment period;
  2. When the investment period is expired, the user withdraws the fund into her own wallet.

It’s pretty similar to P2P investment products. The only difference is that a new user might have difficulty setting up her TRON wallet. However, the ROI of this product is substantial.

Details

Background

There are multiple tokens in TRON:

  • TRX
  • TRC20
  • TRC10

TRX is the platform token of TRON which is similar to ETH on Ethereum. On the other hand, TRC20 is a token standard which is compatible to Ethereum ERC20. After creating the smart contract of a TRC20 token, the token transfers are done by sending transactions to the smart contract, which is difficult to normal users. Since ERC20/TRC20 is too complicated, TRON creates TRC10 — a type of token that can be used directly. Each TRON account can pay 1,024 TRC to create one (and only one) TRC10 token. Whenever a TRC10 token is created, the system allocates an unique ID, i.e., tokenId, an integer value starting from 1,000,001. Each tokenId maps to one unique token. By the time writing this blog, TRON has 1,850+ TRC10 tokens.

When TRON launches mainnet, only EOA accounts can transfer ERC10, which means contract accounts are not capable to transfer TRC10. To improve the liquidity and usability, TRON allows internal TRC10 transactions in smart contracts since Odyssey 3.2. An example of TRC10 transfer in smart contracts is shown in the following:

  1. transferTokenTest() is used to transfer TRC10 in an internal transaction. The user can transfer tokenValue of TRC10 token with the token ID tokenId to address by invoking address.transferToken(uint256 tokenValue, trcToken tokenId).
  2. msgTokenValueAndTokenIdTest() illustrates that an user can include tokenId and tokenvalue in msg, which means TRC10 is the VIP token on TRON platform compared to TRC20.
  3. getTokenBalanceTest() is used to get the balance of a TRC10 token with the token ID tokdId.

Therefore, the creator of a smart contract need to deal with TRC10 transfers carefully, especially, on the effectiveness and integrity of tokenId.

The Attack

PechShield researchers found a critical vulnerability while analyzing BTTBank smart contract which leads to financial damage. In the following paragraphs, the details are explained through the investment and withdrawal operations of BTTBank.

Invest

The public interface invest() retrieves msg.tokenvalue and passes it into the private interface _invest() to complete an investment operation. Inside _invest(), the amount, the investment period, and other information about the investment are processed and stored into the smart contract. One thing to mention here is that only the msg.tokenvalue is retrieved. None of the code retrieves msg.tokenid for checking if msg.tokenid is equal to the token ID of BTT token, i.e., 1002000.

Withdraw

The logic of withdrawal is illustrated in the following:

The process of withdrawal is simple. When an user invokes withdraw(), the amount of already expired investment funds would be calculated and returned to the user. Note that msg.sender.transferToken(withdrawalAmount, BTT_ID) uses a hardcoded value BTT_ID, i.e., 1002000.

This explains why a normal user invests BTT and get BTT back while a hacker invests BTTx but gets BTT back.

Mitigation

PechShield researchers would like to remind developers that TRC10 and TRC20 are totally different types of tokens on TRON platform. If you need to transfer TRC10 tokens inside a smart contract, make sure to check the token ID carefully. The invest() mentioned above could be enhanced as follows:

PechShield researchers also identified similar problems on other TRON smart contracts while analyzing BTTBank. We suggest the dev teams and exchanges to place importance on security for ensuring the safety of users’ digital assets. One thing worth to mention is that smart contract developers could leverage the code instead of reinventing the wheels. However, while leveraging the code, in some cases, the smart contracts may be vulnerable due to common zero-days in the templates or libraries. Before deploying the contract, make sure to consult with security firms and conduct for an audit.

About Us

PeckShield Inc. is a leading blockchain security company with the goal of elevating the security, privacy, and usability of current blockchain ecosystem. For any business or media inquiries (including the need for smart contract auditing), please contact us at telegram, twitter, or email.