門羅幣XMR轉(zhuǎn)賬鎖定攻擊的具體步驟及防護措施解析
近日據(jù)慢霧區(qū)情報顯示,針對門羅幣(XMR)轉(zhuǎn)賬鎖定攻擊在多個交易所出現(xiàn),慢霧安全團隊在收到情報第一時間進行分析跟進,本著負責(zé)任披露的原則我們第一時間在慢霧區(qū)進行了預(yù)警并為我們所服務(wù)的客戶進行了及時的情報同步以及協(xié)助檢測和修復(fù)。
攻擊步驟
0x01:通過 monero-wallet-cli 輸入密碼登錄錢包
0x02:通過命令發(fā)送鎖定交易
0x03:轉(zhuǎn)賬完成,交易所未進行鎖定交易(locked_transfer)檢測,接收到被設(shè)置鎖定區(qū)塊高度才能解鎖的幣(可以理解為鎖定了指定時間)。
0x04:惡意用戶立即提幣走人,留下交易所一臉懵逼。
造成影響
首先該攻擊不會導(dǎo)致交易所任何資金損失,但是會鎖定了交易所 XMR 流動性。
極端情況舉例:如果交易所收到的都是需要鎖定一年甚至更多年的門羅幣則會導(dǎo)致一年內(nèi)用戶來提幣的時候無幣可以提(只能去購買額外的幣來給用戶提?。?/p>
關(guān)于 locked_transfer 命令
monero-wallet-cli 關(guān)于 locked_transfer 命令解釋如下:
locked_transfer [index=《N1》[,《N2》,。..]] [《priority》] [《ring_size》] (《URI》 | 《addr》 《amount》) 《lockblocks》 [《payment_id (obsolete)》]
轉(zhuǎn)賬命令:
locked_transfer FromAddress ToAddress 0.0101 20000
FromAddress:發(fā)送地址(一般為攻擊者錢包地址)
ToAddress:接收地址(一般為交易所錢包地址)
0.0101:為轉(zhuǎn)賬金額
20000:為鎖定區(qū)塊數(shù)
如何防護
一般交易所會通過 get_transfers RPC 接口來解析 XMR 交易檢測充值是否到賬,在進行解析的時候只需要對 unlock_time 字段進行判斷是否大于 0 則可以進行有效檢測。
注:unlock_TIme 為 int 類型,如果大于 0 則意味著該交易有鎖定區(qū)塊,為惡意交易可以不予確認到賬。為了避免充值不予到賬損害“用戶”利益可以進行另外一種處理:判斷鎖定區(qū)塊是否到達,如果未到達則不予入賬。
所有受影響 RPC 接口
(1)get_transfer
(2)get_bulk_payments
(3)show_transfer
(4)get_payments
同理:在其他地方使用了如上四個接口的地方也需要對 unlock_TIme 字段進行判斷是否大于 0 ,大于 0 則不予充值到賬。
該問題之前在 HackerOne 也有被白帽子提過漏洞賞金,其中門羅官方回復(fù):
附:以下內(nèi)容為官方文檔摘錄
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 - 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
}
}
}