Skip to content
KitploitKITPLOIT
उपकरणब्लॉग
जमा करें
उपकरणब्लॉग
जमा करें

हैकिंग, पेनटेस्ट और साइबर सुरक्षा उपकरण आपके सुरक्षा शस्त्रागार के लिए!

Kitploit हैकिंग, साइबर सुरक्षा और पेंटेस्टिंग टूल्स की एक निर्देशिका है। कमजोरियों को खोजने, सिस्टम का विश्लेषण करने, परीक्षण को स्वचालित करने और अपनी सुरक्षा को मजबूत करने के लिए नवीनतम प्रोजेक्ट अपडेट खोजें।

··फ़ीड·संपर्क·गोपनीयता·© 2026 Kitploit

टूल निर्देशिका

श्रेणियाँ

सभी श्रेणियाँ देखें
Loading categories
CVE-2023-6702 — क्रोम रेंडरर 1day RCE एसिंक स्टैक ट्रेस में टाइप कन्फ्यूज़न के माध्यम से (v8ctf सबमिशन) | Kitploit
उपकरण/GitHubGitHub/kaist-hacking/cve-2023-6702
भेद्यता विश्लेषणशोषणवेब एप्लिकेशन शोषणCTFलर्निंग और शिक्षाबाइनरी शोषण
GitHubkaist-hacking/cve-2023-6702

CVE-2023-6702

क्रोम रेंडरर 1day RCE एसिंक स्टैक ट्रेस में टाइप कन्फ्यूज़न के माध्यम से (v8ctf सबमिशन)

रिपॉजिटरी देखें
8692 साल पहलेKitploit द्वारा समीक्षित

सबसे लोकप्रिय

सभी देखें →

हमारे समुदाय द्वारा सबसे अधिक उपयोग किए जाने वाले उपकरण खोजें।

सभी उपकरण खोजें

हमारे उपकरणों का संग्रह ब्राउज़ करें

सभी उपकरण देखें →
साझा करें

क्रोम रेंडरर 1day RCE एशिंक स्टैक ट्रेस में टाइप कन्फ्यूज़न के माध्यम से (CVE-2023-6702)

सारांश

यह भेद्यता एक दूरस्थ हमलावर को क्रोम रेंडरर प्रक्रिया के अंदर मनमाना कोड निष्पादित करने की अनुमति देती है।

एशिंक स्टैक ट्रेस हैंडलिंग कोड में अपर्याप्त प्रकार जाँच थी। यह FunctionContext और NativeContext के बीच टाइप कन्फ्यूज़न की ओर ले जाता है, जिससे JSGlobalProxy->hash मान तक अवैध पहुँच होती है। हीप स्प्रेइंग के साथ, हमलावर एक नकली एशिंक स्टैक फ्रेम इंजेक्ट करने और fakeobj प्रिमिटिव का निर्माण करने में सक्षम था। fakeobj प्रिमिटिव का उपयोग करके, हमलावर क्रोम रेंडरर प्रक्रिया में मनमाना कोड निष्पादन प्राप्त करने में सक्षम था।

आप हमारे TyphoonCon 2024 स्लाइड्स देख सकते हैं।

विक्रेता / उत्पाद / संस्करण

  • गूगल क्रोम
  • प्रभावित संस्करण: 120.0.6099.109 से पहले
  • स्थिर संस्करण: 120.0.6099.109

समयरेखा

  • 2020-05-13: Bug Introduced - [Promise.any] Implment async stack traces for Promise.any
  • 2023-11-10: Bug Report - Security: V8 Debug check failed: LAST_TYPE >= value
  • 2023-11-15: Patch - [promises, async stack traces] Fix the case when the closure has run
  • 2023-12-12: Advisory - https://chromereleases.googleblog.com/2023/12/stable-channel-update-for-desktop_12.html
  • 2024-01-12: v8CTF सबमिशन <-- वह समय जब हमने इस भेद्यता पर काम किया
  • 2024-02-23: बग रिपोर्ट का खुलासा

पृष्ठभूमि

एशिंक स्टैक ट्रेस

