作者:ISME@慢霧安全團隊
公眾號:慢霧科技

近日據慢霧區情報顯示,針對門羅幣(XMR)轉賬鎖定攻擊在多個交易所出現,慢霧安全團隊在收到情報第一時間進行分析跟進,本著負責任披露的原則我們第一時間在慢霧區進行了預警并為我們所服務的客戶進行了及時的情報同步以及協助檢測和修復。如有其他需要提供驗證和檢測服務歡迎聯系慢霧安全團隊。

一.攻擊步驟

0x01:通過 monero-wallet-cli 輸入密碼登錄錢包

0x02:通過命令發送鎖定交易

0x03:轉賬完成,交易所未進行鎖定交易(locked_transfer)檢測,接收到被設置鎖定區塊高度才能解鎖的幣(可以理解為鎖定了指定時間)。

0x04:惡意用戶立即提幣走人,留下交易所一臉懵逼。

二.造成影響

首先該攻擊不會導致交易所任何資金損失,但是會鎖定了交易所 XMR流動性。

極端情況舉例:如果交易所收到的都是需要鎖定一年甚至更多年的門羅幣則會導致一年內用戶來提幣的時候無幣可以提(只能去購買額外的幣來給用戶提取)。

三.關于 locked_transfer 命令

monero-wallet-cli 關于 locked_transfer 命令解釋如下:

  locked_transfer [ index = <N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <addr> <amount>) <lockblocks> [<payment_id (obsolete)>]

轉賬命令:

locked_transfer FromAddress ToAddress 0.0101 20000

FromAddress :發送地址(一般為攻擊者錢包地址)

ToAddress :接收地址(一般為交易所錢包地址)

0.0101:為轉賬金額

20000:為鎖定區塊數

四.如何防護

一般交易所會通過get_transfers RPC 接口來解析 XMR交易檢測充值是否到賬,在進行解析的時候只需要對 unlock_time字段進行判斷是否大于 0 則可以進行有效檢測。

注:unlock_time 為 int 類型,如果大于 0 則意味著該交易有鎖定區塊,為惡意交易可以不予確認到賬。為了避免充值不予到賬損害“用戶”利益可以進行另外一種處理:判斷鎖定區塊是否到達,如果未到達則不予入賬。

五.所有受影響 RPC接口

(1)get_transfer

(2)get_bulk_payments

(3)show_transfer

(4)get_payments

同理:在其他地方使用了如上四個接口的地方也需要對unlock_time字段進行判斷是否大于 0 ,大于 0 則不予充值到賬。

該問題之前在 HackerOne 也有被白帽子提過漏洞賞金,其中門羅官方回復:

文章鏈接:

https://hackerone.com/reports/417515

附:以下內容為官方文檔摘錄

get_transfers

Returns a list of transfers.

Alias: None.

Inputs:

  • in - boolean; (Optional) Include incoming transfers.
  • out - boolean; (Optional) Include outgoing transfers.
  • pending - boolean; (Optional) Include pending transfers.
  • failed - boolean; (Optional) Include failed transfers.
  • pool - boolean; (Optional) Include transfers from the daemon's transaction pool.
  • filter_by_height - boolean; (Optional) Filter transfers by block height.
  • min_height - unsigned int; (Optional) Minimum block height to scan for transfers, if filtering by height is enabled.
  • max_height - unsigned int; (Opional) Maximum block height to scan for transfers, if filtering by height is enabled (defaults to max block height).
  • account_index - unsigned int; (Optional) Index of the account to query for transfers. (defaults to 0)
  • subaddr_indices - array of unsigned int; (Optional) List of subaddress indices to query for transfers. (Defaults to empty - all indices)

Outputs:

  • in array of transfers:

    • address - string; Public address of the transfer.

    • amount - unsigned int; Amount transferred.

    • confirmations - unsigned int; Number of block mined since the block containing this transaction (or block height at which the transaction should be added to a block if not yet confirmed).

    • double_spend_seen - boolean; True if the key image(s) for the transfer have been seen before.

    • fee - unsigned int; Transaction fee for this transfer.

    • height - unsigned int; Height of the first block that confirmed this transfer (0 if not mined yet).

    • note - string; Note about this transfer.

    • payment_id - string; Payment ID for this transfer.

    • subaddr_index - JSON object containing the major & minor

      • major -unsigned int; Account index for the subaddress.
      • minor -unsigned int; Index of the subaddress under the account.
    • suggested_confirmations_threshold- unsigned int; Estimation of the confirmations needed for the transaction to be included in a block.

    • timestamp - unsigned int; POSIX timestamp for when this transfer was first confirmed in a block (or timestamp submission if not mined yet).

    • txid - string; Transaction ID for this transfer.

    • type - string; Transfer type: "in"

    • unlock_time - unsigned int; Number of blocks until transfer is safely spendable.

  • out array of transfers (see above).

  • pending array of transfers (see above).
  • failed array of transfers (see above).
  • pool array of transfers (see above).

