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

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

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

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

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

श्रेणियाँ

सभी श्रेणियाँ देखें
Loading categories
CVE-2023-21768 — प्रूफ-ऑफ-कॉन्सेप्ट शोषण CVE-2023-21768 के लिए, एक विंडोज एंसिलरी फंक्शन ड्राइवर (AFD.sys) मनमाना कर्नेल लिखने की कमजोरी जो I/O रिंग के माध्यम से स्थानीय विशेषाधिकार वृद्धि को सक्षम करती है। | Kitploit
उपकरण/GitHubGitHub/h1bana/cve-2023-21768
विशेषाधिकार वृद्धिभेद्यता विश्लेषणशोषणबाइनरी शोषण
GitHubh1bana/cve-2023-21768

CVE-2023-21768

प्रूफ-ऑफ-कॉन्सेप्ट शोषण CVE-2023-21768 के लिए, एक विंडोज एंसिलरी फंक्शन ड्राइवर (AFD.sys) मनमाना कर्नेल लिखने की कमजोरी जो I/O रिंग के माध्यम से स्थानीय विशेषाधिकार वृद्धि को सक्षम करती है।

रिपॉजिटरी देखें
33 साल पहलेअभी तक समीक्षित नहीं

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

सभी देखें →

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

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

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

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

CVE-2023-21768

Windows Ancillary Function Driver for WinSock

CVE-2023-21768 के विस्तृत विवरण के अनुसार, जो Microsoft Security Response Center (MSRC) द्वारा प्रकाशित किया गया है, यह भेद्यता Ancillary Function Driver (AFD) में मौजूद है, जिसका सिस्टम फ़ाइल नाम afd.sys है। AFD मॉड्यूल WinSock API का कर्नेल एंट्री पॉइंट है। इस विश्लेषण में मैं इसका उपयोग विंडोज 11 पर विशेषाधिकार वृद्धि (LPE) के लिए करूंगा।

Patch Diff and Root Cause Analysis

दो संस्करण afd.sys डाउनलोड करें Winbindex से, एक संस्करण पैच से पहले का नवीनतम, और एक पैच के बाद का संस्करण। फिर इन दो संस्करणों की तुलना करने के लिए Bindiff का उपयोग करें। bindiff

दो संस्करणों की सामान्य तुलना से पता चलता है कि केवल एक फ़ंक्शन में अंतर है, वह है AfdNotifyRemoveIoCompletion। इस फ़ंक्शन के दो संस्करणों के बीच अंतर को अधिक विस्तार से देखें। bindiff

दोनों संस्करणों के बीच बहुत अधिक अंतर नहीं है। पोस्ट-पैच संस्करण में, पैरामीटर सेट करने और ProbeForWrite फ़ंक्शन को कॉल करने के लिए अतिरिक्त असेंबली निर्देश जोड़े गए हैं। Microsoft के दस्तावेज़ के अनुसार, यह फ़ंक्शन यह जांचने के लिए उपयोग किया जाता है कि कोई पता वास्तव में यूज़र-मोड का है, उसमें लिखने की अनुमति है, और सही ढंग से संरेखित (aligned) है या नहीं। इस कोड का अधिक विस्तृत विश्लेषण:

  • pre-patch afd.sys version 10.0.22621.608 code1

-post-patch afd.sys version 10.0.22621.1105 code2

दोनों r15_1 के मान की जाँच करते हैं, यदि यह 0 के बराबर है तो var_304 के मान को struct_1 के एक फ़ील्ड पर निर्दिष्ट पॉइंटर पर लिखते हैं। यदि यह 0 नहीं है, तो ProbeForWrite को कॉल किया जाएगा ताकि यह सुनिश्चित किया जा सके कि पॉइंटर एक वैध पते की ओर इशारा करता है। pre-patch संस्करण में, उसके बाद ही var_304 का मान पॉइंटर पर लिखा जाता है, यह जाँच गायब थी। इस पैच से, हम अनुमान लगा सकते हैं कि हम arg3_1->field_18 के नियंत्रित मान के साथ इस कोड को कॉल कर सकते हैं। यदि हम field_18 पर एक कर्नेल पता सेट कर सकते हैं, तो हम var_304 को कर्नेल मेमोरी क्षेत्र के पते पर लिख सकते हैं।

=> bug type: arbitrary kernel Write-Where

अब बग को ट्रिगर करने का तरीका खोजना होगा। फ़ंक्शन AfdNotifyRemoveIoCompletion को सीधे AfdNotifySock फ़ंक्शन में कॉल किया जाता है। crossRef

इसी तरह, AfdNotifySock का क्रॉस रेफरेंस ढूंढने पर हम देखते हैं कि इसे किसी अन्य फ़ंक्शन से सीधे कॉल नहीं किया जाता है, लेकिन फ़ंक्शन का पता .rdata में एक पते पर संग्रहीत है। cross2

