Sehen Sie eine Schwachstelle in diesem Vertrag?

Es ist möglich, dass dieser Vertrag ein Problem hat, weil ich etwas Seltsames in meinem Vertragsprotokoll sehe. Jemand führt eine andere Vertragsfunktion aus, um mit dieser Mini-Glücksspiel-App zu spielen, und in 2 Spielen räumt er das ganze Geld ab xD.

Danke im Voraus :)

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;
  }

}

Antworten (4)

Laut Dokumentation bleiben die block.timestamp, die block.blockhashund die block.numbergleich, bis der nächste Block zur Blockchain hinzugefügt wird. Da die Blockzeit etwa 15 bis 17 Sekunden beträgt, ist es leicht möglich, "den richtigen Block zu bekommen", um Ihren Smart Contract anzugreifen.

Man könnte einfach einen Smart Contract mit einer Funktion schreiben getChances(), die den aktuellen Wert von prüft (block.timestamp + uint(block.blockhash(block.number-1)))%100 + 1und, wenn er im richtigen Bereich liegt (z. B. < 3), die playSlot()Funktion Ihres Vertrags aufruft.

Die Funktion getChances()muss dann nur jedes Mal von einem Orakel aufgerufen werden, wenn ein neuer Block zur Blockchain hinzugefügt wird.

Keine Magie.

Ich hoffe es hilft

BEARBEITEN :

Ich habe Ihren Smart Contract komprimiert und einen weiteren geschrieben, um besser zu zeigen, was ich oben gemeint habe:

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;
    }
}

Wenn Sie dies in Remix versuchen, rufen Sie einfach an getCurrentR()und rufen Sie sofort an playSlot(). Sie werden sehen, dass sich der Wert nur in Sekunden unterscheidet, die Sie warten, bevor Sie aufrufen playSlot(). Daher kann ein Spieler überprüfen, rbevor er mit dem Spielen beginnt...

Aber die uint(block.blockhash(block.number-1)) ist unmöglich vorherzusagen, richtig?
Ich habe diese Befehle nie in meinen Projekten verwendet. Aber soweit ich die Definition verstehe block.numberbekommt man mit die Nummer des neusten Blocks. So block.number-1wird die Nummer des Blocks vor dem aktuellen hinzugefügt. Daher block.blockhash(block.number-1)erhalten Sie mit etwas, das auf Informationen der Vergangenheit basiert . Übersehe ich etwas?
Wenn ich block.blockhash(block.number) lese, bekomme ich jedes Mal Null, weil der aktuelle Block nicht richtig abgebaut ist?
Okay. Ich habe es gerade ausprobiert und an dieser Stelle hast du Recht. Macht also block.number-1Sinn. Entschuldigen Sie das Durcheinander. Aber obwohl Sie eine Zahl auf der Grundlage bereits verfügbarer Daten berechnen, richtig?
Ja, aber wenn Sie die Transaktion ausführen, ist der übergeordnete Hash bereits voraus oder nicht?
Ehrlich gesagt bin ich mir nicht sicher. Aber ich denke, dass diese Berechnungen im EVM durchgeführt werden und daher bevor die Transaktion tatsächlich abgebaut wird.
Ja, ich erstelle auch einen Vertrag, um den Spielautomaten anzugreifen, und ich kann die Funktion ausführen, um zu sehen, ob das Ergebnis ein Gewinner ist, und gleich danach den Spielautomaten ausführen. Und alle Transaktionen finden im selben Block zur selben Zeit und mit demselben Ergebnis statt. Es fehlt sha3 und Startwert und kein öffentlicher Auftrag und scheint mir sicher zu sein, weil niemand den Startwert sehen kann. du stimmst zu?

Ihre Lotterie ist nicht wirklich zufällig.

Jede Entscheidung, die ein Benutzer trifft, die sich auf das Ergebnis auswirkt, verschafft diesem Benutzer einen unfairen Vorteil. Beispiele beinhalten:

  1. Verwenden eines Blockhashs, Zeitstempels oder eines anderen vom Miner definierten Werts. Denken Sie daran, dass der Miner die Wahl hat, ob er einen Block veröffentlicht oder nicht, so dass er möglicherweise eine Chance auf den Preis pro Block hat, den er abbaut.

Siehe diese Antwort für eine ausführlichere Erklärung: Wie kann ich sicher eine Zufallszahl in meinem Smart Contract generieren? .

behaupten (msg.value <100000000000000000); require(msg.value < 100000000000000000);

Das heißt, ich kann auch mit 0 wei spielen und trotzdem gewinnen, richtig?

Vielleicht meintest du >?

ja aber du gewinnst 0 * 2 = 0 richtig?
hm du hast recht

Zur Generierung der Zufallszahl verwenden Sie den aktuellen Blockzeitstempel und den vorherigen Blockhash. Der vorherige Block-Hash ist bekannt, sodass keine Zufälligkeit hinzugefügt wird. Der aktuelle Blockzeitstempel ist dem Miner bekannt, sodass er flipCoin anrufen kann, wann immer der Zeitstempel für ihn günstig ist.

Beachten Sie, dass Sie den aktuellen Block-Hash in Solidity nicht abrufen können. Wie in den Dokumenten angegeben:

block.blockhash(uint blockNumber) gibt zurück (bytes32): Hash des angegebenen Blocks – funktioniert nur für die 256 neuesten Blöcke , ausgenommen den aktuellen

Stattdessen sollten Sie das Spiel in 2 Phasen aufteilen:

  • in flipCoinnotieren Sie das block.numberund msg.valuefür den Absender.
  • in withdrawRewardSie die Belohnung übertragen, wenn der Block-Hash für die aufgezeichnete Blocknummer ihn zum Gewinner macht. Hier müssen Sie auch bestätigen, dass die aufgezeichnete Blocknummer kleiner als die aktuelle Blocknummer ist.

Es ist in diesem Fall in Ordnung, Blockhash zu verwenden, da die 0,1-Ether-Belohnung viel niedriger ist als die Block-Belohnung. Um die Zufallszahl zu generieren, sollten Sie auch die Adresse des Absenders in den Seed aufnehmen, da sonst ein Angreifer mehrere flipCoin-Anfragen senden kann, die insgesamt mehr als die Blockbelohnung ergeben, was einen Anreiz zur Manipulation des Blockhashs und die Möglichkeit zum Schummeln bietet.