Platform: Code4rena
Start Date: 18/10/2022
Pot Size: $75,000 USDC
Total HM: 27
Participants: 144
Period: 7 days
Judge: gzeon
Total Solo HM: 13
Id: 170
League: ETH
Rank: 136/144
Findings: 1
Award: $0.00
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Rolezn
Also found by: 0x1f8b, 0x52, 0x5rings, 0xNazgul, 0xSmartContract, 0xZaharina, 0xhunter, 0xzh, 8olidity, Amithuddar, Aymen0909, B2, Bnke0x0, Chom, Deivitto, Diana, Diraco, Dravee, Franfran, JC, Jeiwan, Josiah, JrNet, Jujic, KingNFT, KoKo, Lambda, Margaret, Migue, Ocean_Sky, PaludoX0, Picodes, Rahoz, RaoulSchaffranek, RaymondFam, RedOneN, ReyAdmirado, Shinchan, Tagir2003, Trust, Waze, Yiko, __141345__, a12jmx, adriro, ajtra, arcoun, aysha, ballx, bin2chen, bobirichman, brgltd, bulej93, catchup, catwhiskeys, caventa, cccz, cdahlheimer, ch0bu, chaduke, chrisdior4, cloudjunky, cryptostellar5, cryptphi, csanuragjain, cylzxje, d3e4, delfin454000, djxploit, durianSausage, erictee, fatherOfBlocks, francoHacker, gianganhnguyen, gogo, hansfriese, i_got_hacked, ignacio, imare, karanctf, kv, leosathya, louhk, lukris02, lyncurion, m_Rassska, malinariy, martin, mcwildy, mics, minhtrng, nicobevi, oyc_109, pashov, peanuts, pedr02b2, peiw, rbserver, ret2basic, rotcivegaf, rvierdiiev, ryshaw, sakman, sakshamguruji, saneryee, securerodd, seyni, sikorico, svskaushik, teawaterwire, tnevler, w0Lfrum
0 USDC - $0.00
HolographFactory.deployHolographableContract()
: There's no check that signer != address(0)
signer == address(0)
is a valid input and would pass the require statement if ecrecover()
fails, as it returns address(0)
when it fails:
File: HolographFactory.sol 192: function deployHolographableContract( 193: DeploymentConfig memory config, 194: Verification memory signature, 195: address signer //@audit can be address(0) 196: ) external { ... 206: bytes32 hash = keccak256( 207: abi.encodePacked( 208: config.contractType, 209: config.chainType, 210: config.salt, 211: keccak256(config.byteCode), 212: keccak256(config.initCode), 213: signer //@audit contains the signer which can be address(0) 214: ) 215: ); ... 220: require(_verifySigner(signature.r, signature.s, signature.v, hash, signer), "HOLOGRAPH: invalid signature"); //@audit-issue this is valid wih signer == address(0) and any signature as the hash shouldn't match anything ... 320: function _verifySigner( 321: bytes32 r, 322: bytes32 s, 323: uint8 v, 324: bytes32 hash, 325: address signer 326: ) private pure returns (bool) { ... 333: return (ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)), v, r, s) == signer || //@audit returns true with signer == address(0) 334: ecrecover(hash, v, r, s) == signer); 335: }
Consider adding a check that signer != address(0)
msg.sender
via calldata
The usual syntax is shr(96,calldataload(sub(calldatasize(),20)))
, meaning the last bytes of msg.data give the sender address.
However, in this project, the syntax is the following:
contracts/abstract/ERC20H.sol: 156 /** 157 * @dev The Holographer passes original msg.sender via calldata. This function extracts it. 158 */ 159: function msgSender() internal pure returns (address sender) { 160 assembly { 161 sender := calldataload(sub(calldatasize(), 0x20)) 162 } 163 }
contracts/abstract/ERC721H.sol: 156 /** 157 * @dev The Holographer passes original msg.sender via calldata. This function extracts it. 158 */ 159: function msgSender() internal pure returns (address sender) { 160 assembly { 161 sender := calldataload(sub(calldatasize(), 0x20)) 162 } 163 }
Unless it's certain that the only calldata will always only be the sender, consider using the full syntax.
Sources :
function _msgSender() internal view virtual override returns (address sender) { if (isTrustedForwarder(msg.sender)) { // The assembly code is more direct than the Solidity version using `abi.decode`. /// @solidity memory-safe-assembly assembly { sender := shr(96, calldataload(sub(calldatasize(), 20))) } } else { return super._msgSender(); } }
function _msgSender() internal view returns (address payable signer) { signer = msg.sender; if (msg.data.length>=20 && isTrustedForwarder(signer)) { assembly { signer := shr(96,calldataload(sub(calldatasize(),20))) } } }