【雑記】stratumにおけるshareの重複承認への対策

移行直後ハッシュの割にblockが見つけられていないユーザーが存在することが話題となりましたがこういうことだったとは・・・ 移行当初からハッシュレートの割にblock発見量が少ないユーザーが存在することから何か不正をしているのでは?と噂になっていましたが、このたびもなとれ運営のびりある氏の指摘もあり、採掘サーバであるstratumにshareの重複送信によるハッシュ水増しが可能な脆弱性があることが明らかとなりました。

 

 

今回の問題となっているものは、node.jsにより実装されているnode-stratum-pool(node-open-mining-portalがstratumとして利用しているソフト)とPythonにて実装されているstratum-miningで、実際には過去に同じnonce値のshareを送信済みであったとしても、条件を満たした場合に同じshareを重複してカウントし実際よりハッシュレートを水増しできるという脆弱性です。

なおこの脆弱性自体は、2015年の5月上旬にCrypto-Expert氏管理のstratum-mining側で修正が実施され、その後5月下旬にNOMPのgit projectでも指摘が行われていたようですが、こちらは実態のnode-stratum-poolにて修正が行われていないため現在も残っています。(ゆえにstratum-miningで該当のプロジェクトから5月中旬以降にcloneした場合は不要です。)

解決法を以下にまとめておきますので、プール運営に携わっている方などは参考にどうぞ。

stratum-mining利用時

