function burn(address _from, uint256 _unitAmount) onlyOwner public {
require(_unitAmount > 0 && balanceOf(_from) >= _unitAmount);
balances[_from] = SafeMath.sub(balances[_from], _unitAmount);
totalSupply = SafeMath.sub(totalSupply, _unitAmount);
Burn(_from, _unitAmount);
}
流程: 1、首先使用了onlyOwner来表明只有函数所有者才能调用 2、传入_unitAmount值大于0,并且传入一个任意的地址,当地址上的余额大于传入的值则可以正常执行 3、执行sub函数,将目标地址的余额进行减少 4、更新totalSupply 总铸币量 5、触发Burn事件 6、因此合约拥有者可以任意销毁任意用户的代币,而不用经过他人同意。
function burnFrom(uint256 _value, address victim) onlyOwner canMint public{
require(_value <= balances[victim]);
balances[victim] = balances[victim].sub(_value);
totalSupply_ = totalSupply().sub(_value);
emit Burn(victim, _value);
}
流程: 1、首先使用了onlyOwner来表明只有函数所有者才能调用 2、传入_value值和目标victim地址,要求传入的值必须比目标victim地址上拥有的代币少 3、同样的可以直接减少目标地址上拥有的代币 4、然后查看canMint修饰符,当require为true时则执行,!mintingFinished,因此minitingFinished需要为False。
modifier canMint() {
require(!mintingFinished);
_;
}
5、因此合约拥有者可以任意减少地址的代币数量
function burnTokens(address _from, uint _value) onlyIcoContract {
assert(_from != 0x0);
require(_value > 0);
balances[_from] = sub(balances[_from], _value);
}
modifier onlyIcoContract() {
require(msg.sender == icoContract);
_;
}
流程: 1、使用onlyIcoContract修饰符修饰方法,可以看到调用者必须为icoContract地址 2、因此我们需要查找icoContract地址,查看代码 里面存在两个burnTokens函数 第一个和之前的burnTokens函数一样 第二个如下,我们暂时先看第二个
function burnTokens(address _from, uint _value) onlyManager notMigrated {
cartaxiToken.burnTokens(_from, _value);
}
主要关注以下两个修饰符notMigrated和onlyManager
modifier notMigrated() {
require(currentState != State.Migrated);
_;
}
modifier onlyManager() {
require(msg.sender == icoManager);
_;
}
3、对notMigrated和onlyManager进行分析,当前状态不等于State.Migrated状态(迁移状态),调用者地址必须为icoManager
4、 当当前状态不等于State.Migrated状态(迁移状态),调用者地址为管理者时,可以调用cartaxiToken.burnTokens(_from, _value);函数,cartaxiToken就是之前的合约地址,此时执行第一个合约地址上的burnTokens函数。
5、可以查看交易明细
6、这个后门比较绕,可以简单总结下: icoManager地址可以调用icoContract合约(第二个合约)上的burnTokens函数,当icoContract.burnTokens函数满足状态部为State.Migrated状态(迁移状态)时,就会调用第一个合约的burnTokens,从而销毁任意用户的代币。传入内容:用户地址不为0,代币数量大于0。
function destroy(address _from, uint256 _amount) public {
_checkMyAging(_from);
require((msg.sender == _from && allowManuallyBurnTokens) || msg.sender == owner);
require(accountBalance(_from) >= _amount);
balances[_from] = safeSub(balances[_from], _amount);
_totalSupply = safeSub(_totalSupply, _amount);
Transfer(_from, this, _amount);
Destruction(_amount);
}
流程: 1、该方法是公开可访问的,接受地址和数量两个参数 2、使用_checkMyAging方法来检查传入的地址,询问GPT,说这个方法通过传入地址来判断这个地址上的代币,处理老化的代币。 3、需要满足条件:条件1:allowManuallyBurnTokens为true,并且合约的调用者为是否和_from(想销毁的地址)一致,或者条件2:合约调用者是owner,即合约调用者是不是合约的所有者。 4、这就导致条件1:自我销毁代币合情合理,但是条件2:却导致合约的owner可以操控任意地址用户的代币,例如:销毁地址用户的所有代币,导致任意地址用户的代币为他人所操控。
function destroyTokens(address _owner, uint _amount) onlyController returns (bool) {
uint curTotalSupply = totalSupply();
require(curTotalSupply >= _amount);
uint previousBalanceFrom = balanceOf(_owner);
require(previousBalanceFrom >= _amount);
updateValueAtNow(totalSupplyHistory, curTotalSupply - _amount);
updateValueAtNow(balances[_owner], previousBalanceFrom - _amount);
Transfer(_owner, 0, _amount);
return true;
}
modifier onlyController {
require(msg.sender == controller);
_;
}
流程: 1、使用修饰符onlyController修饰函数,传入地址_owner和_amount数量值 2、curTotalSupply获取总的铸币量,_amout值需要小于总的铸币量。需要销毁的地址的代币量要大于要销毁的量。 3、两个updateValueAtNow更新总的铸币量和对方地址上的代币数量,然后发送一个事件Transfer(_owner, 0, _amount); 这行代码通常用于通知监听者,从 _owner 地址销毁了 _amount 数量的代币,这里的0一般是接收者的地址,但是设置为0就代表为销毁——将金币转到不存在的地址上。 4、控制者就是 5、同样的这个后面也是可以销毁任意的代币
总结:上面的几个后门合约都是类似的,控制者可以任意销毁任何人的代币数量,对其他的代币持有者非常的不公平