Lodestar snappy checksum issue
Impact
Unintended permanent chain split affecting greater than or equal to 25% of the network, requiring hard fork (network partition requiring hard fork)
Lodestar does not verify checksum in snappy framing uncompressed chunks.
Vulnerability Details
In Req/Resp protocol the messages are encoded by using ssz_snappy encoding, which is a snappy framing compression over ssz encoded message.
In snappy framing format there are uncompressed chunks, each such chunk is prefixed with a checksum.
Let's see how golang implementation parses such chunks - https://github.com/golang/snappy/blob/master/decode.go#L176
case chunkTypeUncompressedData:
// Section 4.3. Uncompressed data (chunk type 0x01).
if chunkLen < checksumSize {
r.err = ErrCorrupt
return r.err
}
buf := r.buf[:checksumSize]
if !r.readFull(buf, false) {
return r.err
}
checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
// Read directly into r.decoded instead of via r.buf.
n := chunkLen - checksumSize
if n > len(r.decoded) {
r.err = ErrCorrupt
return r.err
}
if !r.readFull(r.decoded[:n], false) {
return r.err
}
if crc(r.decoded[:n]) != checksum {
r.err = ErrCorrupt
return r.err
}
r.i, r.j = 0, n
continue
As you can see, if checksum is incorrect, decoder fails and returns error.
Now let's look at lodestar decoder https://github.com/ChainSafe/lodestar/blob/unstable/packages/reqresp/src/encodingStrategies/sszSnappy/snappyFrames/uncompress.ts#L17
uncompress(chunk: Uint8ArrayList): Uint8ArrayList | null {
this.buffer.append(chunk);
const result = new Uint8ArrayList();
while (this.buffer.length > 0) {
if (this.buffer.length < 4) break;
const type = getChunkType(this.buffer.get(0));
const frameSize = getFrameSize(this.buffer, 1);
if (this.buffer.length - 4 < frameSize) {
break;
}
const data = this.buffer.subarray(4, 4 + frameSize);
this.buffer.consume(4 + frameSize);
if (!this.state.foundIdentifier && type !== ChunkType.IDENTIFIER) {
throw "malformed input: must begin with an identifier";
}
if (type === ChunkType.IDENTIFIER) {
if (!Buffer.prototype.equals.call(data, IDENTIFIER)) {
throw "malformed input: bad identifier";
}
this.state.foundIdentifier = true;
continue;
}
if (type === ChunkType.COMPRESSED) {
result.append(uncompress(data.subarray(4)));
}
if (type === ChunkType.UNCOMPRESSED) {
1) result.append(data.subarray(4));
}
}
if (result.length === 0) {
return null;
}
return result;
}
As you can see, checksum is not verified, bytes are appended to 'result'
Proof of Concept
How to reproduce:
get poc via gist link and run it:
$ node dec1.mjs
checking chunk type=255
checking chunk type=1
got uncompressed chunk..
Decompressed ok 124 bytes
Czego dotyczy
Kliknij technologię, aby zobaczyć wszystkie dotyczące jej podatności.
| Platforma | Paczka | Podatne wersje | Poprawka w wersji |
|---|---|---|---|
| Paczka npm | @lodestar/reqresp | < 1.25.0 | 1.25.0 |
Odnośniki
- Słabość (CWE)
CWE-354- Platforma
- npm
- Paczka
@lodestar/reqresp- Ujawniono
- 14.01.2025
- Aktualizacja
- 11.10.2026 10:22
Źródła i licencje
OSV / GitHub Advisory Database - GHSA-m9c9-mc2h-9wjw - CC BY 4.0
Historia podatności: @lodestar/reqresp
Wszystkie opisane podatności tej paczki (Paczka npm), od najnowszej. Widać, czy to jednorazowa wpadka, czy paczka, której trzeba pilnować.
- Wszystkich wpisów
- 2
- Pierwsza → ostatnia
- 01.2025 01.2025
- Bez poprawki
- 0
- Wykorzystywana w atakach
- 0
Rozkład istotności
- Niska 2
Ujawnienia w latach
- 2025: 2
- 2026: 0
-
Lodestar snappy checksum issue ten wpis
Dowiedz się, które podatności dotyczą Twojej aplikacji
Po podłączeniu integracji DockRay i włączeniu raportowania oprogramowania DockRay porównuje zgłoszone wersje komponentów ze znanymi podatnościami i powiadamia o potwierdzonych dopasowaniach. Zakres zależy od obsługiwanej technologii i dostępnych biuletynów.