यह पता AfdIrpCallDispatch से ठीक पहले स्थित है। cross3

बग को ट्रिगर करने के लिए, मैं DeviceIoControl को IOCTL_AFD_NOTIFY_SOCK के साथ कॉल करूंगा और AfdNotifySock को कॉल किया जाएगा।

root@kitploit:~
BOOL DeviceIoControl(
  [in]                HANDLE       hDevice,
  [in]                DWORD        dwIoControlCode,
  [in, optional]      LPVOID       lpInBuffer,
  [in]                DWORD        nInBufferSize,
  [out, optional]     LPVOID       lpOutBuffer,
  [in]                DWORD        nOutBufferSize,
  [out, optional]     LPDWORD      lpBytesReturned,
  [in, out, optional] LPOVERLAPPED lpOverlapped
);

reverse and debug

प्रत्येक ड्राइवर के लिए, कर्नेल में एक DRIVER_OBJECT ऑब्जेक्ट बनाया जाता है, इसे इस प्रकार परिभाषित किया गया है:

root@kitploit:~
typedef struct _DRIVER_OBJECT {
  CSHORT             Type;
  CSHORT             Size;
  PDEVICE_OBJECT     DeviceObject;
  ULONG              Flags;
  PVOID              DriverStart;
  ULONG              DriverSize;
  PVOID              DriverSection;
  PDRIVER_EXTENSION  DriverExtension;
  UNICODE_STRING     DriverName;
  PUNICODE_STRING    HardwareDatabase;
  PFAST_IO_DISPATCH  FastIoDispatch;
  PDRIVER_INITIALIZE DriverInit;
  PDRIVER_STARTIO    DriverStartIo;
  PDRIVER_UNLOAD     DriverUnload;
  PDRIVER_DISPATCH   MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];
} DRIVER_OBJECT, *PDRIVER_OBJECT;

अंतिम घटक MajorFunction एक सरणी है जिसमें ड्राइवर के डिस्पैच फ़ंक्शन होते हैं, जो कर्नेल और यूज़रमोड के बीच संचार को संभालते हैं। DeviceIoControl कॉल के अनुरूप डिस्पैच फ़ंक्शन MajorFunction[IRP_MJ_DEVICE_CONTROL] पर संग्रहीत होता है।

root@kitploit:~
#define IRP_MJ_DEVICE_CONTROL           0x0e

afd.sys के DriverEntry फ़ंक्शन से, हम देख सकते हैं कि ड्राइवर ने डिवाइस ऑब्जेक्ट "\Device\Afd" बनाया है: code3

यह MajorFunction[IRP_MJ_DEVICE_CONTROL] = AfdDispatchDeviceControl असाइन करता है, इसलिए जब कर्नेल के साथ संचार करने के लिए DeviceIoControl कॉल किया जाता है, तो यह इस फ़ंक्शन को कॉल करेगा। code4

AFD में दो डिस्पैच टेबल हैं: AfdIrpCallDispatch और AfdImmediateCallDispatch। dispatchtable1 dispatchtable2

यह देखना आसान है कि AfdDispatchDeviceIoControl IoControlCode के माध्यम से सबस्क्रिप्ट की गणना करता है और IoControlCode द्वारा सत्यापित करने के लिए AfdIoctlTable से सबस्क्रिप्ट के अनुरूप मान प्राप्त करता है। 1

AfdImmediateCallDispatch के प्रारंभ पते और AfdNotifySock को संग्रहीत करने वाले पते के बीच की दूरी से, हम इंडेक्स 73 की गणना करते हैं, जिसका नियंत्रण कोड 0x12127 है। ioctl

root@kitploit:~
int main() {
    WSADATA WSAData;
    SOCKET s;
    SOCKADDR_IN sa;
    int ierr;

    WSAStartup(0x2, &WSAData);
    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    memset(&sa, 0, sizeof(sa));
    sa.sin_port = htons(135);
    sa.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    sa.sin_family = AF_INET;
    ierr = connect(s, (const struct sockaddr*)&sa, sizeof(sa));

    char outBuf[100];
    DWORD bytesRet;
    DWORD inbuf1[100];

    memset(inbuf1, 0, sizeof(inbuf1));

    DeviceIoControl((HANDLE)s, 0x12127, (LPVOID)inbuf1, 0x30, outBuf, 0, &bytesRet, NULL);
    return 0;
}

it works!

bp1

जैसा कि शुरुआत में बताया गया था, भेद्यता तब होती है जब हम एक struct के माध्यम से एक अमान्य (unvalidated) पॉइंटर पास कर सकते हैं। यह struct सीधे usermode से DeviceIoControl के lpInBuffer के माध्यम से पास किया जाता है। फिर इसे चौथे पैरामीटर के रूप में AfdNotifySock में और तीसरे पैरामीटर के रूप में AfdNotifyRemoveIoCompletion में पास किया जाता है।

