#36025 [W&A-Critical] A malicious validator can overwrite the account data of any archive server connected to it.
Submitted on Oct 16th 2024 at 05:55:23 UTC by @periniondon630 for Audit Comp | Shardeum: Ancillaries II
Report ID: #36025
Report Type: Websites and Applications
Report severity: Critical
Target: https://github.com/shardeum/archive-server/tree/dev
Impacts:
Direct theft of user funds
Description
Brief/Intro
When any active archive server is connected to a malicious validator to receive data updates, the attacker can overwrite any account data on this archiver
Vulnerability Details
The archive server places too much trust in the data it receives from the source validator. It only performs data format validation, but lacks data verification and account ownership checks. As a result, if an active archive server connects to a malicious validator, the validator can overwrite any account data on it. If data of type RECEIPT is received, the storeReceiptData function will be called, which performs basic format validation by calling validateArchiverReceipt function. ``` if (newData.responses && newData.responses.RECEIPT) { if (config.VERBOSE) Logger.mainLogger.debug( 'RECEIPT', sender.nodeInfo.publicKey, sender.nodeInfo.ip, sender.nodeInfo.port, newData.responses.RECEIPT.length ) storeReceiptData( newData.responses.RECEIPT, sender.nodeInfo.ip + ':' + sender.nodeInfo.port, true, config.saveOnlyGossipData ) } ``` It is important to set the globalModification flag to true, as this will allow the remaining validation to be skipped.: ``` if (receipt.globalModification) return true ``` After the validation is complete, the account provided in the receipt will be updated with the data included in the same receipt: ``` for (const account of afterStates) { const accObj: Account.AccountsCopy = { accountId: account.accountId, data: account.data, timestamp: account.timestamp, hash: account.hash, cycleNumber: cycle, isGlobal: account.isGlobal || false, } if (account.timestamp !== account.data['timestamp']) Logger.mainLogger.error('Mismatched account timestamp', txId, account.accountId) if (account.hash !== account.data['hash']) Logger.mainLogger.error('Mismatched account hash', txId, account.accountId)
...... } ``` There is no check for votes; an attacker can even overwrite the global network account, which contains the entire network configuration.
Impact Details
Any account data on the archive server can be changed if it is connected to a malicious validator via a socket.io connection. If combined with my report 36021, the attacker can change account data on all archivers.
References
https://github.com/shardeum/archive-server/blob/0337daa477b3a30f8fb65b87c23b021a261441bd/src/Data/Data.ts#L211 https://github.com/shardeum/archive-server/blob/0337daa477b3a30f8fb65b87c23b021a261441bd/src/Data/Collector.ts#L765
Link to Proof of Concept
https://gist.github.com/periniondon630/c9f1b1c10eed42e07d3e4980f1199205
Proof of Concept
Enable the VERBOSE flag in `src/Config.ts` of the archive-server repository.
Apply `validator.patch` to all non-attacker validators in the `shardus-core` repository. This will reduce the wait time (for POC) until the archive server connects to the malicious validator. Otherwise, you will need rotation enabled.
Start the local network.
Apply `attacker.patch` to the malicious code repository (`shardus-core`).
Start the malicious node and wait until it becomes active.
Check `main.log` of the archive-server. When it connects to the malicious validator, there will be a message like: ``` RECEIPT <publicKey> <ip> <port> 0 ```
Run the attack by executing the following command: ```shell curl 'http://ATTACKER_IP:ATTACKER_PORT/attack?id=1234' ``` The value of the `id` parameter can be any random string.
Verify that the account was updated using the SQLite client for `accountId` `1000000000000000000000000000000000000000000000000000000000000001`.