33637 - [BC - Critical] In get_tx_timestamp a prototype pollution bri...
In " get_tx_timestamp " a prototype pollution bricks validators
Submitted on Jul 25th 2024 at 13:08:56 UTC by @infosec_us_team for Boost | Shardeum: Core
Report ID: #33637
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
About The Scope
Where is the bug?
The issue is an insecure assignment in line #1049 of TransactionConsensus.ts
.
https://github.com/shardeum/shardus-core/blob/dev/src/state-manager/TransactionConsensus.ts#L1049
What are the scope rules of TransactionConsensus.ts?
@Mehdi from Shardeum team publicly said in Immunefi's Discord about TransactionConsensus.ts
:
"that was originally out of scope, but it's more accurate to say large parts of TransactionConsensus are out of scope. Large parts of it hold old code that is not active with the default config, namely: code paths that are behind useNewPOQ flag."
https://discord.com/channels/787092485969150012/1256211020482084987/1263282057769914368
They also confirmed that:
"Bugs in code from
TransactionConsensu.ts
that are not guarded by any configurable flag at all (like useNewPOQ and others), and that can be directly exploited by anyone, are in scope.https://discord.com/channels/787092485969150012/1256211020482084987/1264362678550265877
This report is in scope
The insecure assignment can be exploited by sending a request to an entry point that isn't guarded by any configurable flag.
Report Description
Location of the insecure assignment
The function generateTimestampReceipt(...)
inside TransactionConsensus.ts
contains the assignment below, where the value of signedTsReceipt.cycleCounter
and txId
are strings controlled by an attacker, and signedTsReceipt
is an object:
Line of code from: https://github.com/shardeum/shardus-core/blob/dev/src/state-manager/TransactionConsensus.ts#L1049
Sending '__proto__'
as the value for signedTsReceipt.cycleCounter
and sending 'somethingHere'
as the value for the variable txId
, the assignment becomes:
If the reader has experience exploiting server-side prototype pollution, the next section can be skipped, but we recommend reading it as a quick recap.
Understanding Prototype Pollution
Before diving deep into the report, is important to understand what the vulnerable assignment in Shardus Core does.
Let's start by mentioning the following line:
Produces the same outcome as the following line:
Executing any of them adds by default a field something
with value 1
to all new and previously created javascript objects during runtime.
Runnable Example:
In Typescript create an empty object out of JSON named person:
Pollute the prototype by adding a field whitehat
with the value true
:
Now the object person.whitehat
returns true:
But also does all other objects in the entire codebase, whether new or existing. For example, create a completely new object and log its whitehat
field:
Here's the full snippet of code for the example if you want to play around:
Exploiting the attack vector in Shardus Core
Active nodes can gossip other validators using the internal route get_tx_timestamp
to ask for or store the timestamp of a tx.
Below is the code that handles this request:
Snippet of code from: https://github.com/shardeum/shardus-core/blob/dev/src/state-manager/TransactionConsensus.ts#L242-L262
First, the server checks if the TX is in the cache. If that's the case, it returns its value, if not, it saves to the cache the values given in the request.
The function generateTimestampReceipt(...)
is the one that stores the received value in the cache.
Snippet of code from: https://github.com/shardeum/shardus-core/blob/dev/src/state-manager/TransactionConsensus.ts#L1029-L1051
There, we can see the vulnerable assignment once again:
this.txTimestampCache[signedTsReceipt.cycleCounter][txId] = signedTsReceipt
Adding fields to all Objects in the server
If the gossipped message to the get_tx_timestamp
route contains the following payload:
Where "bsdsdsdsd" is random gibberish.
Then, the vulnerable assignment becomes:
As a consequence, all objects in existence and new objects created in the future will contain a field . bsdsdsdsd
and the value will be the content of the signedTsReceipt
object.
A chain of crashes in Shardus Core
Many core functions in Shardus Core read and process the keys and values of an object.
When an attacker adds an unexpected key and value to ALL objects in the server, these functions crash, when they crash the server tries to exit by calling the function exitUncleanly(..)
, but even exitUncleanly(..)
crashes.
An infinite loop of crash -> try to exit -> repeat begins.
Let's analyze one of the functions that starts the crashing loop: Take a look at these lines of code in the _takeSnapshot()
function (which is executed repeatedly, with a timer):
Code snippet from: https://github.com/shardeum/shardus-core/blob/dev/src/statistics/index.ts#L337-L362
One of the keys returned by the object this.counters after receiving the malicious payload, is bsdsdsdsd, which is the gibberish we used in the attack to pollute ALL objects in the server.
The function will crash when trying to execute: this.counters[ 'bsdsdsdsd' ].snapshot()
because the value of bsdsdsdsd does not contain a function named "snapshot()".
The following error is printed to
./instances/shardus-instance-PORT_OF_VICTIM/logs/out.log
The following error is printed to the same path, but file
fatal.log
When that line crashes, the exception handler registered here https://github.com/shardeum/shardus-core/blob/dev/src/shardus/index.ts#L2856-L2887 will try to shut down the server uncleanly.
Forcing nodes to keep running under invalid states
But the exit attempt throws an exception as well, with the following stack trace: