✅Gold NFT
Idea
Contract bytecode:
Decompile it:
Here is the pseudocode I got:
function () public payable {
revert();
}
function 0x0daa5703(uint256 varg0, bool varg1) public payable {
require(4 + (msg.data.length - 4) - 4 >= 64);
require(varg0 == varg0);
require(varg1 == varg1);
require(msg.sender == address(0x3));
STORAGE[varg0] = varg1;
}
function read(bytes32 varg0) public payable {
require(4 + (msg.data.length - 4) - 4 >= 32);
require(varg0 == varg0);
return bool(STORAGE[varg0]);
}
// Note: The function selector is not present in the original solidity code.
// However, we display it for the sake of completeness.
function __function_selector__(bytes4 function_selector) public payable {
MEM[64] = 128;
require(!msg.value);
if (msg.data.length >= 4) {
if (0xdaa5703 == function_selector >> 224) {
0x0daa5703();
} else if (0x61da1439 == function_selector >> 224) {
read(bytes32);
}
}
();
}
The read()
function actually reads from a storage slot and that slot number comes from our input. In the end bool(STORAGE[varg0])
will cast the content in that storage slot to boolean, anything nonzero suffices. So our objective is to find out a non-zero storage slot.
The other function with selector 0x0daa5703
is a setter with access control: only address(3) can call it.
In etherscan we can find the "Contract Creation" tx 0x88fc0f1dd855405d092fc408c3311e7131477ec201f39344c4f002371c23f81c
. A lesser-known feature is the "State" tab:

It shows the storage slot 0x23ee4bc3b6ce4736bb2c0004c972ddcbe5c9795964cdd6351dadba79a295f5fe
was updated from 0 to 1 during creation:

This is just what we want, so the "password" is just 0x23ee4bc3b6ce4736bb2c0004c972ddcbe5c9795964cdd6351dadba79a295f5fe
.
After that, notice _safeMint()
has reentrancy problem, so the rest is easy.
PoC
Last updated