#100DaysOfSolidity #075 Hiding Malicious Code within External Contracts: Unveiling Vulnerabilities and Countermeasures 👾

#100DaysOfSolidity Hacks & Tests 075 : “Hiding Malicious Code within External Contracts”

Solidity Academy
7 min readAug 20, 2023

In the exciting realm of smart contracts, the Ethereum blockchain has paved the way for decentralized applications (DApps) that execute autonomously and securely. However, like any technology, smart contracts are not immune to vulnerabilities and attacks. One insidious method employed by malicious actors is the art of concealing harmful code within external contracts, exploiting unsuspecting users and causing substantial financial losses. In this 75th installment of the #100DaysOfSolidity series, we delve into the world of “Hiding Malicious Code with External Contracts,” exploring the dangers it poses and equipping developers with the knowledge to defend against such threats. 🛡️

#100DaysOfSolidity Hacks & Tests 075 : “Hiding Malicious Code within External Contracts”

The Deceptive Veil: Concealing Malicious Intent

External contracts, also known as libraries, are reusable pieces of code deployed on the Ethereum network. They offer a way to modularize functionality, reduce code redundancy, and enhance efficiency. However, this flexibility also opens the door to potential abuse. Malicious actors may take advantage of external contracts to obfuscate harmful code within seemingly innocuous functions, escaping initial scrutiny. Once invoked by a target contract, this concealed malevolence can wreak havoc by stealing funds, manipulating data, or exploiting vulnerabilities in unexpected ways. 😈

Anatomy of an Attack: How It Works

1. Innocent Wrapper Contract: Malicious actors deploy an external contract with legitimate and useful functions. These functions act as a façade to disguise the hidden malicious intent.

2. Malicious Payload: Within the external contract, the attacker embeds a malicious payload, carefully obfuscated to avoid detection during initial reviews.

3. Deceptive Interaction: The innocent wrapper contract interacts with the malicious payload, often passing user inputs or contract states.

4. Unleashing Chaos: Upon invocation, the external contract’s hidden malicious payload executes, carrying out its intended nefarious actions, which could range from unauthorized fund transfers to tampering with critical data.

Real-Life Consequences: Notable Incidents

The DAO Hack 🚫

One of the most infamous examples of exploiting external contracts was the DAO (Decentralized Autonomous Organization) hack in 2016. Malicious actors exploited a vulnerability in an external contract to drain approximately one-third of the DAO’s funds, resulting in a contentious hard fork of the Ethereum blockchain to reverse the malicious transactions.

Parity Multi-Sig Wallet Vulnerability 🔐

In 2017, a vulnerability in a Parity multi-signature wallet’s external contract allowed an attacker to make themselves the owner of the contract and subsequently freeze over 500,000 ether, collectively worth millions of dollars at the time.

Detecting the Hidden Threat: Testing Methodologies

To defend against the lurking danger of hidden malicious code within external contracts, developers must adopt rigorous testing methodologies that leave no stone unturned. Employing a combination of techniques helps to identify vulnerabilities before they can be exploited.

1. Automated Static Analysis Tools 🛠️

Using automated static analysis tools like MythX and Slither, developers can scan their smart contracts for potential vulnerabilities. These tools analyze the code without executing it, identifying suspicious patterns, potential reentrancy issues, and other vulnerabilities that may be lurking within external contracts.

2. Code Review and Peer Audits 👁️‍🗨️

Thoroughly reviewing the code and conducting peer audits can reveal hidden malicious intent. Enlisting the expertise of fellow developers can uncover subtle obfuscation techniques and logic that may lead to vulnerabilities.

3. Dynamic Analysis with Test Suites 🧪

Creating comprehensive test suites that thoroughly exercise contract functionality can help reveal unexpected behavior or unauthorized actions. By simulating various scenarios, developers can expose any hidden actions that external contracts may perform when invoked.

4. Code Verification ✅

Utilizing formal verification tools, developers can mathematically prove the correctness of their contracts. This method helps ensure that the code adheres to its intended behavior and does not contain any unexpected or malicious actions.

Fortifying the Defenses: Best Practices

To fortify smart contracts against the menace of hidden malicious code within external contracts, developers should adhere to a set of best practices that emphasize security and robustness.

1. Minimize External Calls ☎️

Reduce reliance on external contracts to mitigate the risk of hidden vulnerabilities. Opt for inlining code or deploying essential functions directly within your contract to reduce attack surface.

2. Verify External Contracts ✔️

Thoroughly audit and verify the external contracts you intend to use. Use reputable libraries that have undergone security assessments and peer reviews.

3. Limit External Contract Permissions 🔒

Restrict the permissions granted to external contracts. Implement mechanisms like the “pull” payment pattern to avoid reentrancy attacks and unauthorized fund withdrawals.

4. Avoid Encoding Sensitive Data 🙈

