¿Puedes ver alguna vulnerabilidad en este contrato?

Es posible que este contrato tenga algún problema porque veo algo extraño en mi registro de contratos. Alguien ejecuta otra funcion de contrato para jugar con esta mini app de gamble y en 2 jugadas limpia todo el dinero xD.

Gracias por adelantado :)

pragma solidity ^0.4.11;
contract MetaCoin {

  event FlipCoinEvent(
    uint value,
    address owner
  );

    event PlaySlotEvent(
      uint value,
      address owner
    );

  function() public payable {}

  function flipCoin() public payable {
    assert(msg.value < 100000000000000000);
    uint value = (block.timestamp + uint(block.blockhash(block.number-1)))%100 + 1;
    if (value > 55){
      msg.sender.transfer(msg.value * 2);
    }
    FlipCoinEvent(value, msg.sender);
  }

function playSlot() public payable {
    require(msg.value < 100000000000000000);
    uint r = (block.timestamp + uint(block.blockhash(block.number-1)))%100 + 1;
       if(r >0 && r<3){
             PlaySlotEvent(3,msg.sender);
             msg.sender.transfer(msg.value * 12);
       }else if(r >3 && r<6){
             PlaySlotEvent(2,msg.sender);
             msg.sender.transfer(msg.value * 6);
       }else if(r >6 && r<9){
             PlaySlotEvent(1,msg.sender);
             msg.sender.transfer(msg.value * 3);
       }else{
            PlaySlotEvent(0,msg.sender);
       }

  }

  function getBalance() public constant returns(uint bal) {
    bal = this.balance;
    return bal;
  }

}

Respuestas (4)

Según la documentaciónblock.timestamp , , the block.blockhashy the block.numberpermanecen igual hasta que se agrega el siguiente bloque a la cadena de bloques. Dado que el tiempo de bloqueo es de alrededor de 15 a 17 segundos , es posible "obtener el bloque correcto" para atacar su contrato inteligente.

Uno podría simplemente escribir un contrato inteligente con una función getChances()que verifique el valor actual (block.timestamp + uint(block.blockhash(block.number-1)))%100 + 1y si está en el rango correcto (por ejemplo, <3) llama a la playSlot()función de su contrato.

Entonces , la función getChances()solo necesita ser invocada por un oráculo cada vez que se agrega un nuevo bloque a la cadena de bloques.

sin magia

Espero eso ayude

EDITAR :

Condensé su contrato inteligente y escribí otro para mostrar mejor lo que quise decir anteriormente:

pragma solidity ^0.4.11;

contract MetaCoin {
    event PlaySlotEvent(
        uint value,
        address owner
    );

    function playSlot() public returns (uint){
        uint r = (block.timestamp + uint(block.blockhash(block.number-1)))%100 + 1;
        PlaySlotEvent(r,msg.sender);
    }
}

contract Test {

    function getCurrentR() constant returns (uint) {
        return (block.timestamp + uint(block.blockhash(block.number-1)))%100 + 1;
    }
}

Si intenta esto en Remix , simplemente llame getCurrentR()e inmediatamente después de esa llamada playSlot(). Verá que el valor difiere solo en la cantidad de segundos que espera antes de llamar playSlot(). Por lo tanto, un jugador puede comprobar rantes de empezar a jugar...

Pero el uint(block.blockhash(block.number-1)) es imposible de predecir, ¿verdad?
Nunca he usado estos comandos en mis proyectos. Pero, según tengo entendido, la definición con block.numberla que obtienes el número del bloque más reciente. También lo block.number-1es el número del bloque añadido antes del actual. Por lo tanto, con block.blockhash(block.number-1)usted obtiene algo basado en información del pasado . ¿Me estoy perdiendo de algo?
si leo block.blockhash (block.number) me da cero cada vez, porque el bloque actual no se extrae, ¿verdad?
Bueno. Lo acabo de probar y en este punto tienes razón. Así block.number-1que tiene sentido. Perdon por la confusion. Pero a pesar de que está calculando un número basado en datos ya disponibles, ¿verdad?
Sí, pero en el momento en que ejecuta la transacción, ¿el hash principal ya está adelantado o no?
Honestamente, no estoy seguro. Pero creo que estos cálculos se realizan en el EVM y, por lo tanto, antes de que se extraiga la transacción.
Sí, también creo un contrato para atacar el espacio de juego y puedo ejecutar la función para ver si el resultado es ganador e inmediatamente después ejecutar el espacio de juego. Y todas las transacciones ocurren en el mismo bloque con el mismo tiempo y con el mismo resultado. Carece de sha3 y valor inicial y no de contrato público y me parece que es seguro porque nadie puede ver el valor inicial. ¿usted está de acuerdo?

Su lotería no es verdaderamente aleatoria.

Cualquier decisión que tome un usuario que afecte el resultado le da a ese usuario una ventaja injusta. Ejemplos incluyen:

  1. Usando un blockhash, marca de tiempo u otro valor definido por el minero. Tenga en cuenta que el minero tiene la opción de publicar un bloque o no, por lo que posiblemente podría tener una oportunidad de obtener el premio por bloque que extrae.

Consulte esta respuesta para obtener una explicación más completa: ¿Cómo puedo generar de forma segura un número aleatorio en mi contrato inteligente? .

afirmar(mensaje.valor < 100000000000000000); require(mensaje.valor < 100000000000000000);

Esto significa que también puedo jugar con 0 wei y aun así ganar, ¿correcto?

Tal vez quisiste decir >?

si pero tu ganas 0 * 2 = 0 verdad?
mmm tienes razon

Para generar el número aleatorio, utiliza la marca de tiempo del bloque actual y el hash del bloque anterior. El hash del bloque anterior es conocido, por lo que no agrega aleatoriedad. El minero conoce la marca de tiempo del bloque actual, por lo que puede llamar a flipCoin siempre que la marca de tiempo sea favorable para ellos.

Tenga en cuenta que no puede obtener el hash de bloque actual en Solidity. Como se especifica en los documentos:

block.blockhash(uint blockNumber) devuelve (bytes32): hash del bloque dado: solo funciona para los 256 bloques más recientes, excluyendo el actual

En su lugar, debes escupir el juego en 2 etapas:

  • en flipCoinregistras el block.numbery msg.valuepara el remitente.
  • en withdrawRewardusted transfiere la recompensa si el hash de bloque para el número de bloque registrado lo convierte en el ganador. Aquí también debe validar que el número de bloque grabado sea menor que el número de bloque actual.

Está bien usar blockhash en este caso ya que la recompensa de 0.1 ether es mucho más baja que la recompensa del bloque. Para generar el número aleatorio, también debe incluir la dirección del remitente en la semilla, de lo contrario, un atacante puede enviar múltiples solicitudes de flipCoin que totalizarán más que la recompensa del bloque, lo que dará un incentivo para manipular el hash del bloque y dará la posibilidad de hacer trampa.