para1 para2 para3

चूंकि struct में क्या है यह ज्ञात नहीं है, इसलिए मैंने IDA को स्वचालित रूप से struct बनाने दिया। अब इस struct में डेटा पास करने और आवश्यक जाँचों को बायपास करने का तरीका खोजना होगा ताकि बग कोड तक पहुंचा जा सके। AfdNotifySock फ़ंक्शन से शुरू करते हैं:

check1

सबसे पहले, struct का आकार 0x30 बाइट्स होना चाहिए।

check2

वे मान जो शून्य नहीं होने चाहिए:

check3

एक और बात, डीबग करते समय मैंने देखा कि यह पिछले UserBuffer चेक पर विफल हो रहा था, इसलिए DeviceIoControl कॉल करते समय इस मान को NULL सेट किया गया। ऊपर दिए गए मान सेट करने के बाद, मैं चेक2 को पार करने में सक्षम हुआ।

debug1 debug2

अगली जाँच जिसे बायपास करना है:

check4

ObReferenceObjectByHandle को इस चेक को पास करने के लिए STATUS_SUCCESS लौटाना होगा। अर्थात, मुझे एक वैध हैंडल पास करना होगा। मैंने खोजने की कोशिश की लेकिन IoCompletionObjectType बनाने के तरीके के बारे में कोई जानकारी नहीं मिली। इसलिए मैंने इस विश्लेषण का पालन किया: https://securityintelligence.com/posts/patch-tuesday-exploit-wednesday-pwning-windows-ancillary-function-driver-winsock/. NtCreateIoCompletion फ़ंक्शन का उपयोग करके एक IoCompletionObjectType बनाएं और इसका हैंडल ObReferenceObjectByHandle में पास करें।

इस जाँच को बायपास करने के बाद, प्रोग्राम का प्रवाह एक लूप में चला जाता है। इस लूप में ऐसी कोई जगह नहीं है जो विफलता प्रवाह की ओर ले जाए, इसलिए मैंने बस dword20 पर मान को 0x1 सेट कर दिया ताकि लूप से बाहर निकल सकूं।

check5

लूप से बाहर निकलने के बाद, प्रोग्राम AfdNotifyRemoveIoCompletion को कॉल करेगा। AfdNotifyRemoveIoCompletion फ़ंक्शन का विश्लेषण जारी रखें:

check6

सबसे पहले प्रोग्राम struct के एक अन्य फ़ील्ड की जाँच करता है, वह शून्य नहीं होना चाहिए। फिर इसे 0x20 से गुणा किया जाता है, और struct के एक अन्य फ़ील्ड के साथ ProbeForWrite फ़ंक्शन को कॉल करने के लिए पैरामीटर के रूप में उपयोग किया जाता है। यहां केवल एक user-mode मेमोरी पते का उपयोग करने की आवश्यकता है जिसमें लिखने की अनुमति हो और dwLen = 1 हो। बग को ट्रिगर करने से पहले अंतिम जाँच यह है कि IoRemoveCompletion फ़ंक्शन को कॉल करने पर लौटाया गया मान STATUS_SUCCESS होना चाहिए। खोजने पर मुझे पता चला कि NtRemoveIoCompletion फ़ंक्शन कॉल करने पर IoRemoveCompletion को कॉल करता है। इस दस्तावेज़ के अनुसार, NtRemoveIoCompletion फ़ंक्शन एक "प्रतीक्षा कॉल" (waiting call) है और यह तब समाप्त होता है जब किसी निर्दिष्ट Io Completion Object में कम से कम एक पूर्ण रिकॉर्ड होता है। रिकॉर्ड तब जोड़ा जाता है जब I/O प्रक्रिया पूरी होती है।

root@kitploit:~
NtRemoveIoCompletion(
  IN HANDLE               IoCompletionHandle,
  OUT PULONG              CompletionKey,
  OUT PULONG              CompletionValue,
  OUT PIO_STATUS_BLOCK    IoStatusBlock,
  IN PLARGE_INTEGER       Timeout OPTIONAL );

इसके अलावा एक वैकल्पिक पैरामीटर Timeout है, जब टाइमआउट मान पहुंच जाता है तो फ़ंक्शन समाप्त हो जाता है। हालांकि, केवल टाइमआउट = 0 सेट करना फ़ंक्शन को वापस लौटने के लिए पर्याप्त नहीं है, बल्कि यह टाइमआउट त्रुटि कोड लौटाएगा। हम NtSetIoCompletion फ़ंक्शन का उपयोग करके IoCompletionObjectType में लंबित IO की गणना को 1 से बढ़ा सकते हैं और टाइमआउट से पहले NtRemoveIoCompletion फ़ंक्शन को समाप्त कर सकते हैं। कई प्रयासों के बाद, मैंने पाया कि लिखा गया मान हमेशा 0x1 होता है।

