Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSON injection and xss through ERC20 symbol when generating tokenUri #4

Closed
howlbot-integration bot opened this issue Jun 11, 2024 · 2 comments
Closed
Labels
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working primary issue Highest quality submission among a set of duplicates 🤖_primary AI based primary recommendation 🤖_06_group AI based duplicate group recommendation sufficient quality report This report is of sufficient quality unsatisfactory does not satisfy C4 submission criteria; not eligible for awards

Comments

@howlbot-integration
Copy link

Lines of code

https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/base/FactoryNFT.sol#L46-L47
https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/base/FactoryNFT.sol#L69-L112

Vulnerability details

Impact

The tokenURI function uses safeERC20Symbol function in panoptic math to get token symbols but doesn't sanitize it. This causes that any special charcacters can be introduced into the factory NFTs token uri leading to a host of JSON injection attack vectors. The use of SVG is well known to be often vulnerable to Cross-Site Scripting (XSS). If a malicious user can inject malicious JavaScript into an SVG file, any user viewing the SVG on a website may become susceptible to XSS attacks. Considering that anyone can create an ERC20 token, a uniswap pool and a panoptic pool or use the token symbol includes special characters, this can be weaponized by attackers to execute malicious codes on the frontend for instance, running a keylogger script to collect all inputs typed by a user including his password or to create a fake Metamask pop up asking a user to sign a malicious transaction to steal his funds in users. Even while the front end processes securely, such as using the standard builtin JSON.parse() to read URI. This can also be exploited by replacing factory nft 's svg with arbitrary other ones such as creating NFTs containing same art piece data with existing high price NFTs, or other legally risky svgs like gore or pornography images. More about this can be read here, here and here

Proof of Concept

When constructing metadata for the tokenUri, the metadata is constructed first getting the uniswap pools' token symbols. It does this using the safeERC20Symbol function.

    function tokenURI(uint256 tokenId) public view override returns (string memory) {
        address panopticPool = address(uint160(tokenId));

        return
            constructMetadata(
                panopticPool,
                PanopticMath.safeERC20Symbol(PanopticPool(panopticPool).univ3pool().token0()),
                PanopticMath.safeERC20Symbol(PanopticPool(panopticPool).univ3pool().token1()),
                PanopticPool(panopticPool).univ3pool().fee()
            );
    }

The safeERC20Symbol function simply queries the symbol without actually sanitizing the returned symbols. No check for symbol length, and no check for special characters.

    function safeERC20Symbol(address token) external view returns (string memory) {
        // not guaranteed that token supports metadata extension
        // so we need to let call fail and return placeholder if not
        try IERC20Metadata(token).symbol() returns (string memory symbol) {
            return symbol;
        } catch {
            return "???";
        }
    }

This is then used to construct the metadata and to generate the svg info. Notice how the characters are still not sanitized

function constructMetadata(
        address panopticPool,
        string memory symbol0,
        string memory symbol1,
        uint256 fee
    ) public view returns (string memory) {
        uint256 lastCharVal = uint160(panopticPool) & 0xF;
        uint256 rarity = PanopticMath.numberOfLeadingHexZeros(panopticPool);

        string memory svgOut = generateSVGArt(lastCharVal, rarity);

        svgOut = generateSVGInfo(svgOut, panopticPool, rarity, symbol0, symbol1);
        return
            string(
                abi.encodePacked(
                    "data:application/json;base64,",
                    Base64.encode(
                        bytes(
                            abi.encodePacked(
                                '{"name":"',
                                abi.encodePacked(
                                    LibString.toHexString(uint256(uint160(panopticPool)), 20),
                                    "-",
                                    string.concat(
                                        metadata[bytes32("strategies")][lastCharVal].dataStr(),
                                        "-",
                                        LibString.toString(rarity)
                                    )
                                ),
                                '", "description":"',
                                string.concat(
                                    "Panoptic Pool for the ",
                                    symbol0,
                                    "-",
                                    symbol1,
                                    "-",
                                    PanopticMath.uniswapFeeToString(uint24(fee)),
                                    " market"
                                ),
                                '", "attributes": [{',
                                '"trait_type": "Rarity", "value": "',
                                string.concat(
                                    LibString.toString(rarity),
                                    " - ",
                                    metadata[bytes32("rarities")][rarity].dataStr()
                                ),
                                '"}, {"trait_type": "Strategy", "value": "',
                                metadata[bytes32("strategies")][lastCharVal].dataStr(),
                                '"}, {"trait_type": "ChainId", "value": "',
                                getChainName(),
                                '"}]',
                                '", "image": "',
                                "data:image/svg+xml;base64,",
                                Base64.encode(bytes(svgOut)),
                                '"}'
                            )
                        )
                    )
                )
            );
    }

This is dangerous as it can be weaponized to insert extra arbitrary data altering the integrity of the JSON data.

Tools Used

Manual code review

Recommended Mitigation Steps

Sanitize input data according: https://github.com/OWASP/json-sanitizer

Assessed type

Invalid Validation

@howlbot-integration howlbot-integration bot added 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value 🤖_06_group AI based duplicate group recommendation 🤖_primary AI based primary recommendation bug Something isn't working primary issue Highest quality submission among a set of duplicates sufficient quality report This report is of sufficient quality labels Jun 11, 2024
howlbot-integration bot added a commit that referenced this issue Jun 11, 2024
@c4-judge
Copy link

Picodes marked the issue as unsatisfactory:
Out of scope

@c4-judge c4-judge added the unsatisfactory does not satisfy C4 submission criteria; not eligible for awards label Jun 11, 2024
@Picodes
Copy link

Picodes commented Jun 11, 2024

Out of scope with the scoping Q&A

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working primary issue Highest quality submission among a set of duplicates 🤖_primary AI based primary recommendation 🤖_06_group AI based duplicate group recommendation sufficient quality report This report is of sufficient quality unsatisfactory does not satisfy C4 submission criteria; not eligible for awards
Projects
None yet
Development

No branches or pull requests

2 participants