ブロックチェーン上でそれがどのように機能するかを見たい人のための契約のアドレスはここにあります 。 そして、そのソースコードは次のとおりです。
pragma solidity ^0.4.19; contract NEW_YEARS_GIFT { string message; bool passHasBeenSet = false; address sender; bytes32 public hashPass; function () public payable {} function GetHash(bytes pass) public constant returns(bytes32) { return sha3(pass); } function SetPass(bytes32 hash) public payable { if ((!passHasBeenSet && (msg.value > 1 ether)) || hashPass == 0x0) { hashPass = hash; sender = msg.sender; } } function SetMessage(string _message) public { if (msg.sender == sender) { message = _message; } } function GetGift(bytes pass) external payable returns(string) { if (hashPass == sha3(pass)) { msg.sender.transfer(this.balance); return message; } } function Revoce() public payable { if (msg.sender == sender) { sender.transfer(this.balance); message = ""; } } function PassHasBeenSet(bytes32 hash) public { if (msg.sender == sender && hash == hashPass) { passHasBeenSet = true; } } }
契約の作者は、いわば、彼がお金でグリーティングカードを作りたかったことをほのめかしますが、彼は役に立たないプログラマーです。 この契約のアルゴリズムは次のとおりです。
- SetPassメソッドを使用して契約にお金をかけ、パスワードのSHA-3ハッシュを設定します。これは、受信者が(ロマンティックとして)利用できます。
- SetMessageメソッドを使用して受信者にメッセージを送信します
- Revoceメソッドを使用してギフトを拒否することもできます
- そして、受信者はGetGiftメソッドを使用してお金とメッセージを受け取ります
まあ、それは美しさではないですか? さらに、この図は3つのトランザクションによって補完されます。
それらの最初の2つは次のとおりです。
- 契約の公表
- 特定のハッシュを使用してSetPass関数を呼び出し、1エーテル分だけ契約残高を補充します。
1つの関数のみが呼び出されたことに注意してください。
3番目のトランザクションは、 GetGiftメソッドの呼び出しとランダムデータセットを使用して、契約をクラックする「失敗」の試みです。
そして今、トラップ自体:
SetPassメソッドのチェックを詳しく見てみましょう。
function SetPass(bytes32 hash) public payable { if ((!passHasBeenSet && (msg.value > 1 ether)) || hashPass == 0x0) { hashPass = hash; sender = msg.sender; } }
ご覧のとおり、これはpassHasBeenSet変数に基づいています。この変数は、同じ名前の個別のPassHasBeenSetメソッドを使用して設定する必要があります。 このメソッドは呼び出されていないため、変数はまだfalseです。 さらに、このメソッドは、最初にSetPassメソッドを呼び出した人のみが呼び出すことができます。
つまり、理論的には、複数のEtherの残高補充でSetPassメソッドを呼び出す人は誰でも送信者になります。 さらに、誰も1人にならないようにするには、すぐにPassHasBeenSetメソッドを呼び出すか、 Revoce / GetGiftメソッドの 1つを呼び出してお金を引き出すだけです。
そして、すべてが論理的なようです-これらの2つのメソッドを呼び出すだけで、1つのEtherがあなたのものです。 しかし、私たちが知っているように、イーサリアムには前走攻撃があります。 その意味は次のとおりです。攻撃者は保留中のトランザクションのプールを監視し、トランザクションを待ちます。 契約に関連付けられたトランザクションがトランザクションプールに表示されるとすぐに、攻撃者はより高いガス価格でトランザクションを実行します。 攻撃者のトランザクションは現在のラウンドの最後に来ましたが、ガス価格が最も高いため、実際にはトランザクションよりも早く実行されます。
攻撃者はどのような方法で実行すると思いますか? もちろんPassHasBeenSetです。
これにより、送信者の変更を回避する機会が彼に与えられ、さらに、 SetPassメソッドによって送信されたすべてのお金は安全に契約に決済されます。 それで、彼はそれらを表示するだけです。
ブロックチェーンを使用するときは注意してください!