exploit - LPE with IORING

कर्नेल-मोड पते पर मान 0x1 लिखने की क्षमता के साथ, हम I/O रिंग (Microsoft द्वारा जारी एक नया I/O तंत्र) का लाभ उठाकर इस बग का उपयोग मनमाने पते को पढ़ने/लिखने की पूर्ण क्षमता प्राप्त करने के लिए कर सकते हैं। Yarden Shafir ने इस विधि के बारे में एक बहुत विस्तृत विश्लेषण लिखा है, आप इसे यहाँ पढ़ सकते हैं। एक क्रिया जो एप्लिकेशन कर सकता है, वह है अपने भविष्य के I/O संचालन के लिए सभी बफ़र्स आवंटित करना, फिर उन्हें I/O रिंग के साथ पंजीकृत करना। पूर्व-पंजीकृत बफ़र्स I/O ऑब्जेक्ट के माध्यम से संदर्भित होते हैं:

root@kitploit:~
typedef struct _IORING_OBJECT
{
    USHORT Type;
    USHORT Size;
    NT_IORING_INFO UserInfo;
    PVOID Section;
    PNT_IORING_SUBMISSION_QUEUE SubmissionQueue;
    PMDL CompletionQueueMdl;
    PNT_IORING_COMPLETION_QUEUE CompletionQueue;
    ULONG64 ViewSize;
    ULONG InSubmit;
    ULONG64 CompletionLock;
    ULONG64 SubmitCount;
    ULONG64 CompletionCount;
    ULONG64 CompletionWaitUntil;
    KEVENT CompletionEvent;
    UCHAR SignalCompletionEvent;
    PKEVENT CompletionUserEvent;
    ULONG RegBuffersCount;
    PVOID RegBuffers;
    ULONG RegFilesCount;
    PVOID* RegFiles;
} IORING_OBJECT, *PIORING_OBJECT;

यदि कोई सुरक्षा भेद्यता, जैसे कि इस लेख में उल्लिखित, आपको RegBuffersCount और RegBuffers फ़ील्ड को अपडेट/संशोधित करने की अनुमति देती है, तो मानक I/O रिंग API का उपयोग करके कर्नेल मेमोरी को पढ़ा और लिखा जा सकता है। हालांकि, NtQuerySystemInformation फ़ंक्शन का उपयोग करने के लिए Medium IL विशेषाधिकार की आवश्यकता होती है। Low IL से LPE के लिए, कर्नेल पते को लीक करने का कोई तरीका आवश्यक है।

जब IoRing->RegBuffers उपयोगकर्ता-नियंत्रित फ़ेकBuffer की ओर इशारा करता है, तो हम सामान्य I/O रिंग संचालन का उपयोग करके किसी भी पते को पढ़ और लिख सकते हैं, बस फ़ेक में एक इंडेक्स निर्दिष्ट करके जिसे बफ़र के रूप में उपयोग करना है:

  • Read operation + kernel address: कर्नेल हमारे द्वारा चुनी गई फ़ाइल से निर्दिष्ट कर्नेल पते पर "पढ़ेगा", जिससे मनमाना लेखन होगा।
  • Write operation + kernel address: कर्नेल निर्दिष्ट पते पर डेटा को हमारे द्वारा चुनी गई फ़ाइल में "लिखेगा", जिससे मनमाना पढ़ना होगा।

अधिक समझने के लिए आप Yarden Shafir का विश्लेषण ऊपर दिए गए लिंक पर पढ़ सकते हैं।

issue

ऊपर दिए गए POC कोड का उपयोग करके IO Ring ऑब्जेक्ट बनाने और लिखने का प्रयास करने के बाद, DeviceIOControl कॉल करने पर विंडोज क्रैश हो गया /_ \ इसलिए मैंने सीधे Ntfunction कॉल करने का तरीका अपनाया (˘・_・˘)

Affect range

  • विंडोज 11 21H1/22H2 OS build 22000.1455/22621.1105 से पहले
  • विंडोज सर्वर 2022 OS build 20348.1487 से पहले

The patch

  • पैच में ProbeForWrite को कॉल करने वाला कोड जोड़ा गया
  • पैच संस्करण:
    • विंडोज 11 21H1: KB5022287 (OS Build 22000.1455)
    • विंडोज 11 22H2: KB5022303 (OS Build 22621.1105)
    • विंडोज सर्वर 2022: KB5022291 (OS Build 20348.1487)

POC

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