Example:

$ curl -X POST http://127.0.0.1:18082/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"get_transfers","params":{"in":true,"account_index":1}}' -H 'Content-Type: application/json'
{
  "id": "0",
  "jsonrpc": "2.0",
  "result": {
    "in": [{
      "address": "77Vx9cs1VPicFndSVgYUvTdLCJEZw9h81hXLMYsjBCXSJfUehLa9TDW3Ffh45SQa7xb6dUs18mpNxfUhQGqfwXPSMrvKhVp",
      "amount": 200000000000,
      "confirmations": 1,
      "double_spend_seen": false,
      "fee": 21650200000,
      "height": 153624,
      "note": "",
      "payment_id": "0000000000000000",
      "subaddr_index": {
        "major": 1,
        "minor": 0
      },
      "suggested_confirmations_threshold": 1,
      "timestamp": 1535918400,
      "txid": "c36258a276018c3a4bc1f195a7fb530f50cd63a4fa765fb7c6f7f49fc051762a",
      "type": "in",
      "unlock_time": 0
    }]
  }
}

get_payments

Get a list of incoming payments using a given payment id.

Alias: None.

Inputs:

  • payment_id - string; Payment ID used to find the payments (16 characters hex).

Outputs:

  • payments - list of:

    • payment_id - string; Payment ID matching the input parameter.

    • tx_hash - string; Transaction hash used as the transaction ID.

    • amount - unsigned int; Amount for this payment.

    • block_height - unsigned int; Height of the block that first confirmed this payment.

    • unlock_time - unsigned int; Time (in block height) until this payment is safe to spend.

    • subaddr_index - subaddress index:

      • major - unsigned int; Account index for the subaddress.
      • minor - unsigned int; Index of the subaddress in the account.
    • address - string; Address receiving the payment; Base58 representation of the public keys.

Example:

$ curl -X POST http://127.0.0.1:18082/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"get_payments","params":{"payment_id":"60900e5603bf96e3"}}' -H 'Content-Type: application/json'
{
  "id": "0",
  "jsonrpc": "2.0",
  "result": {
    "payments": [{
      "address": "55LTR8KniP4LQGJSPtbYDacR7dz8RBFnsfAKMaMuwUNYX6aQbBcovzDPyrQF9KXF9tVU6Xk3K8no1BywnJX6GvZX8yJsXvt",
      "amount": 1000000000000,
      "block_height": 127606,
      "payment_id": "60900e5603bf96e3",
      "subaddr_index": {
        "major": 0,
        "minor": 0
      },
      "tx_hash": "3292e83ad28fc1cc7bc26dbd38862308f4588680fbf93eae3e803cddd1bd614f",
      "unlock_time": 0
    }]
  }
}

get_bulk_payments

Get a list of incoming payments using a given payment id, or a list of payments ids, from a given height. This method is the preferred method over get_paymentsbecause it has the same functionality but is more extendable. Either is fine for looking up transactions by a single payment ID.

Alias: None.

Inputs:

  • payment_ids - array of: string; Payment IDs used to find the payments (16 characters hex).
  • min_block_height - unsigned int; The block height at which to start looking for payments.

Outputs:

  • payments - list of:

    • payment_id - string; Payment ID matching one of the input IDs.

    • tx_hash - string; Transaction hash used as the transaction ID.

    • amount - unsigned int; Amount for this payment.

    • block_height - unsigned int; Height of the block that first confirmed this payment.

    • unlock_time- unsigned int; Time (in block height) until this payment is safe to spend.

    • subaddr_index - subaddress index:

      • major - unsigned int; Account index for the subaddress.
      • minor - unsigned int; Index of the subaddress in the account.
    • address - string; Address receiving the payment; Base58 representation of the public keys.

Example:

$ curl -X POST http://127.0.0.1:18082/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"get_bulk_payments","params":{"payment_ids":["60900e5603bf96e3"],"min_block_height":"120000"}}' -H 'Content-Type: application/json'
{
  "id": "0",
  "jsonrpc": "2.0",
  "result": {
    "payments": [{
      "address": "55LTR8KniP4LQGJSPtbYDacR7dz8RBFnsfAKMaMuwUNYX6aQbBcovzDPyrQF9KXF9tVU6Xk3K8no1BywnJX6GvZX8yJsXvt",
      "amount": 1000000000000,
      "block_height": 127606,
      "payment_id": "60900e5603bf96e3",
      "subaddr_index": {
        "major": 0,
        "minor": 0
      },
      "tx_hash": "3292e83ad28fc1cc7bc26dbd38862308f4588680fbf93eae3e803cddd1bd614f",
      "unlock_time": 0
    }]
  }
}

get_transfer_by_txid

Show information about a transfer to/from this address.

Alias: None.

Inputs:

  • txid - string; Transaction ID used to find the transfer.
  • account_index - unsigned int; (Optional) Index of the account to query for the transfer.

Outputs:

  • transfer - JSON object containing payment information:

    • address - string; Address that transferred the funds. Base58 representation of the public keys.

    • amount - unsigned int; Amount of this transfer.

    • confirmations - unsigned int; Number of block mined since the block containing this transaction (or block height at which the transaction should be added to a block if not yet confirmed).

    • destinations - array of JSON objects containing transfer destinations:

    • amount - unsigned int; Amount transferred to this destination.

    • address - string; Address for this destination. Base58 representation of the public keys.

    • double_spend_seen - boolean; True if the key image(s) for the transfer have been seen before.

    • fee - unsigned int; Transaction fee for this transfer.

    • height - unsigned int; Height of the first block that confirmed this transfer.

    • note - string; Note about this transfer.

    • payment_id - string; Payment ID for this transfer.

    • subaddr_index - JSON object containing the major & minor subaddress index:

      • major - unsigned int; Account index for the subaddress.
      • minor - unsigned int; Index of the subaddress under the account.
    • suggested_confirmations_threshold - unsigned int; Estimation of the confirmations needed for the transaction to be included in a block.

    • timestamp - unsigned int; POSIX timestamp for the block that confirmed this transfer (or timestamp submission if not mined yet).
    • txid - string; Transaction ID of this transfer (same as input TXID).
    • type - string; Type of transfer, one of the following: "in", "out", "pending", "failed", "pool"
    • unlock_time - unsigned int; Number of blocks until transfer is safely spendable.

Example:

$ curl -X POST http://localhost:18082/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"get_transfer_by_txid","params":{"txid":"c36258a276018c3a4bc1f195a7fb530f50cd63a4fa765fb7c6f7f49fc051762a"}}' -H 'Content-Type: application/json'
{
  "id": "0",
  "jsonrpc": "2.0",
  "result": {
    "transfer": {
      "address": "55LTR8KniP4LQGJSPtbYDacR7dz8RBFnsfAKMaMuwUNYX6aQbBcovzDPyrQF9KXF9tVU6Xk3K8no1BywnJX6GvZX8yJsXvt",
      "amount": 300000000000,
      "confirmations": 1,
      "destinations": [{
        "address": "7BnERTpvL5MbCLtj5n9No7J5oE5hHiB3tVCK5cjSvCsYWD2WRJLFuWeKTLiXo5QJqt2ZwUaLy2Vh1Ad51K7FNgqcHgjW85o",
        "amount": 100000000000
      },{
        "address": "77Vx9cs1VPicFndSVgYUvTdLCJEZw9h81hXLMYsjBCXSJfUehLa9TDW3Ffh45SQa7xb6dUs18mpNxfUhQGqfwXPSMrvKhVp",
        "amount": 200000000000
      }],
      "double_spend_seen": false,
      "fee": 21650200000,
      "height": 153624,
      "note": "",
      "payment_id": "0000000000000000",
      "subaddr_index": {
        "major": 0,
        "minor": 0
      },
      "suggested_confirmations_threshold": 1,
      "timestamp": 1535918400,
      "txid": "c36258a276018c3a4bc1f195a7fb530f50cd63a4fa765fb7c6f7f49fc051762a",
      "type": "out",
      "unlock_time": 0
    }
  }
}

官方文檔鏈接:https://www.getmonero.org/resources/developer-guides/wallet-rpc.html


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.jmbmsq.com/1013/