Taking and/modifying authenticated actions (with or without blockchain state interaction) on behalf of other users without any interaction by that user, such as:
Changing registration information
Commenting
Voting
Making trades
Withdrawals, etc.
Direct theft of user funds
Description
Shardeum Ancillaries Bug Report
Malicious Validator Can Overwrite Any Cycle Data
Summary
A vulnerability in the collectCycleData function allows a malicious Validator to manipulate the Archiver by processing a fake cycle controlled entirely by the Validator. This issue enables the Validator to override legitimate cycles, potentially altering critical network parameters such as adding unauthorized Archivers—an action that should be restricted to the Shardeum team.
Root Cause Analysis
Examining the collectCycleData function:
export function collectCycleData(
cycleData: P2PTypes.CycleCreatorTypes.CycleData[],
senderInfo: string
): void {
for (const cycle of cycleData) {
// Logger.mainLogger.debug('Cycle received', cycle.counter, senderInfo)
let cycleToSave = []
if (receivedCycleTracker[cycle.counter]) {
if (receivedCycleTracker[cycle.counter][cycle.marker]) {
if (!receivedCycleTracker[cycle.counter][cycle.marker]['senderNodes'].includes(senderInfo)) {
receivedCycleTracker[cycle.counter][cycle.marker]['receivedTimes']++
receivedCycleTracker[cycle.counter][cycle.marker]['senderNodes'].push(senderInfo)
}
} else {
if (!validateCycleData(cycle)) continue
receivedCycleTracker[cycle.counter][cycle.marker] = {
cycleInfo: cycle,
receivedTimes: 1,
saved: false,
senderNodes: [senderInfo],
}
if (config.VERBOSE) Logger.mainLogger.debug('Different Cycle Record received', cycle.counter)
}
} else {
if (!validateCycleData(cycle)) continue
receivedCycleTracker[cycle.counter] = {
[cycle.marker]: {
cycleInfo: cycle,
receivedTimes: 1,
saved: false,
senderNodes: [senderInfo],
},
}
}
if (config.VERBOSE)
Logger.mainLogger.debug('Cycle received', cycle.counter, receivedCycleTracker[cycle.counter])
const minCycleConfirmations =
Math.min(Math.ceil(NodeList.getActiveNodeCount() / currentConsensusRadius), 5) || 1
for (const value of Object.values(receivedCycleTracker[cycle.counter])) {
if (value['saved']) {
// If there is a saved cycle, clear the cycleToSave of this counter; This is to prevent saving the another cycle of the same counter
for (let i = 0; i < cycleToSave.length; i++) {
// eslint-disable-next-line security/detect-object-injection
receivedCycleTracker[cycle.counter][cycleToSave[i].marker]['saved'] = false
}
cycleToSave = []
break
}
if (value['receivedTimes'] >= minCycleConfirmations) {
cycleToSave.push(cycle) // BUG HERE!
value['saved'] = true
}
}
if (cycleToSave.length > 0) {
processCycles(cycleToSave)
}
}
// ... some non relevant stuff
}
Vulnerability Breakdown
When processing a new cycle, the function follows these steps:
If the cycle.counter and cycle.marker exist in receivedCycleTracker, it increments receivedTimes.
Otherwise, it validates the cycle using validateCycleData and adds it to receivedCycleTracker.
If receivedTimes meets or exceeds minCycleConfirmations, the cycle is added to cycleToSave.
Finally, processCycles(cycleToSave) is called to process the cycle.
The Problem
The cycle added to cycleToSave is simply the last cycle received, not necessarily the validated one. This means that the last validator to submit a cycle can modify its properties and force an incorrect cycle into the Archiver, potentially altering network parameters.
Exploitation Steps
Scenario
Assume minCycleConfirmations == 5 and there are five connected Validators, with only one being malicious.
Steps to Exploit
The first four Validators submit the correct cycle (cycle.counter == x, cycle.marker == y).
The malicious Validator submits a modified cycle while maintaining the same cycle.counter and cycle.marker. For example, it could add an unauthorized Archiver.
The Archiver receives the fake cycle and sees receivedTimes == 5, adding the newly received cycle to cycleToSave.
The Archiver processes the fake cycle (registering the unauthorized Archiver or modifying other network parameters, depending on the payload).
Impact
A malicious Validator can manipulate key network parameters, including node counts, Archivers, and more.
The Validator effectively gains control over the information processed by Archivers, allowing unauthorized data modifications.
The ability to register Archivers without Shardeum team approval is a critical security risk.
The corrected version ensures that only the cycle that underwent validation is stored and processed, preventing last-minute tampering.
Proof of Concept
Proof of Concept (PoC)
Basically, all we need to do is create a malicious Validator to execute this attack, but to make the POC easier to perform, I added some additional logs to the Archiver for additional visibility, I suggest adding them as well.
In the collectCycleData function add the following logs after the minCycleConfirmations calculation:
NOTE: cycle can contain and false information, for this POC we simply change the maxSyncTime.
5. Observe the Archiver accepting and storing the fake cycle without issues.