Rating:

Crackme.sol

We were given the challenge text:

the fact that everything can be turned into a crackme is so cool

address: 0xDb2F21c03Efb692b65feE7c4B5D7614531DC45BE

author: notforsale

Along with the file chall.sol.

This time OSINTing the flag would be a bit harder, so I tried to go for a more intended solution. I assume we have to call the gib_flag function, which takes 3 arguments (uint arg1, string memory arg2, uint arg3).

By looking at the function we see that (arg1 ^ 0x70) == 20 has to be true. Due to how XOR works, we can simply XOR 0x70 with 20 to get arg1; 0x70 ^ 20 = 100. arg1 then has to be 100.

The next argument is a string, we see that decrypt(arg2) has to be the same as offshift ftw. The goal of the challenge is of course to revere the decrypt function (especially the assembly part), to find the input that equals offshift ftw. However, I didn't have time for this. Even though you only have to call the gib_flag function to get the output (since you don't write anything to the blockchain), I hoped that someone had made a transaction with the correct input data anyways, and I was correct! I clicked on a random transaction and decoded the input, ignoring the garbage data I saw the string evvixyvj vjm (which is ROT10 of offshift ftw). I assumed this was the correct data for arg2.

Finally I had arg3. The function first checks if arg3 is larger than 0, but later in the code we see that arg3 + 1 should be less than 1. This should of course be impossible, but as also hinted by the comment, this can be achieved by an overflow. Since the Solidity uint type is 256 bit, arg3 has to be the highest possible 256 bit unsigned number, which is 2<sup>256</sup>-1, or 115792089237316195423570985008687907853269984665640564039457584007913129639935.

Now yet another problem appeared. I had all then correct function arguments, but how would I call the function? I did some googling, and came across web3, A Python library for interacting with Ethereum. After a little documentation reading, I got working code that succesfully called the contract's function.

from web3 import Web3, HTTPProvider
from web3.middleware import geth_poa_middleware

w3 = Web3(Web3.HTTPProvider('https://rinkeby.infura.io/v3/API_KEY')) # Instead of running a local node to connect to the Rinkeby network, I used https://infura.io/

w3.middleware_onion.inject(geth_poa_middleware, layer=0) # Some stuff StackOverflow told me to add after I got errors

contract_address = '0xDb2F21c03Efb692b65feE7c4B5D7614531DC45BE' # The address of the contract

# I used http://remix.ethereum.org/ to generate the ABI for me from the source code, this allows web3 to know what kind of functions exist in the contract, what those function return, etc.
abi = '[{"inputs":[{"internalType":"uint256","name":"arg1","type":"uint256"},{"internalType":"string","name":"arg2","type":"string"},{"internalType":"uint256","name":"arg3","type":"uint256"}],"name":"gib_flag","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]'

contract = w3.eth.contract(contract_address, abi=abi)

print(contract.functions.gib_flag(100, 'evvixyvj vjm', (pow(2, 256) - 1)).call()) # I then called the function using my arguments, and printed the results

I hoped this would print the flag, but apparently I was doing something wrong. I'm not sure if it's a library error or user error, but the expected output was a uint[] of the flag, however all I got was 67. I tried changing the return type of gib_flag in the ABI for a while, but it just gave me errors. After a litte more thinking, I realized that the library probably received the raw data, and then attempted to parse this. In Visual Studio code I ctrl-clicked on .call(), and it took me to the function definition in the library. The call function returned the response of another function, call_contract_function. I went to the definition of this function as well, and I saw the variable return_data. I then simply added a print statement, and when I now ran my code it printed the raw response data.

b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00g\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00r\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x007\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x005\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00_\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00u\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00_\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00K\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00_\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00m\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x008\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x005\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x004'

I then removed all the nullbytes, return_data.decode().replace('\x00', ''), and the result was the flag: flag{C0ngr@75_Y0u_CR@CK3D_m3854}

Original writeup (https://github.com/WastefulNick/CTF-Writeups/tree/master/0x41414141/blockchain/crackme).