Never encode sensitive data within the contract bytecode, as it can be reverse-engineered and exploited by attackers. Utilize secure off-chain storage solutions for such information.

5. Regularly Update Dependencies 🔄

Stay up-to-date with the latest security patches and updates for external contracts and libraries. Outdated dependencies can expose your contract to known vulnerabilities.

Analysis Report: Vulnerability and Preventative Techniques

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

/*
Let's say Alice can see the code of Foo and Bar but not Mal.
It is obvious to Alice that Foo.callBar() executes the code inside Bar.log().
However Eve deploys Foo with the address of Mal, so that calling Foo.callBar()
will actually execute the code at Mal.
*/

/*
1. Eve deploys Mal
2. Eve deploys Foo with the address of Mal
3. Alice calls Foo.callBar() after reading the code and judging that it is
safe to call.
4. Although Alice expected Bar.log() to be execute, Mal.log() was executed.
*/

contract Foo {
Bar bar;

constructor(address _bar) {
bar = Bar(_bar);
}

function callBar() public {
bar.log();
}
}

contract Bar {
event Log(string message);

function log() public {
emit Log("Bar was called");
}
}

// This code is hidden in a separate file
contract Mal {
event Log(string message);

// function () external {
// emit Log("Mal was called");
// }

// Actually we can execute the same exploit even if this function does
// not exist by using the fallback
function log() public {
emit Log("Mal was called");
}
}

Vulnerability

The provided Solidity code snippet illustrates a vulnerability that arises from the ability to cast any address into a specific contract type, even if the actual contract at that address is not of the type being casted. This vulnerability can be exploited to hide malicious code within external contracts, potentially leading to unexpected and harmful behavior.

In the given example, three contracts are introduced: `Foo`, `Bar`, and `Mal`. The `Foo` contract interacts with the `Bar` contract under the assumption that the function `callBar()` will execute the code inside `Bar`’s `log()` function. However, an attacker (Eve) can deploy the `Mal` contract and provide its address to `Foo`, effectively causing `callBar()` to execute the code within `Mal` instead of `Bar`.

Exploitation Steps

1. Eve Deploys Mal: The attacker, Eve, deploys the `Mal` contract with its own malicious logic.

2. Eve Deploys Foo with Mal’s Address: Eve deploys the `Foo` contract, passing the address of the deployed `Mal` contract as an argument to `Foo`’s constructor.

3. Alice’s Call to Foo.callBar(): Alice, a legitimate user, interacts with `Foo` and calls the function `callBar()` after reviewing the code. Alice believes that the code inside `Bar`’s `log()` function will be executed.

4. Malicious Execution: Despite Alice’s expectation, the code within `Mal`’s `log()` function is executed, leading to unintended and potentially harmful consequences.

Preventative Techniques

Bar public bar;

constructor() public {
bar = new Bar();
}

To mitigate the risks associated with this vulnerability, developers can implement various preventative techniques. Here are two recommended approaches:

1. Initialize a New Contract Inside the Constructor: One way to avoid the vulnerability is by initializing a new instance of the `Bar` contract within the constructor of the `Foo` contract. This ensures that the contract being used is the intended one, and not one provided by an external source. Here’s an example:

contract Foo {
Bar public bar;
constructor() {
bar = new Bar();
}
function callBar() public {
bar.log();
}
}

This approach guarantees that the `Bar` contract used within `Foo` is the correct one and not subject to substitution.

2. Make the Address of External Contract Public: Another way to enhance transparency and prevent substitution is by making the address of the external contract (`Bar` or `Mal`) public. By exposing the address, users and auditors can review the code of the external contract and ensure its legitimacy before interacting with the main contract. Here’s an example:

contract Foo {
address public barAddress;
constructor(address _bar) {
barAddress = _bar;
}
function callBar() public {
Bar(barAddress).log();
}
}

By allowing external visibility to the address, potential users can independently verify that the correct contract is being used.

In Conclusion; The vulnerability demonstrated in the provided code snippet highlights the importance of thorough security practices when dealing with external contracts in Solidity. Developers must be cautious about the potential for malicious code to be hidden within external contracts and adopt preventive techniques to mitigate such risks. By initializing contracts within constructors and ensuring transparency through public addresses, the Ethereum ecosystem can be made more resilient against attacks that exploit the casting vulnerability.

Conclusion

The art of hiding malicious code within external contracts is a sinister technique that exploits the trust and autonomy of smart contracts. As developers, it is our responsibility to stay vigilant and adopt stringent security measures to safeguard the decentralized ecosystem from malicious actors. By embracing robust testing methodologies, adhering to best practices, and continuously honing our skills, we can fortify the resilience of smart contracts and contribute to the growth of a secure and flourishing blockchain landscape. 🌐🔐

📚 Resources 📚

--

--

Solidity Academy

Your go-to resource for mastering Solidity programming. Learn smart contract development and blockchain integration in depth. https://heylink.me/solidity/