1 पॉइंट द्वारा GN⁺ 2024-08-10 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • Google ने Buzzer की मदद से eBPF verifier की CVE-2023-2163 भेद्यता खोजी, और पुष्टि की कि इसका दुरुपयोग local privilege escalation और container escape के लिए किया जा सकता है
  • eBPF रनटाइम पर कर्नेल की क्षमताओं को बढ़ाता है, लेकिन क्योंकि यह उच्च विशेषाधिकार स्तर पर मनमाना bytecode चलाता है, इसलिए लोड से पहले verifier validation सुरक्षा का मुख्य आधार बन जाता है
  • समस्या तब हुई जब verifier का path pruning अलग execution path को गलती से पहले से सुरक्षित माने गए path के समान समझ बैठा
  • exploit ने verifier द्वारा 0 माने गए register और रनटाइम पर उसके वास्तविक अलग मान के बीच के अंतर का उपयोग करके eBPF stack pointer को दूषित किया, जिससे arbitrary read/write और KASLR bypass संभव हुआ
  • patch में precise register को प्रभावित करने वाले imprecise register को भी precise के रूप में चिह्नित किया गया, और उसी pointer arithmetic fuzzing strategy में बाद में कोई अतिरिक्त issue नहीं मिला

eBPF verifier से बढ़ता कर्नेल attack surface

  • eBPF एक ऐसी तकनीक है जो जटिल kernel module के बिना भी Linux कर्नेल की क्षमताओं को रनटाइम पर बढ़ाने देती है
  • eBPF प्रोग्राम custom bytecode में लिखे जाते हैं और किसी विशेष event के होने पर चलने से पहले उनकी safety verification की जाती है
    • एक सामान्य उदाहरण वह eBPF प्रोग्राम है जो किसी खास syscall call पर चलता है
  • यह संरचना उच्च privilege level पर arbitrary code चलाने देती है, इसलिए कर्नेल का attack surface काफी बढ़ जाता है
  • प्रोग्राम को लोड होने से पहले verifier से गुजरना पड़ता है, और verifier यह जांचता है कि eBPF की security assumptions पूरी हो रही हैं या नहीं
  • verifier की भेद्यता का दुरुपयोग होने पर आम तौर पर local privilege escalation या container environment में container escape होता है

Buzzer और pointer arithmetic fuzzing

  • Google ने eBPF verifier code का स्वचालित audit करने के लिए Buzzer बनाया
  • Buzzer एक fuzzer है जो बड़े पैमाने पर syntactically valid eBPF प्रोग्राम बनाता है, और logic bug पैदा करने के लिए strategy सेट कर सकता है
  • CVE-2023-2163 की खोज में pointer arithmetic strategy का उपयोग किया गया
    • यह register को random values से initialize करने वाला header बनाता है
    • यह random arithmetic और jump instruction sequence बनाता है
    • यह कोई random register चुनकर eBPF map element pointer के साथ addition operation करता है
    • यह उस element में magic value लिखता है
    • अगर user space में लिखा गया value दिखाई नहीं देता, तो out-of-bounds write होने की संभावना होती है
  • Buzzer द्वारा खोजी गई CVE-2023-2163 eBPF path pruning logic में थी, और इससे ऐसा exploit बना जो container escape और local privilege escalation दोनों के लिए इस्तेमाल हो सकता है

path pruning और precise tracking bug

  • eBPF verifier यह जांचने के लिए संभावित execution path का simulation करता है कि प्रोग्राम सुरक्षित रूप से चल सकता है या नहीं
  • conditional branch में अगर register value निश्चित नहीं हो, तो verifier सभी संभावित state execution path को follow करने की कोशिश करता है
  • conditional jump बढ़ने पर execution path की संख्या exponential रूप से बढ़ती है, और इससे कर्नेल में प्रोग्राम लोड करने की performance पर भी बुरा असर पड़ता है
  • इसे कम करने के लिए eBPF developers ने path pruning पेश किया
    • अगर verifier यह सुनिश्चित कर सके कि किसी विशेष state के समान state पहले ही सुरक्षित रूप से exit instruction तक पहुंच चुकी है, तो वह उस path को आगे explore नहीं करता
  • अधिक कुशल pruning के लिए precise tracking की अवधारणा भी साथ में उपयोग होती है
    • अगर कोई register pointer arithmetic operation में शामिल हो या helper function को constant के रूप में दिया जाए, तो उसे precise के रूप में mark किया जाता है
    • verifier को उस register से जुड़ी सभी state explore करनी पड़ती हैं
  • CVE-2023-2163 में r6 की precision को प्रभावित करने वाला r9 सही तरह से mark नहीं हुआ था
    • verifier ने मान लिया कि r9, r6 की preciseness में योगदान नहीं देता
    • उसने पहले वाले path पर r6 के साथ सुरक्षित रूप से exit तक पहुंचने का निर्णय लिया, और बाकी state को समान मानकर pruning कर दी
    • रनटाइम पर 1:2:4:6 path execute हुआ, और 6 पर verifier द्वारा 0 माने गए r6 का वास्तविक मान अलग था, जिससे उसे pointer arithmetic operation में इस्तेमाल किया जा सका

