Skip to content

Latest commit

 

History

History
157 lines (120 loc) · 9.99 KB

File metadata and controls

157 lines (120 loc) · 9.99 KB

Useful tools for Solidity exploitation

Here's a list with the most common tools used for smart contract exploitation:

Remix IDE

Remix is one of the best IDEs for Solidity, it is available both as a web or desktop application. Remix IDE is part of the Remix Project which is a platform for development tools with plugins. It's a very powerful tool that helps writing smart contract code and comes with lots of important resources, like:

  • Solidity Compiler, in which you can compile the contract and have the option to select the Solidity language version and other compilation details;

  • Deploy & Run tab, that allows you to deploy the compiled contracts in a local environment (or via a Web3 Provider) and to interact with them. In particular:

    • The environment makes you choose between different Ethereum environments: The JavaScript VMs are local environment, the Injected Web3 requires a connection with an injected web3 provider like MetaMask, the Web3 Provider requires the connection to a remote node (this very used in CTFs).
    • Account contains the list of accounts associated with the current environment.
    • Gas Limit, maximum amount of gas allowed for the transaction.
    • Value amount of Ether (and submultiples) to send to a contract or a payable function.
    • Deploy, sends a transaction that deploys the selected contract (if a contract constructor needs parameters you have to specify them).
    • At Address, permits access a contract that has already been deployed.
    • Deployed contracts, list of deployed contracts you can interact with. Constant or pure functions in Solidity have a blue buttons; non-payable functions have an orange button; payable functions have red button
  • Debugger, that shows the status of the contract during the execution of a transaction, allowing to analyze the bytecode of the EVM line by line. It can be used on transaction created on Remix by clicking the Debug button on the IDE terminal. The debuggers panels are:

    • Function Stack, functions that are interacting in the transaction;
    • Solidity Locals, local variables inside a function;
    • Solidity State, contract's state variables;
    • Opcodes, code that defines the instruction the EVM is executing on the data;
    • Step details, list more info about the opcode step
    • Stack, the EVM Stack;
    • Memory, that consists of 3 columns: the location in memory, hex encoded value and the decoded value. If a part of memory is empty, question marks (?) will be shown. Memory is cleared for each new message call, it is linear and can be addressed at byte level;
    • Storage, contract persistent storage saved on the blockchain;
    • Call Stack, data array on which calculations are performed, that has maximum size of 1024;
    • Call Data, functions parameters;
    • Return Value, value that the function will return;
    • Full Storage Changes, persistent storage at the end of the function;
  • plugins, that can be activated from the plugin manager tab. With the Solidity Static Analysis plugin it's possible to perform static analysis on compiled Solidity smart contracts to find out security vulnerabilities. Analysis result will be shown as badge to the plugin icon with a number indicating the warnings count. There are 4 main categories for the analysis:

    • Miscellaneous
    • ERC
    • Gas & Economy
    • Security

    Here you can find out more about the plugins.

Web3.py is a Python library for interacting with Ethereum. Web3.py can be installed with pip as follows:

pip install web3

To use most of the code of the library it's needed a connection to an Ethereum Node or to a RPC provider. A provider is how web3 talks to the blockchain, it takes JSON-RPC, send to the Blockchain and return a response.

There are different ways to connect to a node:

  • IPC, that uses the local filesystem and it's the fastest and most secure;
    from web3 import Web3
    w3 = Web3(Web3.IPCProvider("yourPath/geth.ipc"))
  • Websockets, that works remotely and are faster than HTTP
    w3 = Web3(Web3.WebsocketProvider("ws://127.0.0.1:8546"))
  • HTTP, the most used;
    w3 = Web3(Web3.HTTPProvider("http://127.0.0.1:8545"))

After connecting to a node, to test the connection it's possible to ask it for the latest block:

blockNumber = w3.eth.block_number
print("Current block number: ", blockNumber)

Or simply to use the command w3.isConnected().

Creating a Transaction

To make you understand how to create a transaction with the Web3.py library let's interact with this contract:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