Crypto-Expert氏管理のstratum-miningの最新版への入れ替えで対策ができます。(こちらへの入れ替え時はこの後のstratum-mining特有の問題も参照のこと

$ git clone https://github.com/Crypto-Expert/stratum-mining.git

またはstratum-miningのディレクトリ中のlib/templateregistry.pyに太字の文字列を以下のように追記する。

        # Check if extranonce2 looks correctly. extranonce2 is in hex form...
        if len(extranonce2) != self.extranonce2_size * 2:
            raise SubmitException("Incorrect size of extranonce2. Expected %d chars" % (self.extranonce2_size*2))
        
        # normalize the case to prevent duplication of valid shares by the client
	ntime = ntime.lower()
	nonce = nonce.lower()
	extranonce2 = extranonce2.lower()
        # Check for job
        job = self.get_job(job_id, worker_name, ip)
        if job == None:
            raise SubmitException("Job '%s' not found" % job_id)
                
        # Check if ntime looks correct
        if len(ntime) != 8:
            raise SubmitException("Incorrect size of ntime. Expected 8 chars")

        if not job.check_ntime(int(ntime, 16)):

詳しくは以下を参照のこと。

https://github.com/Crypto-Expert/stratum-mining/commit/d5b4ffddf60117c177945e0ea544288e9a9b2db9

なお、下記stratum等最近更新が行われていないstratum利用時は未対策版が最新となっているため、上記修正を手動で行う必要があります。

https://github.com/ahmedbodi/stratum-mining

一部では過去の脆弱性として認識がありますが、新規開設プールを中心として未対策版を利用している方が見えるようです。

国内のプール構築記事で指定されていることが多いため、利用時は本修正を実施の上で利用してください。

 

 

NOMP(node-open-mining-portal),node-stratum-poo利用時

実体となるnode-stratum-poolのlib/stratum.jsに対し、以下の太字の内容へ変更する。 ※おそらくこれであっていると思われますが、間違っている場合はご指摘いただけると助かります

 

  function handleSubmit(message){
     if (!_this.authorized){
         sendJson({
              id : message.id,
              result: null,
              error : [24, "unauthorized worker", null]
              });
         considerBan(false);
         return;
     }
     if (!_this.extraNonce1){
         sendJson({
             id : message.id,
             result: null,
             error : [25, "not subscribed", null]
             });
         considerBan(false);
         return;
     }
     _this.emit('submit',
         {            
             name : message.params[0],
             jobId : message.params[1],
             extraNonce2 : message.params[2],
             nTime : message.params[3].toLowerCase(),
             nonce : message.params[4].toLowerCase()
         },
         function(error, result){
             if (!considerBan(result)){
                 sendJson({
                      id: message.id,
                      result: result,
                      error: error
                 });
             }
        }
    );
}

詳しくは以下を参照のこと。

https://github.com/zone117x/node-open-mining-portal/issues/430

 

なお、この脆弱性は現時点で他のstratumにも存在するかは現時点で不明です。(とはいえだいたいの方はどちらかを使っているので問題はないと思われますが・・・)

 

見分け方は、ハッシュレートに対し、同一ハッシュレートなのにblock発見量が異様に少ないマイナーがいるかどうかや、対策導入後にハッシュレートが急減したかがひとつの基準となります。 もし、大きく減少したマイナーがいる場合は、残念ながら過去他マイナーが不正利用者によって割を食っていた時期があったといっていいかと思います。

ただし、復帰直後は他のプールへ退避している可能性や最適難易度に落ち着いていないことによるハッシュ低下もあるため、早期の決めつけは誤爆リスクもあるため十分ご注意ください。

コードから概要を推察されている方もいるかと思いますが、本脆弱性は下記のような値のnonce値の通知があった際、下記例すべてを別の値として認識してしまうことに由来します。

ex1: 0000000089558a32e2fb339d1e99ef2f53eb950e5436ec79dae1260a8cb78abd
ex2: 0000000089558a32e2fb339d1e99ef2f53eb950e5436ec79dae1260a8cb78abD
ex3: 0000000089558a32e2fb339d1e99ef2f53eb950e5436ec79dae1260a8cb78aBd
ex4: 0000000089558a32e2fb339d1e99ef2f53eb950e5436ec79dae1260a8cb78Abd
ex5: 0000000089558a32e2fb339d1e99ef2f53eb950e5436ec79dae1260a8cb78aBD
ex6: 0000000089558a32e2fb339d1e99ef2f53eb950e5436ec79dae1260a8cb78AbD

よくよく見ていただくとわかりますが、これは同一値で末尾側の文字が大文字にしてあるだけです。 わかりやすく説明するためこのような形としていますが、これを利用することで、仕組み上は実体より大規模なハッシュでマイニングしているように見せかけ、多くshareを発見したように偽り報酬を本来より多く奪い取ることができるものです。

そのため、流入元となるstratumですべて小文字統一とし、上記のような値をふるい落としています。(もちろんstratumではなくDB側で値の一意性を担保してもかまいません。)

プールの信用上致命的な問題となるため、未対策の場合は早急な対策を実施しましょう。

※2018/1/14追記 最近構築された方において本脆弱性未対策のstratumを使用されている方がそこそこ見受けられたため、解説などを含め記事内容アップデートを実施しました。

 

なお、今回の水増し対策の動作イメージは下記の通りです。

対策前


対策後

1番をふるい落とすのはもちろんですが、2番や4番をふるい落とせない場合は今回の水増し攻撃の対象となってしまいます。

 

現在ASIC Poolを始めとした国内プールでは今回の問題への対策が行われており、VIP Poolを除いた他のプールは対応が完了。 残っているVIP Poolも日曜日には対応を完了させる予定とのことです。 VIP Poolも含め対応が完了しました。

しかし、厳密に言うとVIP Poolで実際に悪用された脆弱性は別物のようです。

 

stratum-mining特有の問題

このstratum-mining特有の問題は、難易度設定まわりでアルゴリズムごとに適切な目標難易度が設定されないというものです。

この問題が存在するのはCrypto-Expert氏が管理する「stratum-mining」で現在確認が取れています。  他の方が管理するstratum-mining、たとえばahmedbodi氏が管理する「stratum-mining」などではこの問題は発生しません。 ただし、他の方が管理しているstratum-miningでは最初に挙げた脆弱性は修正されていないものも存在するので忘れずに対策を実施してください。(詳しい対象は後ほど記述します)

 

今回stratum-mining特有の脆弱性はCrypto-Expert氏が管理する「stratum-mining」における2013年12月16日のcommitに端を発する問題です。 該当のcommitは以下のリンクより。

https://github.com/Crypto-Expert/stratum-mining/commit/24e71e40e7b4141fadb30c33e6733b55403e0abe

抜粋すると以下の部分が問題の個所(変更点)です。

    def diff_to_target(self, difficulty):
        '''Converts difficulty to target'''
	if settings.COINDAEMON_ALGO == 'scrypt' or 'scrypt-jane':
	       	diff1 = 0x0000ffff00000000000000000000000000000000000000000000000000000000
        elif settings.COINDAEMON_ALGO == 'quark':
                diff1 = 0x000000ffff000000000000000000000000000000000000000000000000000000
        else:   diff1 = 0x00000000ffff0000000000000000000000000000000000000000000000000000
	return diff1 / difficulty

マイニングアルゴのscrypt-janeに対応するためにコードが追加されたのがこのcommitです。

一見するとよさそうなコードに見えます。しかしこの部分、抜き出して実行してみるとおかしなことになります。

以下に部分的に抜き出して実行できるようにしたコードを置いておきます。 期待としてはquarkの処理が実行されてほしいわけですが・・・


見ての通り、quarkを選んでいるのにもかかわらずscryptとしての処理がなされてしまいました。

このコードの問題点を修正すると以下になります。


元のコードはor演算の右側が常に1となるため、一番上のif分の処理が常に実行されてしまう、つまりは論理演算で端折ってはいけない”何が何と等しいか?”を適切に記述しなかったために起きている問題なわけです。

 

修正方法は簡単で、「lib/template_registry.py」の該当部分を以下の太字のように修正するだけでこの問題は解決します。

    def diff_to_target(self, difficulty):
        '''Converts difficulty to target'''
	if settings.COINDAEMON_ALGO == 'scrypt' or settings.COINDAEMON_ALGO =='scrypt-jane':
	       	diff1 = 0x0000ffff00000000000000000000000000000000000000000000000000000000
        elif settings.COINDAEMON_ALGO == 'quark':
                diff1 = 0x000000ffff000000000000000000000000000000000000000000000000000000
        else:   diff1 = 0x00000000ffff0000000000000000000000000000000000000000000000000000
	return diff1 / difficulty

なおこの問題の対象となるstratum-miningは、Crypto-Expert氏のstratum-miningとこのプロジェクトから2013年12月16日以降にforkしたプロジェクトです。 metalicjames氏のstratum-mining-Lyra2REも対象です。 ただし、mrtexaznl氏管理のstratum-miningのようにfork後に対応アルゴ追加と同時に修正しているものも存在するため、それ以降だからといって対象とは限らないのでご注意を。(逆に言うと対象以外でもこの問題を抱えている可能性がありますが・・・)

そして何より不思議なのは、指摘がなされているにも関わらずいまだに修正されていない点だったりしますが、「現状NOMP系に大部分が移行してる」というあたりも影響しているのかもしれません・・・

投稿者プロフィール

monaf
mona digestの管理人でもあります。 こちらは向こうでは外れてしまうような内容を中心にお送りしてまいります。

monacoin:MMditkELuURZgDPduLDLGYArA15G7nWFo3
kumacoin:KHtxC7CdYUUfXLEEprxoN8re58ckhNbSwb
ringo:RVQNKVMRcJ8dCH687zQo3nDUN7JXgoRdid
bitzeny:ZboAKf49TpBg5ef9vSXxNbajmyyjZNNCat

About Author

monaf
mona digestの管理人でもあります。 こちらは向こうでは外れてしまうような内容を中心にお送りしてまいります。 monacoin:MMditkELuURZgDPduLDLGYArA15G7nWFo3 kumacoin:KHtxC7CdYUUfXLEEprxoN8re58ckhNbSwb ringo:RVQNKVMRcJ8dCH687zQo3nDUN7JXgoRdid bitzeny:ZboAKf49TpBg5ef9vSXxNbajmyyjZNNCat

Leave a Comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です