ASUS Fan_Xper is used to control the fan speed. Many vulnerability exits in it's driver AsInsHelp64.sys Before version 10013, which allows low-privileged users to map arbitrary physical memory, read and write arbitary i/o port via specially crafted IOCTL requests. This can be exploited for privilege escalation, code execution under high privileges, and information disclosure. These signed drivers can also be used to bypass the Microsoft driver-signing policy to deploy malicious code.
Using AsInsHelp64.sys as example, AsInsHelp32.sys is also similar.
10013
AsInsHelp64.sys provides the functionality of mapping physical memory and In/Out I/O ports, but it does not restrict the privileges of the caller, resulting in low-privileged users being able to call the driver and execute corresponding functions through DeviceIoControl.
__int64 __fastcall sub_116F0(__int64 a1, __int64 a2) // ioctler
{
char is32bitprocess; // si
__int64 v5; // rdx
int v6; // ebx
char v7; // al
unsigned int v8; // eax
void *v9; // rdx
__int64 v10; // rax
is32bitprocess = IoIs32bitProcess((PIRP)a2);
*(_QWORD *)(a2 + 56) = 0i64;
v5 = *(_QWORD *)(a2 + 184);
v6 = -1073741822;
v7 = *(_BYTE *)v5;
if ( !*(_BYTE *)v5 || v7 == 2 )
{
v6 = 0;
goto LABEL_28;
}
if ( v7 != 14 )
goto LABEL_28;
v8 = *(_DWORD *)(v5 + 24);
if ( v8 > 0xA0406408 )
{
if ( v8 == 0xA040A440 || v8 == 0xA040A444 || v8 == 0xA040A448 )
v6 = sub_11110(a2, v5, is32bitprocess); // out arbitary I/O port
goto LABEL_28;
}
switch ( v8 )
{
case 0xA0406408:
LABEL_10:
v6 = sub_11000(a2, v5); // in arbitary I/O port
break;
case 0xA040244C:
v6 = sub_112F0(a1, a2, *(_QWORD *)(a2 + 184), is32bitprocess);// map physical address
if ( v6 < 0 )
{
*(_DWORD *)(a2 + 48) = -1073741811;
}
else
{
v10 = 8i64;
if ( is32bitprocess )
v10 = 4i64;
*(_QWORD *)(a2 + 56) = v10;
}
break;
case 0xA0402450:
if ( is32bitprocess )
{
if ( *(_DWORD *)(v5 + 16) >= 4u )
{
v9 = (void *)**(unsigned int **)(a2 + 24);
LABEL_14:
v6 = ZwUnmapViewOfSection((HANDLE)0xFFFFFFFFFFFFFFFFi64, v9);
break;
}
}
else if ( *(_DWORD *)(v5 + 16) >= 8u )
{
v9 = **(void ***)(a2 + 24);
goto LABEL_14;
}
v6 = -1073741670;
break;
case 0xA0406400:
case 0xA0406404:
goto LABEL_10;
}
LABEL_28:
*(_DWORD *)(a2 + 48) = v6;
IofCompleteRequest((PIRP)a2, 0);
return (unsigned int)v6;
}This IOCTL code triggers the mapping of physical memory. The composition of the input buffer is as follows.
#pragma pack (1)
typedef struct {
ULONG64 junk1;
ULONG64 section_offset; // The physical address you want to map.
DWORD junk2;
DWORD view_size; // How many bytes
} AsInsHelp64_Map_Inputbuffer;
#pragma pack()The memory mapping is eventually completed by calling ZwMapViewOfSection.
__int64 __fastcall sub_112F0(__int64 a1, __int64 a2, __int64 a3, bool is32bitprocess)
{
PHYSICAL_ADDRESS *v5; // rdi
_DWORD *v6; // r13
NTSTATUS v7; // ebx
HANDLE v8; // rcx
_DWORD *v9; // r14
DWORD v10; // ecx
DWORD LowPart; // eax
void *SectionHandle; // [rsp+50h] [rbp-D8h] BYREF
ULONG AddressSpace; // [rsp+58h] [rbp-D0h] BYREF
LARGE_INTEGER TranslatedAddress; // [rsp+60h] [rbp-C8h] BYREF
union _LARGE_INTEGER SectionOffset; // [rsp+68h] [rbp-C0h] BYREF
HANDLE Handle; // [rsp+70h] [rbp-B8h] BYREF
ULONG_PTR ViewSize; // [rsp+78h] [rbp-B0h] BYREF
PHYSICAL_ADDRESS BusAddress; // [rsp+80h] [rbp-A8h] BYREF
PVOID Object; // [rsp+88h] [rbp-A0h] BYREF
PVOID v21; // [rsp+90h] [rbp-98h] BYREF
ULONG v22; // [rsp+98h] [rbp-90h] BYREF
PVOID BaseAddress; // [rsp+A0h] [rbp-88h] BYREF
struct _OBJECT_ATTRIBUTES ObjectAttributes; // [rsp+A8h] [rbp-80h] BYREF
struct _UNICODE_STRING DestinationString; // [rsp+D8h] [rbp-50h] BYREF
v5 = *(PHYSICAL_ADDRESS **)(a2 + 24);
AddressSpace = v5[2].LowPart;
v22 = AddressSpace;
Handle = 0i64;
v21 = 0i64;
LODWORD(SectionHandle) = 0;
LODWORD(Object) = 0;
if ( is32bitprocess )
{
v6 = v5;
if ( *(_DWORD *)(a3 + 16) < 0x18u || *(_DWORD *)(a3 + 8) < 4u )
{
v7 = 0xC000009A;
goto LABEL_5;
}
v9 = v21;
}
else
{
v9 = v5;
if ( *(_DWORD *)(a3 + 16) < 0x18u || *(_DWORD *)(a3 + 8) < 8u )
return (unsigned int)-1073741670;
v6 = v21;
}
RtlInitUnicodeString(&DestinationString, L"\\Device\\PhysicalMemory");
ObjectAttributes.Length = 48;
ObjectAttributes.RootDirectory = 0i64;
ObjectAttributes.Attributes = 64;
ObjectAttributes.ObjectName = &DestinationString;
ObjectAttributes.SecurityDescriptor = 0i64;
ObjectAttributes.SecurityQualityOfService = 0i64;
if ( is32bitprocess )
{
v7 = ZwOpenSection(&SectionHandle, 0xF001Fu, &ObjectAttributes);
if ( v7 < 0 )
goto LABEL_5;
v7 = ObReferenceObjectByHandle((HANDLE)(int)SectionHandle, 0xF001Fu, 0i64, 0, &Object, 0i64);
if ( v7 < 0 )
goto LABEL_5;
}
else
{
v7 = ZwOpenSection(&Handle, 0xF001Fu, &ObjectAttributes);
if ( v7 < 0 )
goto LABEL_32;
v7 = ObReferenceObjectByHandle(Handle, 0xF001Fu, 0i64, 0, &v21, 0i64);
if ( v7 < 0 )
goto LABEL_32;
}
BusAddress.QuadPart = v5[1].QuadPart + v5[2].HighPart + (unsigned int)(unsigned __int16)v5[1].LowPart;
if ( !HalTranslateBusAddress((INTERFACE_TYPE)v5->LowPart, v5->HighPart, v5[1], &AddressSpace, &TranslatedAddress)
|| !HalTranslateBusAddress((INTERFACE_TYPE)v5->LowPart, v5->HighPart, BusAddress, &v22, &BusAddress)
|| (v10 = BusAddress.LowPart - TranslatedAddress.LowPart,
ViewSize = BusAddress.QuadPart - TranslatedAddress.QuadPart,
BusAddress.LowPart == TranslatedAddress.LowPart) )
{
v7 = 0xC0000001;
goto LABEL_5;
}
if ( AddressSpace )
{
LowPart = TranslatedAddress.LowPart;
if ( is32bitprocess )
{
*v6 = TranslatedAddress.LowPart;
v7 = 0;
goto LABEL_5;
}
goto LABEL_30;
}
SectionOffset = TranslatedAddress;
if ( !is32bitprocess )
{
BaseAddress = 0i64;
v7 = ZwMapViewOfSection(
Handle, // rcx
(HANDLE)0xFFFFFFFFFFFFFFFFi64, // rdx
&BaseAddress, // r8
0i64, // r9
v10, // commitsize==viewsize rsp+20
&SectionOffset, // sectionoffset = inputbuffer 0x8-0xf
&ViewSize, // rsp+30
ViewShare, // rsp+38
0, // rsp+40
0x204u); // rsp+48
if ( v7 >= 0 )
{
LowPart = TranslatedAddress.LowPart + (_DWORD)BaseAddress - SectionOffset.LowPart;
LABEL_30:
*v9 = LowPart;
v7 = 0;
goto LABEL_5;
}
LABEL_32:
v8 = Handle;
if ( Handle )
goto LABEL_33;
return (unsigned int)v7;
}
HIDWORD(SectionHandle) = 0;
v7 = ZwMapViewOfSection(
(HANDLE)(int)SectionHandle,
(HANDLE)0xFFFFFFFFFFFFFFFFi64,
(void **)((char *)&SectionHandle + 4),
0i64,
v10,
&SectionOffset,
&ViewSize,
ViewShare,
0,
0x204u);
if ( v7 >= 0 )
{
HIDWORD(SectionHandle) += TranslatedAddress.LowPart - SectionOffset.LowPart;
*v6 = HIDWORD(SectionHandle);
v7 = 0;
}
LABEL_5:
if ( !is32bitprocess )
goto LABEL_32;
if ( (_DWORD)SectionHandle )
{
v8 = (HANDLE)(int)SectionHandle;
LABEL_33:
ZwClose(v8);
}
return (unsigned int)v7;
}This code triggers reading from an I/O port. The composition of the input buffer is as follows.
#pragma pack (1)
typedef struct {
DWORD port // target port
} AsInsHelp64_In_Inputbuffer;
#pragma pack()The I/O port reading is eventually completed in function below.
__int64 __fastcall sub_11000(__int64 a1, __int64 a2)
{
int v3; // ebx
_WORD *v4; // rdi
ULONG v5; // esi
unsigned __int32 v6; // eax
unsigned __int16 v7; // ax
unsigned __int8 v8; // al
ULONG AddressSpace; // [rsp+30h] [rbp-48h] BYREF
PHYSICAL_ADDRESS BusAddress; // [rsp+38h] [rbp-40h] BYREF
v3 = *(_DWORD *)(a2 + 24);
v4 = *(_WORD **)(a1 + 24);
switch ( v3 )
{
case 0xA0406400:
v5 = 1;
break;
case 0xA0406404:
v5 = 2;
break;
case 0xA0406408:
v5 = 4;
break;
default:
v5 = AddressSpace;
break;
}
BusAddress.QuadPart = *(unsigned int *)v4;
AddressSpace = 1;
HalTranslateBusAddress(Isa, 0, BusAddress, &AddressSpace, &BusAddress);
if ( AddressSpace == 1 )
{
if ( v3 != 0xA0406400 )
{
if ( v3 == 0xA0406404 )
{
v7 = __inword(BusAddress.LowPart);
*v4 = v7;
}
else if ( v3 == 0xA0406408 )
{
v6 = __indword(BusAddress.LowPart);
*(_DWORD *)v4 = v6;
}
goto LABEL_22;
}
v8 = __inbyte(BusAddress.LowPart);
LABEL_21:
*(_BYTE *)v4 = v8;
goto LABEL_22;
}
switch ( v3 )
{
case 0xA0406400:
v8 = *(_BYTE *)BusAddress.LowPart;
goto LABEL_21;
case 0xA0406404:
*v4 = *(_WORD *)BusAddress.LowPart;
break;
case 0xA0406408:
*(_DWORD *)v4 = *(_DWORD *)BusAddress.LowPart;
break;
}
LABEL_22:
*(_QWORD *)(a1 + 56) = v5;
return 0i64;
}This code triggers reading from an I/O port. The composition of the input buffer is as follows.
DWORDLONG inputbuffer= (DWORD64)data << 32 | port;The I/O port reading is eventually completed in function below.
__int64 __fastcall sub_11110(__int64 a1, __int64 a2, char is32bitprocess)
{
int v4; // r12d
_DWORD *v5; // rdi
unsigned int v6; // eax
unsigned __int8 *v7; // rdi
DWORD *QuadPart; // rbx
unsigned int *v9; // rbx
ULONG AddressSpace; // [rsp+30h] [rbp-38h] BYREF
PHYSICAL_ADDRESS BusAddress; // [rsp+38h] [rbp-30h] BYREF
v4 = *(_DWORD *)(a2 + 24);
if ( is32bitprocess )
{
v5 = *(_DWORD **)(a1 + 24);
v6 = *v5;
v7 = (unsigned __int8 *)(v5 + 1);
QuadPart = (DWORD *)BusAddress.QuadPart;
}
else
{
v9 = *(unsigned int **)(a1 + 24);
v6 = *v9;
QuadPart = v9 + 1;
v7 = (unsigned __int8 *)BusAddress.QuadPart;
}
BusAddress.QuadPart = v6;
AddressSpace = 1;
HalTranslateBusAddress(Isa, 0, (PHYSICAL_ADDRESS)v6, &AddressSpace, &BusAddress);
if ( AddressSpace != 1 )
{
switch ( v4 )
{
case 0xA040A440:
if ( is32bitprocess )
{
*(_BYTE *)BusAddress.LowPart = *v7;
_mm_sfence();
return 0i64;
}
LOBYTE(BusAddress.LowPart) = *(_BYTE *)QuadPart;
_mm_sfence();
return 0i64;
case 0xA040A444:
if ( is32bitprocess )
*(_WORD *)BusAddress.LowPart = *(_WORD *)v7;
else
LOWORD(BusAddress.LowPart) = *(_WORD *)QuadPart;
break;
case 0xA040A448:
if ( is32bitprocess )
*(_DWORD *)BusAddress.LowPart = *(_DWORD *)v7;
else
BusAddress.LowPart = *QuadPart;
break;
default:
return 0i64;
}
_mm_sfence();
return 0i64;
}
switch ( v4 )
{
case 0xA040A440:
if ( is32bitprocess )
__outbyte(BusAddress.LowPart, *v7);
else
__outbyte((unsigned __int16)&BusAddress, *(_BYTE *)QuadPart);
return 0i64;
case 0xA040A444:
if ( is32bitprocess )
__outword(BusAddress.LowPart, *(_WORD *)v7);
else
__outword((unsigned __int16)&BusAddress, *(_WORD *)QuadPart);
return 0i64;
case 0xA040A448:
if ( is32bitprocess )
__outdword(BusAddress.LowPart, *(_DWORD *)v7);
else
__outdword((unsigned __int16)&BusAddress, *QuadPart);
return 0i64;
default:
return 0i64;
}
}Load AsInsHelp64.sys and run CVE-2024-30804.exe
