Skip to content

v2.1.0

Compare
Choose a tag to compare
@kuzdogan kuzdogan released this 19 Apr 15:58
· 1549 commits to master since this release
ec2ac91

What's Changed

  • Added basic test coverage
  • Fix: Handling Etherscan contracts with multiple sources
  • Use the respective solc binary according to the platform, save binaries locally
  • Update Etherscan chains
    • Users can now import contracts from other Etherscan instances: Polygonscan, Snowtrace etc.
  • Make use of immutableReferences on verification
    • This lets us verify contracts with immutables very often without needing alternative methods such as looking at the creatorTxHash or "simulation"
  • Fix addLibraryAddresses potential vulnerability with regex.

Fixed vulnerability

Thanks to @Hellobloc and @samczsun for reporting the vulnerability.

addLibraryAddress RegExp vulnerability

Risk Description

Previously the following code was used to replace libraryAddresses.

export function addLibraryAddresses(
  template: string,
  real: string
): {
  replaced: string;
  libraryMap: StringMap;
} {
  const PLACEHOLDER_START = '__$';
  const PLACEHOLDER_LENGTH = 40;

  const libraryMap: StringMap = {};

  let index = template.indexOf(PLACEHOLDER_START);
  for (; index !== -1; index = template.indexOf(PLACEHOLDER_START)) {
    const placeholder = template.slice(index, index + PLACEHOLDER_LENGTH);
    const address = real.slice(index, index + PLACEHOLDER_LENGTH);
    libraryMap[placeholder] = address;
    const regexCompatiblePlaceholder = placeholder
      .replace('__$', '__\\\\$')
      .replace('$__', '\\\\$__');
    const regex = RegExp(regexCompatiblePlaceholder, 'g');
    template = template.replace(regex, address);
  }
  return {
    replaced: template,
    libraryMap,
  };
}

The code replaced the library placeholders in the bytecode by identifying __$ and constructed regular matching expressions from the placeholder.
But note that not all versions use the hash of the library name as a placeholder. For example, the 0.4 Solidity version uses __{Path:FileName}__ placeholder.

https://i.imgur.com/BTSFsmO.png

This allows a malicious user to manipulate regular expressions, which in turn allows other parts of the bytecode to be marked as a library address.

Example

As with the risks mentioned above, we can achieve arbitrary regular expression tampering by changing the file name of the library contract.
An example is the following:

pragma solidity ^0.4.0;
import "./$.{37}|2{40}|cantbematchedcharacters__";
contract A {
    address constant public a = address(0x2222222222222222222222222222222222222222);
    uint public b;
    function cc() public{
        b = L_.get4();
    }
}

In the above code, a regular expression is written in the filename and the expression appears in the generated bytecode and is adopted by the addLibraryAddresses function.

https://i.imgur.com/gpsm01V.png

This regular expression will first replace itself, and then look for 22...22 to do the same. Here 2{40} can also be modified to 5b.{...} to perform arbitrary replacements of executable bytecode.

A real attack case(0x4AD29c9716569f3c466BB123Efdd0B9B43207dE1 in goerli) was constructed.

https://i.imgur.com/vU5KhnD.png

https://i.imgur.com/gwNwRLR.png

The tampering of constants in the above case is possible. Of course executable bytecode tampering is also possible, simply by changing
2{40} to 5b.{...}, and writing L_ address to the bytecode we want.

DOS Attack

In addition to performing bytecode tampering, one can also perform a DOS attack on Sourcify. Specifically, once can design a regular expression that will not have any matches in bytecode, resulting in __$ not being replaced, which in turn makes addLibraryAddresses a dead loop.

Solution

We replaced the regex replacement of the pattern with a simple string match to __ in the bytecode, and replacing the placeholder with a fixed length (40 chars = 20 bytes).

PRs

New Contributors

Full Changelog: v2.0.0...v2.1.0