arbitrary read/write और KASLR bypass

  • exploit code Google security research repository में सार्वजनिक है
  • exploit development में @chompie और @_manfp के काम का महत्वपूर्ण उपयोग हुआ
  • कुल flow यह है कि पहले arbitrary read/write हासिल किया जाता है, फिर process credentials खोजकर uid और fs_struct pointer को patch करके privilege बढ़ाया जाता है
  • पहले चरण में दूषित register value को 1 बनाया जाता है
    • manual analysis में रनटाइम r6 value 0x400 थी, जबकि verifier ने उसे 0 माना
    • r6 >>= 10 instruction से वांछित value 1 बनाई गई
  • इसके बाद bpf_skb_load_bytes_relative helper function से eBPF stack के pointer को दूषित किया गया
    • verifier ने माना कि len 8 है, लेकिन वास्तविक रनटाइम में r6 value की वजह से len 9 हो गया
    • नतीजतन 8 byte की जगह 9 byte लिखे गए और stack offset -32 value का पहला byte दूषित हो गया
  • दूषित stack pointer को manipulate करने पर eBPF map pointer leak कराया जा सकता है
    • user space में R2 और R3 value पढ़ी जाती हैं, और देखा जाता है कि R2 0xBACA बनता है या नहीं
    • अगर यह शर्त पूरी हो, तो R3 map pointer leak value बन जाता है
    • eBPF map pointer leak होने से KASLR bypass की स्थिति बन जाती है
  • इसी strategy से, एक single byte के बजाय लगातार stack क्षेत्र को मनचाहे pointer से overwrite कर दिया जाए, तो arbitrary read/write संभव हो जाता है
    • verifier के नज़रिए से यह BPF stack manipulation है, लेकिन वास्तव में इससे kernel memory पढ़ी और लिखी जा सकती है

map leak से root shell तक

  • map pointer leak के बाद exploit, Chompie के exploit से बहुत अलग नहीं है, और कुछ code वहीं से लिया गया है
  • मोटे तौर पर प्रक्रिया इस प्रकार है
    • kstrtab में init_pid_ns string को बार-बार search किया जाता है
    • उस string को refer करने वाला ksymtab symbol ढूंढकर init_pid_ns structure address प्राप्त किया जाता है
    • radix tree traverse करके वह entry खोजी जाती है जिसका comm field चल रहे exploit executable के नाम से मेल खाता हो
      • container के अंदर चलते समय PID भरोसेमंद heuristic नहीं होता, इसलिए केवल PID का उपयोग नहीं किया जाता
    • uid को 0 में patch किया जाता है और fs_struct pointer को patch किया जाता है
    • अगर fs_struct को PID 1 द्वारा refer किए गए pointer के समान value से patch किया जाए, तो container के अंदर चलते समय host file system को देखा जा सकता है
    • system("/bin/bash") चलाकर root shell प्राप्त किया जाता है
  • GitHub पर प्रकाशित code केवल कुछ खास Linux version पर ही container escape के रूप में काम करता है
  • Ubuntu और कुछ अन्य distributions में overwrite किए गए data structure के offset अलग होने के कारण यह केवल local privilege escalation के रूप में काम करता है
  • इसे किसी भी Linux distribution पर काम करने लायक बनाने के लिए code adjustment की जरूरत है

patch और बाद की verification

  • CVE-2023-2163 के root cause analysis और patch को kernel mailing list में देखा जा सकता है
  • fix यह है कि precise register को प्रभावित करने वाले operation के imprecise register को भी precise mark किया जाए
  • इस fix का eBPF verifier performance पर क्या असर पड़ता है, यह स्पष्ट नहीं है
  • उसी pointer arithmetic fuzzing strategy को लगातार चलाने पर भी कोई अतिरिक्त issue नहीं मिला
  • Buzzer का विकास जारी है, और open source community से योगदान GitHub repo के जरिए लिया जा रहा है

1 टिप्पणियां

 
GN⁺ 2024-08-10
Hacker News की राय
  • जिन प्लेटफॉर्मों पर eBPF सबसे आम तौर पर इस्तेमाल होता है, वहाँ शुरुआत से ही unprivileged code eBPF programs लोड नहीं कर सकता, इसलिए verifier bugs का असर अक्सर बहुत बड़ा नहीं होता
    ऐसे bugs अंततः root → ring0 vulnerability होते हैं; इसका मतलब यह नहीं कि वे मामूली हैं, लेकिन server-side workloads में यह आम तौर पर स्वीकार्य trade-off होता है
    खासकर eBPF का kernel local privilege escalation इतिहास, पूरे kernel की तुलना में काफी अच्छा रहा है, और मौजूदा eBPF environment में verifier की सबसे बड़ी value यह है कि गलत eBPF program से kernel को गलती से crash कर देना मुश्किल हो जाता है
    सामान्य loadable kernel modules के मामले में यह बात हास्यास्पद हद तक लागू नहीं होती

    • PoC out-of-bounds pointer से eBPF map में लिखता है, लेकिन अगर यह सिर्फ scalar value range tracking error है, तो seccomp के जरिए load किए जा सकने वाले non-extended BPF program से भी इसे exploit किया जा सकता है
      ऐसे में ज्यादातर platforms पर privileges की जरूरत नहीं होती
      और अगर unprivileged user namespaces हों, तो आप खुद “root” बन सकते हैं, इसलिए root → ring0 भी कम restrictive समस्या बन जाती है
      distributions ने इन्हें on किया और फिर ज्यादातर वापस off कर दिया, उसके बाद आए eBPF bug PoC में यही pattern बार-बार दिखता रहा है
    • यह भी नहीं भूलना चाहिए कि container को CAP_BPF दिया जा सकता है
      Cilium जैसे tools जितने बड़े होते जाएंगे, cap_bpf वाले container environment में entry पाने वाले attack paths उतने ही अधिक realistic होते जाएंगे
    • verifier bugs को ठीक करना अहम है, क्योंकि unprivileged eBPF usage को safe बनाने के लिए यह prerequisite है
  • “Uno no es ninguno” का शाब्दिक अर्थ “एक शून्य नहीं है”, यानी “One is not none” के करीब है

    • Spanish में double negative अक्सर सचमुच double negative नहीं होता
      उदाहरण के लिए “यहाँ कुछ भी नहीं है” को “no hay nada aquí” कहते हैं, जिसे शब्द-दर-शब्द अनुवाद करें तो यह “यहाँ कुछ भी नहीं नहीं है” जैसा दिख सकता है
      Royal Spanish Academy भी इसे ऐसे समझाती है:

      https://www.rae.es/espanol-al-dia/doble-negacion-no-vino-nad...

      तथाकथित “double negative” Spanish और दूसरी Romance languages में कुछ स्थितियों में जरूरी negative concord की वजह से पैदा होता है, जिसके परिणामस्वरूप adverb no और negative meaning रखने वाला कोई दूसरा element एक ही sentence में साथ दिखते हैं
      ये दो “negatives” साथ हों तो भी sentence का negative meaning cancel नहीं होता

  • पहले जब मैंने eBPF इस्तेमाल करने की कोशिश की थी, तो जो काम चाहिए था उसके लिए इसकी expressiveness कम पड़ी
    सीमित flexibility पाने के लिए kernel-space complexity बढ़ाना सचमुच उचित है या नहीं, इस पर मुझे संदेह है
    packet filtering के लिए तो समझ आता है, लेकिन sandboxing जैसे दूसरे use cases में इसका इस्तेमाल कम convincing लगता है

    • ऐसे use cases के लिए DTrace जैसी दूसरी technologies भी हैं
      kernel के options eBPF या कुछ भी नहीं नहीं हैं, बल्कि eBPF या उसी जैसा कोई दूसरा विकल्प हैं
      हो सकता है आप इसे खुद बहुत ज्यादा न इस्तेमाल करें, लेकिन कुछ लोग इसे पूरे दिन इस्तेमाल करते हैं
      लगता है FAANG engineers ने कहा था कि वे हर server पर हमेशा ऐसे दर्जनों, शायद सैकड़ों programs चलाते हैं, और इसमें one-off usage शामिल नहीं है
      FAANG dedicated kernel developers भी hire करता है, इसलिए वे जिस complexity का उपयोग करते हैं, उसे fund भी करते हैं
      मैंने भी eBPF से problems solve की हैं
      eBPF के बिना वे समस्याएँ kernel expert न होने वाले व्यक्ति के लिए असल में unsolvable थीं; अक्सर जरूरत नहीं पड़ती, लेकिन जब जरूरत पड़ती है तो इसका कोई विकल्प नहीं होता
      कुछ मामलों में kernel expert के लिए भी चुनाव यह होता है कि eBPF इस्तेमाल करें या custom kernel patch हमेशा maintain करते रहें
    • क्या पारंपरिक loadable kernel-mode driver patch या eBPF से बेहतर नहीं होगा?
      मुझे पता है कि यह unsafe है, लेकिन जो लोग इसे handle करते हैं वे जानते हैं कि बड़ी power के साथ responsibility आती है
  • मुझे लगता है “Uno no es ninguno” का अनुवाद “One is not none” होना सही है

    https://bughunters.google.com/blog/6303226026131456/a-deep-d...

    • लेकिन इसे “One is none” अनुवाद किया गया है
      यह वही कुख्यात double negative है जिससे foreign language speakers, मेरे सहित, जूझते हैं

      https://spanish.stackexchange.com/questions/26777/how-does-d...

    • literal translation तो वही है, लेकिन Spanish में अजीब तरह से double negative आम तौर पर बस negative के रूप में इस्तेमाल होता है

    • शायद “one ain't nothin'” जैसा अनुवाद और सही बैठे

  • हमारे यहाँ “पैंट के अंदर साही” जैसा एक expression है
    चाहे यह कितना भी उपयोगी हो, यह safe और carefully लिखा हुआ नहीं लगता

    • experience बढ़ने पर पता चलता है कि safe और carefully किया गया हो तब भी गलतियाँ हो सकती हैं