
cve-2023-21768용 exp 재현
원인: 202209와 202307의 AFD.sys를 비교하면, 함수 AfdNotifyRemoveIOCompletion에서 202209 버전의 Windows와 202307 버전의 Windows 모두 ProbeForWrite 함수를 사용하여 메모리 영역이 사용자 모드에 있는지 검사하는 단계가 있습니다. 그러나 검사하는 버퍼 주소가 0x8 바이트 차이가 나며, 해당 취약점이 패치되기 전에는 이 검사가 없었습니다. 따라서 202209 버전의 버퍼 검사 주소가 올바르지 않아 무효일 가능성이 있습니다. 그리고 이 검사의 버퍼는 중간 단계의 대입과 관련이 있습니다: **(_DWORD **)(a3 + 24) = v20; 여기서 검사되어야 할 버퍼는 a3+24이고, v20의 값은 v8 = IoRemoveIoCompletion(v25, Pool2, v4, (unsigned int)v6, &v20, a1, v13, 0)와 관련이 있습니다. 그중 최소한 Pool2, v4, v13은 사용자 모드에서 전달된 unknown struct에 의해 결정되는 것으로 보입니다. 따라서 v20의 값도 사용자 모드에서 전달된 struct와 관련이 있을 것으로 추측됩니다~ (writeup을 보면 IoRemoveIoCompletion이 KeRemoveQueueEx를 호출한 반환값인 것 같습니다).
그리고 만약 a3+24가 커널 모드 주소를 저장하고 있다면, kernel_arbitrary_write 프리미티브가 발생할 수 있으며, 이를 통해 IORING을 이용한 익스플로잇이 가능합니다~ (이용 방법: https://windows-internals.com/one-i-o-ring-to-rule-them-all-a-full-read-write-exploit-primitive-on-windows-11/)
재현 환경: Visual Studio 2022로 소스 코드 컴파일 + Windows 11 202209 (Hyper-V에서 실행) 컴파일 옵션은 x64 Release, Hyper-V에서 vcruntime140.dll 누락 문제가 있어 정적 링크 사용
AFD.sys 내 함수 이용 체인: AfdFastIOdeviceControl -> AfdNotifySock -> AfdNotifyRemoveIOCompletion. 이 체인은 unknown struct의 한 필드를 사용자 모드에서 결정된 주소로 할당하여 임의 커널 쓰기-위치(arbitrary kernel Write-Where) 프리미티브를 생성하고, 이후 IORing에 의해 이용됩니다.
익스플로잇 구현: ArbitraryKernelWrite0x1 함수를 통해 임의 쓰기(arbitrary write)를 구현합니다. 익스플로잇 내에서 이 함수의 주요 부분은 x86matthew 님의 바퀴를 재사용하여 Winsock을 우회하고 AFD.sys와 직접 상호작용합니다 (원래 바퀴의 용도는 직접 TCP 소켓을 생성하는 것이었습니다) (https://www.x86matthew.com/view_post?id=ntsockets)
위에서 임의 주소 쓰기를 구현하는 것은 structAFD_NOTIFYSOCK_DATA, 즉 위의 unknown struct입니다. 그 다양한 구성 요소는 주로 함수 호출 체인의 여러 검사를 우회하기 위한 것입니다.
첫 번째 인수인 핸들은 문서화되지 않은 NT 함수 NtCreateIoCompletion을 통해 생성됩니다 (https://securityintelligence.com/x-force/patch-tuesday-exploit-wednesday-pwning-windows-ancillary-function-driver-winsock/)
디버깅 경험을 업데이트하자면: 겉보기에 매우 유사한 코드도 디버깅할 때 이상한 곳에서 막히는 경우가 있습니다.
예를 들어, 처음에 _NtCreateFile을 호출할 때 첫 번째 인수로 hSocket을 전달했는데, 원래 함수에서 __imp_ObReferenceObjectByHandle이 계속 음수를 반환했습니다. 확인해보니 _NtCreateFile과 _NtDeviceIoControlFile의 인수로 사용할 핸들을 별도로 정의해야 했습니다 (이 두 함수는 주로 afd.sys와 상호작용하는 데 사용되며, x86matthew의 글을 참고할 수 있습니다).
또한 처음에 NtSetIOCompletion을 호출하지 않아서 IORemoveIOCompletion 검사를 통과하지 못했습니다.. 그래서 https://securityintelligence.com/x-force/patch-tuesday-exploit-wednesday-pwning-windows-ancillary-function-driver-winsock/ 의 방법을 참고했습니다..
그리고 섀도우 그룹에서 심볼 테이블을 불러올 수 없다는 도움말을 올렸는데, 원인은 Windbg를 실행하는 컴퓨터에서 VPN(프록시)을 켜지 않아서 온라인 심볼 테이블에 연결할 수 없었기 때문이었습니다.
마지막으로 Windbg를 사용하여 Hyper-V의 프로그램을 디버깅하는 방법은 Microsoft Learn의 설명처럼 복잡하지 않습니다~, 대략 bcdedit /debug on; bcdedit /dbgsettings net hostip:(호스트의 Ethernet default switch의 IPv4 주소) port:50001 key:1.2.3.4를 Hyper-V 명령줄에 입력하면 됩니다.
해당 zip 파일에는 재현을 위한 Visual Studio 전체 프로젝트가 들어 있습니다.
마지막으로 팅팅 누나와 Windbg 트러블슈팅을 도와준 미미 선배님께 특별히 감사드립니다~