Network not being able to confirm new transactions (total network shutdown)
Description
Description
In shardeum network, archive server maintain a websocket connection to nodes for data subscription. This initiatation happen at /reqeustdata endpoint of core. Archiver can also unsubscribe via that endpoint. The problem lies within the endpoint handler's failure to check the unbsubscribe request's intended cycle so that it prevent replays. This mean that somebody can store a unsubscribe request form archiver to nodes and reply it at later time on the node repeatedly essentially resulting in abusive rejection of data subscription for a particular node.
This is the affected area of the code in core repo for its failure to check the cycle of the unsubscribe request
network.registerExternalPost('requestdata', (req, res) => {
let err = validateTypes(req, { body: 'o' })
if (err) {
/* prettier-ignore */ if (logFlags.error) warn(`requestdata: bad req ${err}`)
res.json({ success: false, error: err })
return
}
err = validateTypes(req.body, {
tag: 's',
})
if (err) {
/* prettier-ignore */ if (logFlags.error) warn(`requestdata: bad req.body ${err}`)
res.json({ success: false, error: err })
return
}
const dataRequest = req.body
if (logFlags.p2pNonFatal) info('dataRequest received', Utils.safeStringify(dataRequest))
const foundArchiver = archivers.get(dataRequest.publicKey)
if (!foundArchiver) {
const archiverNotFoundErr = 'Archiver not found in list'
/* prettier-ignore */ if (logFlags.error) warn(archiverNotFoundErr)
res.json({ success: false, error: archiverNotFoundErr })
return
}
const invalidTagErr = 'Tag is invalid'
const archiverCurvePk = crypto.convertPublicKeyToCurve(foundArchiver.publicKey)
if (!crypto.authenticate(dataRequest, archiverCurvePk)) {
/* prettier-ignore */ if (logFlags.error) warn(invalidTagErr)
res.json({ success: false, error: invalidTagErr })
return
}
/* prettier-ignore */ if (logFlags.p2pNonFatal) info('Tag in data request is valid')
if (config.p2p.experimentalSnapshot && config.features.archiverDataSubscriptionsUpdate) {
if (dataRequest.dataRequestType === DataRequestTypes.SUBSCRIBE) {
// if the archiver is already in the recipients list, remove it first
if (dataRequest.nodeInfo && recipients.has(dataRequest.nodeInfo.publicKey)) {
removeArchiverConnection(dataRequest.nodeInfo.publicKey)
recipients.delete(dataRequest.nodeInfo.publicKey)
}
if (recipients.size >= config.p2p.maxArchiversSubscriptionPerNode) {
const maxArchiversSupportErr = 'Max archivers support reached'
warn(maxArchiversSupportErr)
res.json({ success: false, error: maxArchiversSupportErr })
return
}
addDataRecipient(dataRequest.nodeInfo, dataRequest)
}
if (dataRequest.dataRequestType === DataRequestTypes.UNSUBSCRIBE) {
removeDataRecipient(dataRequest.publicKey)
removeArchiverConnection(dataRequest.publicKey)
}
res.json({ success: true })
return
}
delete dataRequest.publicKey
delete dataRequest.tag
const dataRequestCycle = dataRequest.dataRequestCycle
const dataRequestStateMetaData = dataRequest.dataRequestStateMetaData
const dataRequests = []
if (dataRequestCycle) {
dataRequests.push(dataRequestCycle)
}
if (dataRequestStateMetaData) {
dataRequests.push(dataRequestStateMetaData)
}
if (dataRequests.length > 0) {
addDataRecipient(dataRequest.nodeInfo, dataRequests)
}
res.json({ success: true })
})
Proof of Concept
Proof of Concept
Apply this patch to archiver repo to grab the data unsubscribe payload
What the above example shows is that archiver is subscribe to node at port 9001 and intended for cycle 2. but if we used that payload object to inject to the node 9001 even at later cycle, the node terminate the socket connection with the archiver. This is because the node is not checking the cycle of the unsubscribe request.
To trigger attack please make a POST request to node x endpoint with the payload object above
We can reuse the same payload on same node over and over to make node reject the archiver connection.
Please just make sure to replace node ip accordingly
Impact
This vulnerability allows an attacker to abuse the archiver by replaying the unsubscribe request to the node. This can result in the node rejecting the archiver connection. This can be used to disrupt the data subscription service of the archiver.