VYPR
Critical severityOSV Advisory· Published Aug 20, 2025· Updated Nov 3, 2025

Missing type checks leading to hash rewind and passing on crafted data

CVE-2025-9288

Description

Improper Input Validation vulnerability in sha.js allows Input Data Manipulation.This issue affects sha.js: through 2.4.11.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

sha.js through 2.4.11 lacks input type validation, allowing attackers to rewind hash state, cause collisions, or trigger denial of service.

Vulnerability

Overview

CVE-2025-9288 is an improper input validation vulnerability in the sha.jssha.js library (versions through 2.4.11). The library fails to enforce that inputs to the update() method are well-formed Buffer or string types. This allows arbitrary objects, such as those with a length` property set to a negative number or a very large number, to be processed, leading to undefined behavior [1][2].

Exploitation

An attacker can exploit this by providing crafted input objects to sha.js's update() method. For example, passing an object like {length: -3} can rewind the internal hash state, effectively, allowing an attacker to turn a tagged hash into an untagged hash. Similarly, providing {length: '1e99} as length can cause a denial of service (DoS) due to excessive resource consumption. The attack does not require authentication if the attacker can control the input to the hash function, which is common in many applications that hash user-supplied data [2].

Impact

Successful exploitation can lead to: - Hash state rewind: An attacker can manipulate the hash state to produce the same hash for different inputs, effectively creating collisions or bypassing hash-based integrity checks. - Value miscalculation: By crafting inputs with specific properties, an attacker can cause the hash to produce the same output for different data, leading to potential security bypasses in systems relying on hash uniqueness. - Denial of Service: Providing extremely large length values can cause the library to hang or crash, impacting availability [2].

Mitigation

The vulnerability affects all versions of sha.js up to and including 2.4.11. The fix has been implemented in commit f2a258e which adds input validation and throws errors on invalid types [3]. Users should update to a patched version (e.g., 2.4.12 or later) as soon as possible. No workaround is available other than upgrading.

AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
sha.jsnpm
< 2.4.122.4.12

Affected products

2
  • Browserify/Sha.jsOSV2 versions
    v2.1.4, v2.1.5, v2.1.6, …+ 1 more
    • (no CPE)range: v2.1.4, v2.1.5, v2.1.6, …
    • (no CPE)range: <=2.4.11

Patches

1
f2a258e9f2d0

[Fix] support multi-byte wide typed arrays

https://github.com/browserify/sha.jsNikita SkovorodaNov 26, 2024via ghsa
4 files changed · +51 9
  • .eslintrc+12 0 modified
    @@ -60,5 +60,17 @@
     				"max-lines-per-function": "off",
     			},
     		},
    +		{
    +			"files": "hash.js",
    +			"globals": {
    +				"Uint8Array": false,
    +			},
    +		},
    +		{
    +			"files": "test/test.js",
    +			"globals": {
    +				"Uint16Array": false,
    +			},
    +		},
     	],
     }
    
  • hash.js+2 4 modified
    @@ -1,6 +1,7 @@
     'use strict';
     
     var Buffer = require('safe-buffer').Buffer;
    +var toBuffer = require('to-buffer');
     
     // prototype class for hash functions
     function Hash(blockSize, finalSize) {
    @@ -12,10 +13,7 @@ function Hash(blockSize, finalSize) {
     
     Hash.prototype.update = function (data, enc) {
     	/* eslint no-param-reassign: 0 */
    -	if (typeof data === 'string') {
    -		enc = enc || 'utf8';
    -		data = Buffer.from(data, enc);
    -	}
    +	data = toBuffer(data, enc || 'utf8');
     
     	var block = this._block;
     	var blockSize = this._blockSize;
    
  • package.json+2 1 modified
    @@ -9,7 +9,8 @@
       },
       "dependencies": {
         "inherits": "^2.0.4",
    -    "safe-buffer": "^5.2.1"
    +    "safe-buffer": "^5.2.1",
    +    "to-buffer": "^1.2.0"
       },
       "devDependencies": {
         "@ljharb/eslint-config": "^21.1.1",
    
  • test/test.js+35 4 modified
    @@ -6,6 +6,12 @@ var Buffer = require('safe-buffer').Buffer;
     
     var Sha1 = require('../').sha1;
     
    +var nodeSupportsUint16 = false;
    +try {
    +	crypto.createHash('sha1').update(new Uint16Array());
    +	nodeSupportsUint16 = true;
    +} catch (err) {}
    +
     var inputs = [
     	['', 'ascii'],
     	['abc', 'ascii'],
    @@ -15,8 +21,10 @@ var inputs = [
     	['123456789abcdef123456789abcdef123456789abcdef123456789ab', 'ascii'],
     	['0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde', 'ascii'],
     	['0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef', 'ascii'],
    -	['foobarbaz', 'ascii']
    -];
    +	['foobarbaz', 'ascii'],
    +	[Buffer.from('buffer')],
    +	nodeSupportsUint16 ? [new Uint16Array([1, 2, 3])] : null
    +].filter(Boolean);
     
     tape("hash is the same as node's crypto", function (t) {
     	inputs.forEach(function (v) {
    @@ -35,7 +43,7 @@ tape('call update multiple times', function (t) {
     		var sha1hash = crypto.createHash('sha1');
     
     		for (var i = 0; i < v[0].length; i = (i + 1) * 2) {
    -			var s = v[0].substring(i, (i + 1) * 2);
    +			var s = v[0].slice(i, (i + 1) * 2);
     			hash.update(s, v[1]);
     			sha1hash.update(s, v[1]);
     		}
    @@ -74,7 +82,7 @@ tape('hex encoding', function (t) {
     		var sha1hash = crypto.createHash('sha1');
     
     		for (var i = 0; i < v[0].length; i = (i + 1) * 2) {
    -			var s = v[0].substring(i, (i + 1) * 2);
    +			var s = v[0].slice(i, (i + 1) * 2);
     			hash.update(Buffer.from(s, 'ascii').toString('hex'), 'hex');
     			sha1hash.update(Buffer.from(s, 'ascii').toString('hex'), 'hex');
     		}
    @@ -88,6 +96,29 @@ tape('hex encoding', function (t) {
     	t.end();
     });
     
    +tape('throws on invalid input', function (t) {
    +	var invalid = [
    +		{}, // non-arrayish
    +		{ length: 20 }, // undefined values
    +		[NaN], // non-numbers
    +		[[]], // non-numbers
    +		[1, 1.5], // non-integers
    +		[1, 256], // out of bounds
    +		[-1, 0] // out of bounds
    +	];
    +
    +	invalid.forEach(function (input) {
    +		var hash = new Sha1();
    +
    +		t['throws'](function () {
    +			hash.update(input);
    +			hash.digest('hex');
    +		});
    +	});
    +
    +	t.end();
    +});
    +
     tape('call digest for more than MAX_UINT32 bits of data', function (t) {
     	var sha1hash = crypto.createHash('sha1');
     	var hash = new Sha1();
    

Vulnerability mechanics

Synthesis attempt was rejected by the grounding validator. Re-run pending.

References

7

News mentions

0

No linked articles in our index yet.