contract whoSendMe{
    address public lastInteraction;

    constructor(){
        lastInteraction = 0x0000000000000000000000000000000000000000;
    }

    function interact() public payable {
        require(msg.value == 1 gwei);

        lastInteraction = msg.sender;
    }

}

This simple contract shows the last address that interacted with the function interact(), that initially is the null contract (0x0000000000000000000000000000000000000000). To change it we can simply call the smart contract function interact() through a transaction, in which the value sent should be equal to 1 gwei as requested by the function with require(msg.value == 1 gwei);

This could be done with the Web3.py library with the following code:

from web3 import Web3
import json

url = 'YOUR_HTTP_ENDPOINT_URL'
privateKey = 'YOUR_PRIVATE_KEY'
nonce = 0 # You address nonce

abi = json.loads('[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"interact","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"lastInteraction","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]')
provider = Web3(Web3.HTTPProvider(url))

contractAddress = Web3.toChecksumAddress('CONTRACT_ADDRESS')
WhoMe = provider.eth.contract(address = contractAddress, abi=abi)

tx_hash = WhoMe.functions.interact().buildTransaction({
    'value': Web3.toWei(1, 'gwei'),
    'gas': 70000,
    'gasPrice': Web3.toWei(40, 'gwei'), 
    'nonce': nonce
})
signed_tx = provider.eth.account.sign_transaction(tx_hash, privateKey)
provider.eth.send_raw_transaction(signed_tx.rawTransaction)

print(WhoMe.functions.lastInteraction().call())

To interact with Ethereum Smart Contract it's needed to specify the ABI of the contract. The ABI (Application Binary Interface) is used to word with smart contract compiled as sequence of bytecode through high level elements (like a variable or function name). To work function defined in high-level languages (like Solidity) it's needed to translate names and arguments into byte representation and to interpreter the bytes sent in response it's needed to convert back the return values in data defined in higher-level languages. To perform this translation it's needed a documentation, the ABI. The ABI encoding consist of indicating the caller of a function to encode the needed information in a format that the EVM can understand and that permits to call that function in bytecode.

There is a library corresponding to Web3.py for Javascript, Web3.js. The library can be installed through the npm javascript package manager via the command: npm install web3. Some commands are pretty much similar between these two libraries, but I will focus more on Web3.py. To find more information about this library you can go here.

EVM Disassembling

A disassebler is a software to translate machine language into assembly language. A disassembler is a great reverse-engigneering tool, but it's impossible to perfectly covert a binary back into assembly: whenever an assembler needs to take a decision for example between two binary codes that do similar things, a disassembler isn't able to know which is the correct route to follow; a disassebler recreates a code very similar to the original.

A disassebler is very useful each time we want to see the code of a smart contract whose source code isn't public available.

It can also be used to verify if the source code of a public smart contract is the same of the developed smart contract: all we have to do is compile the Solidity code public available and compare the bytecode/assembly it produces to the optcode we retrieved from the disassebmler (or compare the instruction of the assembly code to the one of the solidity smart contract code).

An example of how it is easily possible to retrieve the assembly code of a smart contract is shown below: through the use of the web3.py library command provider.eth.get_code(contractAddress) we can retrieve the smart contract bytecode and with the pyevmasm library we can disassemble the smart contract bytecode.

from web3 import Web3
from pyevmasm import disassemble_all

infuriaURL = 'YOUR_RPC_ENDPOINT_PROVIDER'
provider = Web3(Web3.HTTPProvider(infuriaURL))

contractAddress = Web3.toChecksumAddress('CONTRACT_ADDRESS')

contractBytecode = provider.eth.get_code(contractAddress)

originalCode = disassemble_all(contractBytecode.hex())

for inst in originalCode:
    print(inst)

The code disassembly was done using pyevmasm a python library useful for assemble and disassemble EVM bytecode. It can be installed with pip install pyevmasm. A very useful command of the library is disassemble_all which, given a string representing the bytecode of a contract, returns human-readable instructions.

Similar tools can be found online too, like the Bytecode to Opcode Disassembler from Etherscan.io and many others; here there is a list.