Skip to content

(ReDoS) Regular Expression Denial of Service in tf2-item-format

High
danocmx published GHSA-8h55-q5qq-p685 Jul 23, 2024

Package

npm tf2-item-format (npm)

Affected versions

<5.9.14

Patched versions

5.9.14

Description

Summary

Versions of tf2-item-format since at least 4.2.6 are vulnerable to a Regular Expression Denial of Service (ReDoS) attack when parsing crafted user input.

Tested Versions

  • 5.9.13
  • 5.8.10
  • 5.7.0
  • 5.6.17
  • 4.3.5
  • 4.2.6

Mitigation

v5

Upgrade package to ^5.9.14

v4

No patch exists. Please consult the v4 to v5 migration guide to upgrade to v5.

If upgrading to v5 is not possible, fork the module repository and implement the fix detailed below.

Details

All mentions of source code are based on the 5.9.13 version of the package.

The error originates from src/shared/decomposeName.ts Lines 54-57:

itemName = itemName.replace(
  new RegExp(`(( ${toRemove})|(${toRemove} ))`),
  ''
);

since both itemName and toRemove originate from user input, this can be exploited to perform a ReDoS attack.

This ReGeX is only called under the following conditions:

  • attributes.usableItem is truthy
  • either attributes.usableItem.output or attributes.usableItem.target are falsy

under these conditions, itemName is is unmodified from user input, and toRemove is created by the getUsableItemToRemove() function, which can return one of three possible strings:

  • "{attributes.usableItem.target}"
  • "{attributes.usableItem.target.output}"
  • "{attributes.usableItem.outputQuality} {usableItem.output}"

decomposeName() is used on two API functions: parseString() (src/parseString.ts) and ParsedEcon.itemName.getShort() (src/parseEconItem/ParsedEcon/ItemName.ts). Since the latter typically only handles input from Steam, it was not investigated as a potential attack vector.

When parseString() is called, the attributes object is created from the constructor of the Attributes class, where the usableItem field is populated by getUsableItem() (src/parseString/Attributes/getUsableItem.ts).

Since malicious payloads need to ensure either attributes.usableItem.output or attributes.usableItem.target are falsy, either isChemistrySet() or getItemIfTarget() must not return a falsy value. Since the getItemIfTarget() was easier to exploit, it was used in the PoC. It is highly possible that isChemistrySet() is also vulnerable to a similar attack.

src/parseString/Attributes/getUsableItem.ts Lines 34-45

const item = getItemIfTarget(name);
if (item) {
  return {
    target: name
      .replace(` ${item}`, '')
      .replace(`${getKillstreak(name)} `, '')
      // Incase its uncraftable
      .replace('Non-Craftable ', '')
      // For Unusualifiers
      .replace('Unusual ', ''),
  };
}

Here, name is unmodifed user input.

If an input of form "<anything> Kit" is passed to getItemIfTarget(), the function will return "Kit", which will result in attributes.usableItem.target being set to "<anything>" and attributes.usableItem.output being undefined.

Now, since attributes.usableItem.target is defined, getUsableItemToRemove() will return "<anything>", setting toRemove to "<anything>".

In short, due to the above, a payload of form "<anything> Kit" cause a Regex of form
/(( <anything>)|(<anything> ))/ to be created and executed on "<anything> Kit".

This exposes the possibility of ReDoS through catastrophic backtracking.

If <anything> is set a specially crafted payload, the Regex will take an exponential amount of time to execute, causing the server to hang.

For the PoC, the payload )|^.{23}(a+)+$| Kit |( aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZ
was used.

  • )| is used to isolate the malicious pattern
  • ^.{23} causes the malicious pattern to ignore itself and "Kit"
  • (a+)+$ This is a malicious Regex pattern that will cause catastrophic backtracking upon encountering a string of form aaa...aaaZ
  • | is used to isolate the malicious pattern
  • Kit is used to ensure this payload is converted to Regex as shown above.
  • |( is used to avoid causing a Regex syntax error.
  • a space is used to make sure the aaa...aaaZ cannot match itself, as this create a group.
  • aaa...aaaZ is used to trigger the catastrophic backtracking.

PoC

v4

const { parseString } = require( "tf2-item-format");
const payload = ")|^.{23}(a+)+$| Kit |( aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZ";
parseString(payload , true, true);

v5

const { parseString } = require( "tf2-item-format/static");
const payload = ")|^.{23}(a+)+$| Kit |( aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZ";
parseString(payload , true, true);

Remediation

In src/shared/decomposeName.ts Lines 54-57:

itemName = itemName.replace(
  new RegExp(`(( ${toRemove})|(${toRemove} ))`),
  ''
);

should be updated to

itemName = itemName.replace(`${toRemove} `, '');
itemName = itemName.replace(` ${toRemove}`, '');

Impact

This vulnerability can be exploited by an attacker to perform DoS attacks on any service that uses any tf2-item-format to parse user input.

Credit

  • piman51277

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
None
Availability
High

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

CVE ID

CVE-2024-41655

Weaknesses

Credits