Shardeum validator nodes implemented in the https://github.com/shardeum/shardeum
repository are vulnerable to complete DOS due to lack of input validation in fixDeserializedWrappedEVMAccount
,
which is used in internal endpoints.
Exploitation leads to a complete stall of all of the validator node's processes,
and due to the simplicity of the exploit, it is possible to execute it on all active nodes.
A more sophisticated attack would involve shutting down only a large part of the nodes, but not all,
for attacker-controlled nodes to be the only ones available, which can then be used to overtake the whole network.
The fixDeserializedWrappedEVMAccount
function,
which is commonly used throughout shardeum
repositorie, does not perform validation on the passed data.
It is possible to pass an "Array-like" object as the storageRoot
,codeHash
,codeByte
,value
, fields,
which is accepted by Uint8Array.from
used by the fixWrappedEVMAccountBuffers
/
fixAccountFields
functions called from fixDeserializedWrappedEVMAccount
.
Since "Array-like" objects only require a length
property to be set,
it is easy to pass such an object from an incoming request,
in which case Uint8Array.from({length: x})
would attempt to sequentially copy
x
values from the "Array-like" object, each of which will be 0 due to no actual array values being set,
making for an easy way to greatly increase the memory usage of the validator node without sending a large request,
and cause a DOS of the validator node because the copying done by Buffer.from
linearly depends in CPU time on the value of x
. This can easily be checked in a node
shell, using the same version that
is used by shardeum
, v18.16.1:
node
Welcome to Node.js v18.16.1.
Type ".help" for more information.
> function time(f) {console.time('f');f();console.timeEnd('f')}
undefined
> time(() => Uint8Array.from({length:1_000_000}))
f: 37.399ms
undefined
> time(() => Uint8Array.from({length:10_000_000}))
f: 231.761ms
undefined
> time(() => Uint8Array.from({length:100_000_000}))
f: 2.098s
The most effective way this could be exploited is through the internal protocol repair_oos_accounts
handler
This handler calls calculateAccountHash
, which in turn calls fixDeserializedWrappedEVMAccount
with data controlled by a potential attacker
The most basic outcome would be a total network shutdown caused by exploiting
the DOS vulnerability on each available active validator node.
Sending large length
values will cause nodes to either crash due to OOM,
or spend long times in the Uint8Array.from
call, which, when utilized across all nodes in the network,
will halt all transaction processing and cause consensus mechanisms to crash later on.
Because there are no prerequisites to the attack, the attacker can just keep on spamming
DOS-causing requests to any endpoint which calls fixDeserializedWrappedEVMAccount
,
so that actually starting the network back up would require a fix to be deployed to all nodes,
making it even more difficult to mitigate.
A more complex attack scenario, as said in the intro, would be the shutdown of nearly all, but not all, validator nodes. After the attackers nodes are picked as active and join the validators, the remaining honest validators can be shut down using the DOS vulnerability, which would leave the whole network to be controlled by the attacker. This means the network will continue functioning, but the attacker will be able to execute any transaction they want, and drain all the funds to themselves.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/from