Monroe (XMR) lock transfer attack details analysis

By: ISME@SlowMist Team

Recently, according to the information of the slow fog area, the XMR transfer lock-up attack appeared on multiple exchanges. The slow fog security team conducted analysis and follow-up on the first time of receiving information. Based on the principle of responsible disclosure, we For a time, early warnings were carried out in the slow fog area and timely intelligence synchronization and assistance in testing and repair were performed for the customers we served. If you need to provide verification and testing services, please contact the Slow Fog Safety Team.

Attack step

0x01: Enter the password to enter the wallet via monero-wallet-cli

0x02: Send a locked transaction by command

0x03: The transfer is completed, the exchange does not perform locked transaction (locked_transfer) detection, and receives the coin that is set to lock the block height to unlock (can be understood as locked for the specified time).

0x04: The malicious user immediately withdraws the money and leaves the exchange face.

Affect

First of all, the attack will not result in any loss of funds on the exchange, but will lock in the exchange's XMR liquidity.

An extreme case: If the exchange receives a Monroe coin that needs to be locked for one year or more, it will result in no money to be paid when the user comes to withdraw the coin within one year (you can only purchase additional coins to give the user extract).

About the locked_transfer command

Monero-wallet-cli The following explains the locked_transfer command:

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

Transfer order:

Locked_transfer FromAddress ToAddress 0.0101 20000

FromAddress: Send address (usually the attacker's wallet address)

ToAddress: Receive address (usually the exchange wallet address)

0.0101: the amount of the transfer

20000: The number of locked blocks

How to protect

The general exchange will use the get_transfers RPC interface to resolve whether the XMR transaction detection recharge is accounted for. When parsing, it only needs to judge whether the unlock_time field is greater than 0 to perform effective detection.

Note: unlock_time is of type int. If it is greater than 0, it means that the transaction has a locked block, and it can not be confirmed for malicious transactions. In order to avoid the recharge of the account, the user may be treated for another kind of treatment: to determine whether the locked block has arrived, and if not, it will not be credited.

All affected RPC interfaces

(1) get_transfer

(2) get_bulk_payments

(3) show_transfer

(4) get_payments

Similarly, if you use the above four interfaces in other places, you need to judge whether the unlock_time field is greater than 0. If it is greater than 0, it will not be recharged.

The problem was previously mentioned in HackerOne by a white hat, and the Monroe official reply:

Article link:

Https://hackerone.com/reports/417515

Attachment: The following is an excerpt from the official document

Get_transfers

Returns a list oftransfers.

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 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 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 ofincoming 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 ofincoming payments using a given payment id, or a list of payments ids, from agiven height. This method is the preferred method over get_paymentsbecause it has the same functionality butis more extendable. Either is fine for looking up transactions by a singlepayment 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 informationabout 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 of 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 – 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
     }
   }
 } 

Click to view the official documentation:

Https://www.getmonero.org/resources/developer-guides/wallet-rpc.html