Google Chrome V8 - Object Allocation Size Integer Overflow

EDB-ID:

44584




Platform:

Multiple

Date:

2018-05-04


Become a Certified Penetration Tester

Enroll in Penetration Testing with Kali Linux and pass the exam to become an Offensive Security Certified Professional (OSCP). All new content for 2020.

GET CERTIFIED

There's an integer overflow in computing the required allocation size when instantiating a new javascript object. 

See the following code in objects.cc

// static
bool JSFunction::CalculateInstanceSizeForDerivedClass(
    Handle<JSFunction> function, InstanceType instance_type,
    int requested_embedder_fields, int* instance_size,
    int* in_object_properties) {
  Isolate* isolate = function->GetIsolate();
  int expected_nof_properties = 0;
  bool result = true;
  for (PrototypeIterator iter(isolate, function, kStartAtReceiver);
       !iter.IsAtEnd(); iter.Advance()) {
    Handle<JSReceiver> current =
        PrototypeIterator::GetCurrent<JSReceiver>(iter);
    if (!current->IsJSFunction()) break;
    Handle<JSFunction> func(Handle<JSFunction>::cast(current));
    // The super constructor should be compiled for the number of expected
    // properties to be available.
    Handle<SharedFunctionInfo> shared(func->shared());
    if (shared->is_compiled() ||
        Compiler::Compile(func, Compiler::CLEAR_EXCEPTION)) {
      DCHECK(shared->is_compiled());
      expected_nof_properties += shared->expected_nof_properties(); // <--- overflow here!
    } else if (!shared->is_compiled()) {
      // In case there was a compilation error for the constructor we will
      // throw an error during instantiation. Hence we directly return 0;
      result = false;
      break;
    }
    if (!IsDerivedConstructor(shared->kind())) {
      break;
    }
  }
  CalculateInstanceSizeHelper(instance_type, true, requested_embedder_fields,
                              expected_nof_properties, instance_size,
                              in_object_properties);
  return result;
}

By supplying a long prototype chain of objects with a large expected_nof_properties we can control the resulting value of instance_size by causing (requested_embedder_fields + requested_in_object_properties) << kPointerSizeLog2 to be overflown to a small negative value, resulting in an allocation smaller than header_size, which is the minimum required size for the base object class being allocated. This results in memory corruption when the object is initialised/used.

void JSFunction::CalculateInstanceSizeHelper(InstanceType instance_type,
                                             bool has_prototype_slot,
                                             int requested_embedder_fields,
                                             int requested_in_object_properties,
                                             int* instance_size,
                                             int* in_object_properties) {
  int header_size = JSObject::GetHeaderSize(instance_type, has_prototype_slot);
  DCHECK_LE(requested_embedder_fields,
            (JSObject::kMaxInstanceSize - header_size) >> kPointerSizeLog2);
  *instance_size =
      Min(header_size +
              ((requested_embedder_fields + requested_in_object_properties)
               << kPointerSizeLog2),
          JSObject::kMaxInstanceSize);
  *in_object_properties = ((*instance_size - header_size) >> kPointerSizeLog2) -
                          requested_embedder_fields;
}

The attached PoC crashes current stable on linux.

See crash report ID: 307546648ba8a84a

Chrome issue is https://bugs.chromium.org/p/chromium/issues/detail?id=808192

Attaching the working exploit for this issue.

Note that issue_808192.html is a template - it requires server.py to do a version check and patch a few version dependent constants in, since some object layouts have changed during the range of Chrome versions on which the exploit was tested.


Proof of Concept:
https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/44584.zip