Skip to content
KitploitKITPLOIT
도구익스플로잇블로그
Log in
제출
도구익스플로잇블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2023-21768 — CVE-2023-21768에 대한 개념 증명 익스플로잇, Windows 보조 함수 드라이버(AFD.sys)의 임의 커널 쓰기 취약점으로 I/O 링을 통해 로컬 권한 상승을 가능하게 합니다. | Kitploit
도구/GitHubGitHub/h1bana/cve-2023-21768
Privilege EscalationVulnerability AnalysisExploitationBinary Exploitation
GitHubh1bana/cve-2023-21768

CVE-2023-21768

CVE-2023-21768에 대한 개념 증명 익스플로잇, Windows 보조 함수 드라이버(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의 커널 진입점입니다. 이 분석에서는 이를 사용하여 Windows 11에서 권한 상승을 악용할 것입니다.

Patch Diff and Root Cause Analysis

afd.sys의 두 버전을 Winbindex에서 다운로드합니다. 하나는 패치 이전의 최신 버전이고, 다른 하나는 패치 이후 버전입니다. 그런 다음 Bindiff를 사용하여 두 버전을 비교합니다. bindiff

두 버전을 전체적으로 비교하면, 오직 하나의 함수 AfdNotifyRemoveIoCompletion에서만 차이가 있음을 알 수 있습니다. 이 함수의 두 버전 간 차이를 자세히 살펴봅니다. bindiff

두 버전 간에 큰 차이는 없습니다. 패치 후 버전에서는 매개변수를 설정하고 ProbeForWrite 함수를 호출하는 어셈블리 명령어가 추가되었습니다. Microsoft 문서에 따르면 이 함수는 주소가 실제로 사용자 모드에 속하는지, 쓰기 권한이 있는지, 올바르게 정렬되었는지 확인하는 데 사용됩니다. 이 코드를 더 자세히 분석합니다:

  • 패치 전 afd.sys 버전 10.0.22621.608 code1

  • 패치 후 afd.sys 버전 10.0.22621.1105 code2

둘 다 r15_1의 값을 확인하며, 0이면 var_304의 값을 struct_1의 특정 필드에 지정된 포인터에 씁니다. 0이 아니면 ProbeForWrite가 호출되어 포인터가 유효한 주소를 가리키는지 확인합니다. 패치 전 버전에서는 그런 다음 var_304 값을 포인터에 씁니다. 이 검사가 누락되어 있습니다. 이 패치로부터 우리는 제어 가능한 arg3_1->field_18 값을 사용하여 이 코드를 호출할 수 있다고 추측할 수 있습니다. 만약 field_18에 커널 주소 값을 설정할 수 있다면 var_304를 해당 커널 메모리 주소에 쓸 수 있습니다.

=> 버그 유형: 임의의 커널 Write-Where

이제 버그를 트리거할 방법을 찾아야 합니다. AfdNotifyRemoveIoCompletion 함수는 AfdNotifySock 함수 내에서 직접 호출됩니다. crossRef

마찬가지로 AfdNotifySock의 교차 참조를 찾으면 다른 함수에서 직접 호출되지 않지만 함수 주소는 .rdata의 한 주소에 저장됩니다. cross2

이 주소는 AfdIrpCallDispatch 바로 앞에 위치합니다. cross3

버그를 트리거하기 위해 IOCTL_AFD_NOTIFY_SOCK과 함께 DeviceIoControl을 호출하면 AfdNotifySock이 호출됩니다.

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 객체가 생성되며, 다음과 같이 정의됩니다:

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]에 저장됩니다.

#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를 통해 첨자를 계산하고 AfdIoctlTable에서 첨자에 해당하는 값을 가져와 IoControlCode로 확인하는 것을 쉽게 알 수 있습니다. 1

AfdImmediateCallDispatch의 시작 주소와 AfdNotifySock이 저장된 주소 사이의 거리로부터 인덱스가 73이고 컨트롤 코드가 0x12127임을 계산할 수 있습니다. ioctl

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;
}

작동합니다!

bp1

처음부터 말했듯이, 취약점은 struct를 통해 검증되지 않은 포인터를 전달할 때 발생합니다. 이 struct는 DeviceIoControl의 lpInBuffer를 통해 사용자 모드에서 직접 전달됩니다. 그런 다음 네 번째 매개변수에 해당하는 AfdNotifySock에 전달되고, 세 번째 매개변수에 해당하는 AfdNotifyRemoveIoCompletion에 전달됩니다.

para1 para2 para3

struct의 구성을 아직 모르기 때문에 IDA가 struct를 자동으로 생성하도록 했습니다. 이제 struct에 데이터를 전달하고 필요한 검사를 우회하여 취약한 코드에 도달하는 방법을 찾아야 합니다. AfdNotifySock 함수부터 시작합니다:

check1

첫째, struct의 크기는 0x30바이트여야 합니다.

check2

0이 아니어야 하는 값들:

check3

또 한 가지는 디버그할 때 이전 UserBuffer 검사에서 실패로 점프하는 것을 확인했기 때문에 DeviceIoControl을 호출할 때 이 값을 NULL로 설정했습니다. 위의 값들을 설정한 후 check2를 통과했습니다.

debug1 debug2

우회해야 할 다음 검사:

check4

도구 다운로드