Hiding Malicious Code with External Contracts

Posted on

If you’re building smart contracts with Solidity, you’re likely aware of the importance of security. However, even with the best intentions, malicious code can still find its way into your codebase. One way attackers can hide malicious code is by using external contracts. In this article we’ll discuss how to protect your smart contracts from attacks or exploits introduced in this manner.

What is an External Contract?

An external contract is Solidity code which is used by another contract—the caller—or, ultimately, an account. An external contract can be called through an address and a function signature. External contracts can be utilized for storing shared data or executing calculations.

How External Contracts Can Hide Malicious Code

Attackers can hide malicious code by either misleading users—pretending the contract is something else and providing false information about it—by obscuring its code so that users are not aware of possible attacks, or by deploying a seemingly innocent contract and later updating it with malicious code.

The caller may not be aware of malicious code or changes to the contract, allowing the attacker to potentially compromise the calling contract or account.

How to Protect Your Smart Contracts

To protect yourself from malicious external contracts, you can implement several strategies:

  1. Use trusted external contracts: only use external contracts from trusted sources. If possible, reputable security experts should audit and vet external contracts to ensure their security.
  2. Utilize immutable external contracts: utilize external contracts that remain immutable once deployed. This means that they are not behind a Proxy implementation. By using immutable external contracts, it is ensured that the contract cannot be modified with malicious code at any time in the future.
  3. Monitor external contracts: keep an eye on the external contracts that your smart contracts use. If you notice any unexpected changes, investigate immediately.
  4. Limit external contract access: only provide access to external contracts that your smart contract needs to function. Limiting access reduces the potential attack surface for malicious code.

Sample Code

Here’s an example of how you can use an external contract in Solidity:

pragma solidity 0.8.4;

contract ExternalContract {
    uint256 public sharedData;

    function setData(uint256 _data) public {
        sharedData = _data;
    }
}

contract MyContract {
    ExternalContract externalContract;

    constructor(address _externalContract) {
        externalContract = ExternalContract(_externalContract);
    }

    function setData(uint256 _data) public {
        externalContract.setData(_data);
    }
}

In the code above, we define an external contract that has a public variable called sharedData. We then define MyContract, which can call an instance ExternalContract via an address passed to the constructor. MyContract has a function setData that calls the setData function in ExternalContract, allowing it to store data that can be shared between multiple contracts.

Vulnerability

In Solidity, it is possible to cast an address into any arbitrarily-typed contract variable, even if the contract housed at that address is not of the same type as the one being casted.

Malicious actors can exploit this to conceal harmful code. Let’s see how.

pragma solidity 0.8.4;

import "hardhat/console.sol";

contract MyContract {
    MyLibrary lib;

    constructor(address _lib) {
        lib = MyLibrary(_lib);
    }

    function callLib() public {
        lib.utility();
    }
}

contract MyLibrary {
    function utility() public {
        console.log("library utility has been called");
    }
}

contract MaliciousLibrary { 
    function utility() public {
        console.log("exploit has been carried out");
    }
}

In this case, a calling contract of the type MyContract expects an external contract of the type MyLibrary to be set in its constructor and called at a later point through a utility function.

See also  Mastering Multisig Wallets: A Comprehensive Guide

However, if a MaliciousLibrary contract instance was provided instead of a MyLibrary contract instance, an attack or exploit could take place. Why? Because it contains a function which mimics the signature of the regular library, and so the call can still be carried out regardless of the type declared for the lib variable.

The code executed for the utility function might be completely unexpected for the caller, depending on whether or not they are aware of the code that resides in the address provided in the constructor at the moment of contract deployment.

Preventative Techniques

Naturally, making the address of external contract public so that its code can be reviewed is a sensible idea. With this objective in mind, using tools like Etherscan (which provide smart contract code verification) can help a caller judge whether or not they should invoke an external function.

From the side of the caller contract, having a setter function can provide more flexibility than setting the address exclusively at the deployment time through the constructor. In this case, if a reference is found out to be malicious or house problematic code, it can be updated to a different address. However, keep in mind that:

  1. The function to set or change the address of an external contract should be a privileged action, so that only an owner or admin can carry it out, instead of any caller.
  2. An attack could be executed in this manner, too. A deployed contract can initially point to a benign external contract but then later updated to a malicious one. As a result, a modifiable external address can reduce the trust users have in the caller contract.

Finally, the code in an external contract can change if its implementation is behind a proxy. Proxies are widely used, and therefore do not necessarily imply that an implementation is malicious. If your contract calls a external code deployed through a proxy, you should be on the lookout for events such as Upgraded. Based on the emission of these events you can inspect whether or not the code you are calling is still safe to use, and disable the call (or change its referenced address) if necessary.

Conclusion

By using external contracts, you can improve the functionality and flexibility of your Solidity smart contracts. However, it’s important to be aware of the potential risks and take steps to protect your contracts from malicious code. By following the strategies outlined above and keeping an eye on the external contracts your smart contracts use, you can help ensure the security and reliability of your blockchain applications.

Posted in Blockchain, Smart Contract, Solidity, TechnologiesTagged , ,

Martin Liguori
linkedin logo
twitter logo
instagram logo
By Martin Liguori
I have been working on IT for more than 20 years. Engineer by profession graduated from the Catholic University of Uruguay, and I believe that teamwork is one of the most important factors in any project and/or organization. I consider having the knowledge both developing software and leading work teams and being able to achieve their autonomy. I consider myself a pro-active, dynamic and passionate person for generating disruptive technological solutions in order to improve people's quality of life. I have helped companies achieve much more revenue through the application of decentralized disruptive technologies, being a specialist in these technologies. If you want to know more details about my educational or professional journey, I invite you to review the rest of my profile or contact me at martin@infuy.com