एशिंक्रोनस जावास्क्रिप्ट में सबसे महत्वपूर्ण विशेषताओं में से एक है। अतीत में, एशिंक्रोनस कोड को त्रुटि स्टैक के साथ डीबग करना कठिन था क्योंकि एशिंक फ़ंक्शन त्रुटि स्टैक में कैप्चर नहीं किए जाते थे। निलंबित एशिंक फ़ंक्शन कॉल स्टैक में नहीं बल्कि इवेंट लूप के कॉलबैक कतार में संग्रहीत होते हैं, इसलिए त्रुटि स्टैक में एशिंक फ़ंक्शन शामिल नहीं होता है। इस समस्या को हल करने के लिए, V8 त्रुटि स्टैक में एशिंक फ़ंक्शन को कैप्चर करने के लिए "async stack trace" सुविधा (V8 v7.3 से डिफ़ॉल्ट रूप से) प्रदान करता है। (v8 blog, v8 docs)

Promise.all रिज़ॉल्व एलिमेंट क्लोज़र

"Promise.all Resolve Element Closure" Promise.all फ़ंक्शन में इनपुट प्रॉमिसेज़ को हल करने के लिए एक सहायक फ़ंक्शन है। Promise.all फ़ंक्शन प्रॉमिसेज़ की एक सरणी लेता है और एक प्रॉमिस लौटाता है जो सभी इनपुट प्रॉमिसेज़ हल होने पर हल होता है। "Promise.all Resolve Element Closure" Promise.all फ़ंक्शन में प्रत्येक इनपुट प्रॉमिस का एक रिज़ॉल्व हैंडलर है। फ़ंक्शन की भूमिका इनपुट प्रॉमिस को हल करना और परिणाम सरणी में पूर्ति मान को संग्रहीत करना है।

फ़ंक्शन के बारे में 2 बिंदु ध्यान देने योग्य हैं:

  1. यह एक आंतरिक बिल्ट-इन फ़ंक्शन है और जावास्क्रिप्ट कोड से सीधे सुलभ नहीं है।
  2. फ़ंक्शन के संदर्भ का उपयोग यह जाँचने के लिए मार्कर के रूप में किया जाता है कि फ़ंक्शन निष्पादित किया गया है या नहीं। जब तक इसे कॉल नहीं किया जाता तब तक इसमें FunctionContext होता है, और इसे कॉल करने के बाद इसमें NativeContext होता है। (v8 कोड)

भेद्यता

बग वर्ग: FunctionContext और NativeContext के बीच टाइप कन्फ्यूज़न

भेद्यता विवरण:

भेद्यता को पहले से निष्पादित "Promise.all Resolve Element Closure" फ़ंक्शन या समान आंतरिक बिल्ट-इन फ़ंक्शन के साथ एक एशिंक स्टैक ट्रेस कैप्चर करके ट्रिगर किया जा सकता है। इस एक्सप्लॉइट में, मैंने "Promise.all Resolve Element Closure" फ़ंक्शन का उपयोग उदाहरण के रूप में किया।

जब जावास्क्रिप्ट कोड में कोई त्रुटि उत्पन्न होती है, V8 स्टैक से त्रुटि स्टैक कैप्चर करता है और वर्तमान माइक्रोटास्क से एशिंक स्टैक फ्रेम जोड़ता है [1]।

root@kitploit:~
CallSiteBuilder builder(isolate, mode, limit, caller);
VisitStack(isolate, &builder);

// If --async-stack-traces are enabled and the "current microtask" is a
// PromiseReactionJobTask, we try to enrich the stack trace with async
// frames.
if (v8_flags.async_stack_traces) {
    CaptureAsyncStackTrace(isolate, &builder);
}

CaptureAsyncStackTrace फ़ंक्शन [2] प्रॉमिस श्रृंखला को देखता है और एशिंक कॉल प्रकार (जैसे, await, Promise.all, Promise.any) के अनुसार एशिंक स्टैक फ्रेम जोड़ता है।

नीचे CaptureAsyncStackTrace फ़ंक्शन का स्निपेट है जो Promise.all मामले को संभालता है:

root@kitploit:~
} else if (IsBuiltinFunction(isolate, reaction->fulfill_handler(),
                                Builtin::kPromiseAllResolveElementClosure)) {
    Handle<JSFunction> function(JSFunction::cast(reaction->fulfill_handler()),
                                isolate);
    Handle<Context> context(function->context(), isolate);
    Handle<JSFunction> combinator(context->native_context()->promise_all(),
                                isolate);
    builder->AppendPromiseCombinatorFrame(function, combinator);

    // Now peak into the Promise.all() resolve element context to
    // find the promise capability that's being resolved when all
    // the concurrent promises resolve.
    int const index =
        PromiseBuiltins::kPromiseAllResolveElementCapabilitySlot;
    Handle<PromiseCapability> capability(
        PromiseCapability::cast(context->get(index)), isolate);
    if (!IsJSPromise(capability->promise())) return;
    promise = handle(JSPromise::cast(capability->promise()), isolate);
} else if (

प्रॉमिस श्रृंखला को देखते समय, यदि reaction->fulfill_handler "Promise.all Resolve Element Closure" बिल्ट-इन फ़ंक्शन है, तो यह त्रुटि स्टैक में एशिंक प्रॉमिस कॉम्बिनेटर फ्रेम जोड़ता है। फिर, यह function->context->capability->promise तक पहुँचकर अगले प्रॉमिस पर जाता है।

समस्या यह है कि फ़ंक्शन मान लेता है कि "Promise.all Resolve Element Closure" फ़ंक्शन अभी तक निष्पादित नहीं हुआ है। यदि "Promise.all Resolve Element Closure" फ़ंक्शन पहले ही निष्पादित हो चुका है, तो संदर्भ FunctionContext से NativeContext में बदल जाता है। यह CaptureAsyncStackTrace फ़ंक्शन में FunctionContext और NativeContext के बीच टाइप कन्फ्यूज़न की ओर ले जाता है।

PoC बनाना:

भेद्यता को ट्रिगर करने की रणनीति इस प्रकार है:

  1. "Promise.all Resolve Element Closure" फ़ंक्शन प्राप्त करें जो एक आंतरिक बिल्ट-इन फ़ंक्शन है।
  2. संदर्भ को FunctionContext से NativeContext में बदलने के लिए "Promise.all Resolve Element Closure" फ़ंक्शन को स्पष्ट रूप से कॉल करें।
  3. "Promise.all Resolve Element Closure" फ़ंक्शन को एक नई प्रॉमिस श्रृंखला के साथ एक प्रॉमिस के फ़ुलफ़िल हैंडलर के रूप में सेट करें।
  4. प्रॉमिस श्रृंखला में एक त्रुटि उत्पन्न करें और एशिंक स्टैक ट्रेस कैप्चर करें।

मैंने JS स्क्रिप्ट स्तर पर "Promise.all Resolve Element Closure" फ़ंक्शन प्राप्त करने के लिए Promise.all के लिए सिंक्रोनस प्रॉमिस रिज़ॉल्विंग पैटर्न का उपयोग किया। मैंने test262 परीक्षण मामलों से पैटर्न उधार लिया।

फ़ंक्शन को स्पष्ट रूप से कॉल करने के बाद, भेद्यता को ट्रिगर करने के लिए, मैंने एक नई प्रॉमिस श्रृंखला तैयार करने और आंतरिक बिल्ट-इन फ़ंक्शन को एक प्रॉमिस के फ़ुलफ़िल हैंडलर के रूप में सेट करने के लिए शून्य-लागत एशिंक स्टैक ट्रेस दस्तावेज़ में नमूना कोड का उपयोग किया।

अंत में, जब त्रुटि उत्पन्न होती है, तो एशिंक स्टैक ट्रेस पहले से निष्पादित "Promise.all Resolve Element Closure" फ़ंक्शन के साथ फ़ुलफ़िल हैंडलर के रूप में कैप्चर किया जाता है, जिससे FunctionContext और NativeContext के बीच टाइप कन्फ्यूज़न होता है।

यहाँ PoC कोड है: poc.js

एक्सप्लॉइट

(एक्सप्लॉइट प्रिमिटिव, एक्सप्लॉइट रणनीति, एक्सप्लॉइट तकनीक और एक्सप्लॉइट प्रवाह की शर्तें यहाँ परिभाषित हैं।)

एक्सप्लॉइट प्रिमिटिव: fakeobj प्रिमिटिव

एक्सप्लॉइट रणनीति: टाइप कन्फ्यूज़न बग से fakeobj प्रिमिटिव बनाने के लिए, मैंने निम्नलिखित रणनीति का उपयोग किया:

  1. यादृच्छिक हैश संख्या को एक वैध JSPromise ऑब्जेक्ट पॉइंटर से मिलाने के लिए JSPromise ऑब्जेक्ट्स के साथ हीप स्प्रे करें।
  2. हैश मान को वैध JSPromise ऑब्जेक्ट पॉइंटर के रूप में उपयोग करें और नकली एशिंक स्टैक फ्रेम इंजेक्ट करें।
  3. नकली ऑब्जेक्ट प्राप्त करने के लिए getThis विधि के साथ Error.prepareStackTrace का उपयोग करें।

बग CaptureAsyncStackTrace फ़ंक्शन में FunctionContext और NativeContext के बीच टाइप कन्फ्यूज़न की ओर ले जाता है। यह अगले एशिंक स्टैक फ्रेम के निर्माण के लिए Context->PromiseCapability->JSPromise तक पहुँचता है। जब बग ट्रिगर होता है, तो यह NativeContext->JSGlobalProxy->hash तक पहुँचता है। बग का शोषण करने के लिए, मैंने हैश मान को JSPromise ऑब्जेक्ट पॉइंटर के रूप में उपयोग किया।

हम निम्नलिखित हैश उत्पन्न करने वाले फ़ंक्शन से जाँच सकते हैं कि हैश मान की सीमा (0, 0xfffff) है:

root@kitploit:~
int Isolate::GenerateIdentityHash(uint32_t mask) {
  int hash;
  int attempts = 0;
  do {
    hash = random_number_generator()->NextInt() & mask;
  } while (hash == 0 && attempts++ < 30);
  return hash != 0 ? hash : 1;
}
root@kitploit:~
pwndbg> p/x mask
$1 = 0xfffff

हैश मान SMI-टैग किया जाता है, इसलिए मेमोरी में, इसे hash << 1 के रूप में संग्रहीत किया जाएगा। इसलिए, मेमोरी में मान सम संख्या के साथ (0, 0xfffff << 1) की सीमा में होगा।

यादृच्छिक हैश संख्या को एक वैध JSPromise ऑब्जेक्ट पॉइंटर से मिलाने के लिए, हमें 2 बाधाएँ मिलीं:

  1. व्याख्या किया गया पॉइंटर पता विषम संख्या होना चाहिए।
  2. हमें सीमा (0, 0xfffff << 1) में हीप स्प्रे करना होगा।

बाधाओं का पालन करते हुए, मैंने पते को विषम बनाने के लिए JSPromise ऑब्जेक्ट्स को 8 बिट्स शिफ्ट-लेफ्ट करके हीप स्प्रे किया, और सीमा (0, 0xfffff << 1) में फिट होने के लिए छोटे फॉर-लूप का उपयोग किया।

यहाँ यादृच्छिक हैश संख्या को एक वैध ऑब्जेक्ट पॉइंटर से मिलाना काफी कम संभावना लगता है। विश्वसनीयता बढ़ाने के लिए, मैंने iframe तकनीक का उपयोग किया। क्रोम में साइट आइसोलेशन के कारण विभिन्न वेबसाइटों के पेज विभिन्न प्रक्रियाओं में चल रहे हैं। इसलिए, मैंने एक अलग डोमेन के साथ एक iframe बनाया, और मुख्य प्रक्रिया की क्रैश से बचने के लिए iframe में एक्सप्लॉइट चलाया।

प्रॉमिस श्रृंखला में अगले प्रॉमिस पर जाने के बाद, प्रोग्राम प्रॉमिस की वैधता की जाँच करता है और एशिंक कॉल प्रकार के अनुसार एशिंक स्टैक फ्रेम जोड़ने का प्रयास करता है।

root@kitploit:~
  while (!builder->Full()) {
    // Check that the {promise} is not settled.
    if (promise->status() != Promise::kPending) return;

    // Check that we have exactly one PromiseReaction on the {promise}.
    if (!IsPromiseReaction(promise->reactions())) return;
    Handle<PromiseReaction> reaction(
        PromiseReaction::cast(promise->reactions()), isolate);
    if (!IsSmi(reaction->next())) return;

    // Check if the {reaction} has one of the known async function or
    // async generator continuations as its fulfill handler.
    if (IsBuiltinFunction(isolate, reaction->fulfill_handler(),
                          Builtin::kAsyncFunctionAwaitResolveClosure) ||
        IsBuiltinFunction(isolate, reaction->fulfill_handler(),
                          Builtin::kAsyncGeneratorAwaitResolveClosure) ||
        IsBuiltinFunction(
            isolate, reaction->fulfill_handler(),
            Builtin::kAsyncGeneratorYieldWithAwaitResolveClosure)) {
      // Now peek into the handlers' AwaitContext to get to
      // the JSGeneratorObject for the async function.
      Handle<Context> context(
          JSFunction::cast(reaction->fulfill_handler())->context(), isolate);
      Handle<JSGeneratorObject> generator_object(
          JSGeneratorObject::cast(context->extension()), isolate);
      CHECK(generator_object->is_suspended());

      // Append async frame corresponding to the {generator_object}.
      builder->AppendAsyncFrame(generator_object);

हमने kAsyncFunctionAwaitResolveClosure मामला चुना क्योंकि AppendAsyncFrame फ़ंक्शन का पैरामीटर, generator_object, पूरी तरह से नियंत्रणीय है।

स्थितियों को पारित करने के लिए PromiseReaction, Function, Context, JSGeneratorObject जैसी उपयुक्त नकली वस्तुएँ स्थापित करके, हम builder->AppendAsyncFrame(generator_object) को कॉल करके अपना नकली एशिंक फ्रेम इंजेक्ट कर सकते हैं। हम टर्मिनल से इंजेक्ट किए गए नकली एशिंक फ्रेम की जाँच कर सकते हैं।

root@kitploit:~
Error: Let's have a look...
    at bar (../../../../fake_frame.js:168:15)
    at async foo (../../../../fake_frame.js:163:9)
    at async Promise.all (index 0)
    at async Array.sloppy_func (../../../../fake_frame.js:1:1)

यहाँ fake_frame.js कोड है।

नकली एशिंक फ्रेम इंजेक्ट करने के बाद, मैंने त्रुटि ऑब्जेक्ट का receiver प्राप्त करने के लिए getThis विधि के साथ Error.prepareStackTrace का उपयोग किया (इस मामले में, यह JSGeneratorObject है)। receiver के साथ, हम हीप से नकली ऑब्जेक्ट प्राप्त कर सकते हैं (fakeobj प्रिमिटिव)।

एक्सप्लॉइट प्रवाह: मैंने V8 एक्सप्लॉइट्स के लिए विशिष्ट शोषण प्रवाह का उपयोग किया।

  1. fakeobj प्रिमिटिव का उपयोग करके, मैंने नकली OOB सरणी लगाई और प्राप्त की।
  2. नकली OOB सरणी का उपयोग करके, मैंने caged_read/caged_write प्रिमिटिव बनाए।
  3. RCE की ओर, मैंने Google CTF 2023 से साझा की गई तकनीक का संदर्भ लिया। V8 सैंडबॉक्स से बचने के लिए, मैंने मनमाना बाइटकोड निष्पादित करने के लिए BytecodeArray ऑब्जेक्ट को दूषित किया। आउट-ऑफ-बाउंड्स एक्सेस के साथ Ldar/Star निर्देशों का उपयोग करके, हम स्टैक को पढ़/लिख सकते हैं। क्रोम बाइनरी बेस पता लीक करने के लिए, मैंने बेस पते के निचले 32 बिट्स को लीक करने के लिए स्टैक से एक रिटर्न पता पढ़ा, और पते के उच्च 16 बिट्स प्राप्त करने के लिए एक लिबक हीप पॉइंटर पढ़ा। फिर, मैंने स्टैक पिवोटिंग के लिए फ्रेम पॉइंटर को दूषित किया और RCE प्राप्त करने के लिए ROP चेन निष्पादित की।

यहाँ पूरा एक्सप्लॉइट कोड है: index.html और exploit.html इसे क्रोम 118.0.5993.70 पर परीक्षण किया गया है जो v8CTF M118 का लक्ष्य संस्करण था।

क्रेडिट्स

कैस्ट हैकिंग लैब के हेन ली

टूल डाउनलोड करें