[하루한줄] CVE-2025-1533: ASUS AsIO3.sys의 Stack Overflow 취약점
URL
Target
- Asus Armoury Crate 5.9.13.0
- AI suite 3 ver 1.0.0.0
- AsIO3 Driver 1.02.30
Explain
CVE-2025-1533은 AsIO3.sys 드라이버에서 Asusgio3 장치 핸들을 얻으려고 할 때 Stack Overflow 취약점이 발생했습니다.
먼저 Asusgio3 장치 핸들을 얻기 위해 드라이버는 아래 두 가지 검사를 수행합니다.
- SHA-256 해시 검증
- 프로세스 ID(PID) allowed list 확인
bool sub_140002EF4() // Is AsusCertService.exe trying to obtain a handle ?
{
[...]
if ( pZwQueryInformationProcess(
(HANDLE)0xFFFFFFFFFFFFFFFFLL, ProcessImageFileNameWin32,
0LL,
0,
(PULONG)NumberOfBytes) == 0xC0000004 )
{
win32ImagePath = (_UNICODE_STRING *)ExAllocatePoolWithTag(PagedPool, *(unsigned int *)NumberOfBytes, 'pPR');
if ( win32ImagePath )
{
memset(PoolWithTag, 0, *(unsigned int *)NumberOfBytes);
pZwQueryInformationProcess(
(HANDLE)-1LL,
ProcessImageFileNameWin32,
win32ImagePath,
*(unsigned int *)NumberOfBytes,
0LL);
if ( (int)Win32PathToNtPath(win32ImagePath, &ntImagePath) >= 0 )
{
fileContent = GetFileContent(&ntImagePath, &fileSize);
if ( fileContent )
{
blockSize = 64LL;
v12 = 64LL;
_fileSize = fileSize;
v14 = fileSize;
ptrFileContent = (char *)fileContent;
v13 = (char *)fileContent;
Z_SHA256_Init((SHA256_CTX *)sha_ctx);
if ( _fileSize < 64 )
blockSize = _fileSize;
v12 = blockSize;
while ( _fileSize > 0 )
{
if ( _fileSize - blockSize < 0 )
blockSize = _fileSize;
v12 = blockSize;
Util::AES_Block_encrypt(sha_ctx, ptrFileContent, blockSize);
ptrFileContent += blockSize;
v13 = ptrFileContent;
_fileSize -= blockSize;
v14 = _fileSize;
}
SHA_Final(sha_ctx, calculatedSHA256);
result = memcmp(calculatedSHA256, &g_sha256Hash, 0x20uLL) == 0; // [1]
ExFreePoolWithTag(fileContent, 0x705052u);
}
}
RtlFreeUnicodeString(&ntImagePath);
ExFreePoolWithTag(win32ImagePath, 0);
}
}
return result;
}
위 sub_140002EF4()
함수는 SHA-256 해시 검증을 하는 코드로 아래 순서대로 실행됩니다.
- 프로세스 경로 확인
- 파일 내용 읽기
- SHA-256 해시 계산
- g_sha256Hash 해시 값과 비교
따라서 g_sha256Hash 해시 값과 다르면 Asusgio3 핸들을 얻을 수 없는데 취약점은 Win32PathToNtPath()
함수에서 프로세스 경로를 처리할 때 발생합니다.
__int64 __fastcall Win32PathToNtPath(_UNICODE_STRING *win32ImagePath, struct _UNICODE_STRING *outNtImagePath)
{
[...]
WCHAR ntImagePath[255]; // [rsp+D0h] [rbp-30h] BYREF
backslashPos = 0;
memset(partition, 0, sizeof(partition));
inBuffer = win32ImagePath->Buffer; // e.g: C:\AAAAAAAAA......\AAAAAAAAAAA....\TestApp.exe
wcscpy(partition, L"\\??\\"); // [2]
v6 = *inBuffer;
if ( *inBuffer != '\\' )
{
i = 0LL;
do
{
if ( !v6 )
break;
++i;
++backslashPos;
v6 = inBuffer[i];
}
while ( v6 != '\\' );
}
memmove(&partition[4], inBuffer, backslashPos * 2);
RtlInitUnicodeString(&DestinationString, partition);
ObjectAttributes.Length = 48;
ObjectAttributes.ObjectName = &DestinationString;
ObjectAttributes.RootDirectory = 0LL;
ObjectAttributes.Attributes = 64;
ObjectAttributes.SecurityDescriptor = 0LL;
status = ZwOpenSymbolicLinkObject(&LinkHandle, 0x80000000, &ObjectAttributes);
if ( status >= 0 )
{
LinkTarget.Buffer = (PWSTR)ExAllocatePoolWithTag(PagedPool, 0xFFuLL, 0x705052u);
status = ZwQuerySymbolicLinkObject(LinkHandle, &LinkTarget, &ReturnedLength);
ZwClose(LinkHandle);
memset(ntImagePath, 0, sizeof(ntImagePath)); // [3]
memcpy(ntImagePath, LinkTarget.Buffer , LinkTarget.Length) // where LinkTarget.Buffer e.g == \Device\HarddiskVolume3
ptrWin32ImagePath = &win32ImagePath->Buffer[backslashPos]; // set pointer to the application path without partition letter. e.g: AAAAAAAAA......\AAAAAAAAAAA....\TestApp.exe
ptrNtImagePath = ntImagePath;
do
++ptrNtImagePath;
while ( *ptrNtImagePath ); //set pointer at the end of current string
idx = 0LL;
do
{
wcharToCopy = ptrWin32ImagePath[idx];
ptrNtImagePath[idx++] = wcharToCopy;
}
while ( wcharToCopy );
RtlInitUnicodeString(&tmpString, (PCWSTR)ntImagePath);
RtlCopyUnicodeString(outNtImagePath, &tmpString);
RtlFreeUnicodeString(&LinkTarget);
}
return (unsigned int)status;
}
Win32PathToNtPath()
함수는 win32 Path(C:\TestApp.exe)를 NT Namespace Path(\Device\HarddiskVolume3\TestApp.exe)로 변경하는 코드로 아래 순서대로 실행됩니다.
[2]에서 win32ImagePath
드라이브 문자를 분리하고, ZwOpenSymbolicLinkObject
와 ZwQuerySymbolicLinkObject
함수를 통해 드라이브 문자에 해당하는 NT Namespace Path를 얻어옵니다.
[3]에서 NT Namespace Path를 ntImagePath에 복사하고 do-while문을 통해 win32ImagePath에서 드라이브 문자를 제외한 나머지 경로를 ntImagePath에 이어 붙여 win32 Path를 NT Namespace Path로 변경합니다.
여기서 ntImagePath 배열의 크기를 255로 설정한 이유는 Windows의 최대 경로 길이가 255이므로 설정했지만, 경로 앞에 \\?\
가 포함될 경우 경로 길이가 32,767까지 증가합니다.
Stack before get overwritten
fffff807`66943926 4533c0 xor r8d,r8d
18: kd> kb
# RetAddr : Args to Child : Call Site
00 fffff807`66943021 : 00000000`00000000 ffff9888`b4ab586c 00000000`00000000 00000000`00000001 : AsIO3+0x3926
01 fffff807`66942596 : ffff9888`c152ad00 00000000`c0000002 ffff9888`c152ae50 00000000`00000001 : AsIO3+0x3021
02 fffff807`622650d5 : 00000000`00000000 fffff807`62261993 00000000`00000000 ffff9888`e3ab7e98 : AsIO3+0x2596
03 fffff807`626becb3 : 00000000`00000000 ffff938a`6fccf0d0 ffff9888`e3ab7e98 ffff9888`c152ad00 : nt!IofCallDriver+0x55
04 fffff807`626cac24 : fffff807`626bd700 fffff807`626bd700 ffff9888`b4941a00 ffff938a`6fccf1f0 : nt!IopParseDevice+0x15b3
05 fffff807`626c95d2 : ffff9888`e30d9a01 ffff938a`6fccf1f0 00000000`00000040 ffff9888`b4ab5820 : nt!ObpLookupObjectName+0x1104
06 fffff807`626ace47 : ffff9249`00000000 ffff9888`dbf27a20 00000098`c0f6f980 00000000`00000001 : nt!ObOpenObjectByNameEx+0x1f2
07 fffff807`626ac319 : 00000098`c0f6f920 00000000`c0100080 00000098`c0f6f980 00000098`c0f6f928 : nt!IopCreateFile+0xb17
08 fffff807`6242a508 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : nt!NtCreateFile+0x79
09 00007ffd`68ff0e44 : 00007ffd`6671a650 00000000`00000000 00000000`00000000 00000000`00000360 : nt!KiSystemServiceCopyEnd+0x28
0a 00007ffd`6671a650 : 00000000`00000000 00000000`00000000 00000000`00000360 00000000`00000000 : ntdll!NtCreateFile+0x14
0b 00007ffd`66719fbc : 00007ff6`af8311ae b2200007`ff69d4f0 a8c00000`00032cc1 00000000`00000000 : KERNELBASE!CreateFileInternal+0x590
0c 00007ff6`af835925 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNELBASE!CreateFileW+0x7c
0d 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00410041`00000003 : 0x00007ff6`af835925
Bug triggered
For analysis of this file, run !analyze -v
nt!DbgBreakPointWithStatus:
fffff807`6241f2f0 cc int 3
18: kd> !analyze -v
Connected to Windows 10 22621 x64 target at (Thu Jan 23 21:09:08.278 2025 (UTC + 1:00)), ptr64 TRUE
Loading Kernel Symbols
...............................................................
................................................................
................................................................
.............................................
Loading User Symbols
.........
Loading unloaded module list
...........
************* Symbol Loading Error Summary **************
Module name Error
SharedUserData No error - symbol load deferred
You can troubleshoot most symbol related issues by turning on symbol loading diagnostics (!sym noisy) and repeating the command that caused symbols to be loaded.
You should also verify that your symbol search path (.sympath) is correct.
*******************************************************************************
* *
* Bugcheck Analysis *
* *
*******************************************************************************
DRIVER_OVERRAN_STACK_BUFFER (f7)
A driver has overrun a stack-based buffer. This overrun could potentially
allow a malicious user to gain control of this machine.
DESCRIPTION
A driver overran a stack-based buffer (or local variable) in a way that would
have overwritten the function's return address and jumped back to an arbitrary
address when the function returned. This is the classic "buffer overrun"
hacking attack and the system has been brought down to prevent a malicious user
from gaining complete control of it.
Do a kb to get a stack backtrace -- the last routine on the stack before the
buffer overrun handlers and BugCheck call is the one that overran its local
variable(s).
Arguments:
Arg1: ffbe93cb6f8de881, Actual security check cookie from the stack
Arg2: 00000581a36bfe71, Expected security check cookie
Arg3: fffffa7e5c94018e, Complement of the expected security check cookie
Arg4: 0000000000000000, zero
Debugging Details:
------------------
Unable to load image \??\C:\Windows\system32\drivers\AsIO3.sys, Win32 error 0n2
KEY_VALUES_STRING: 1
[...]
magic_cookie value check:
18: kd> dq ffff938a`6fcceb90 L1
ffff938a`6fcceb90 00410041`00410041
Last 20 frames
18: kd> kb 20
# RetAddr : Args to Child : Call Site
00 fffff807`62566c12 : ffff938a`6fcccf40 fffff807`622a9f60 ffff8181`38f51180 ffbe93cb`6f8de801 : nt!DbgBreakPointWithStatus
01 fffff807`625662d3 : ffff8181`00000003 ffff938a`6fcccf40 fffff807`6242ef20 00000000`000000f7 : nt!KiBugCheckDebugBreak+0x12
02 fffff807`62415017 : 00000000`000000ee fffff807`623c26fb ffff938a`6fcce648 00000000`00000001 : nt!KeBugCheck2+0xba3
03 fffff807`66946f97 : 00000000`000000f7 ffbe93cb`6f8de881 00000581`a36bfe71 fffffa7e`5c94018e : nt!KeBugCheckEx+0x107
04 fffff807`66947032 : ffff938a`6fccd6d8 ffff938a`6fccdce0 00000000`6fccde60 ffff938a`6fccdc68 : AsIO3+0x6f97
05 fffff807`66946fcb : fffff807`00000000 fffff807`6222e158 fffff807`66946fb8 00000000`00000000 : AsIO3+0x7032
06 fffff807`624205b2 : ffff938a`6fccde60 ffff938a`6fccd720 fffff807`66940000 fffff807`66943958 : AsIO3+0x6fcb
07 fffff807`6222cc93 : ffff938a`6fcce890 ffff938a`6fcce648 fffff807`66943958 fffff807`6694a0b4 : nt!RtlpExecuteHandlerForException+0x12
08 fffff807`6231817e : ffffffff`ffffffff ffff938a`6fcce6f0 ffff938a`6fcce6f0 ffff938a`6fccde60 : nt!RtlDispatchException+0x2f3
09 fffff807`6242af7c : ffffd203`b9792f70 ffff938a`6fccec08 00000000`00000000 00000000`00000004 : nt!KiDispatchException+0x1ae
0a fffff807`62425dd8 : 00000000`00ff0000 ffffd203`ee528180 00000000`000000d0 ffffd203`b96b44d0 : nt!KiExceptionDispatch+0x13c
0b fffff807`6242bc67 : fffff807`62261993 00000000`00000010 fffff807`66943929 00000000`00000010 : nt!KiGeneralProtectionFault+0x358
0c fffff807`62261993 : 00000000`00000010 fffff807`66943929 00000000`00000010 00000000`00040244 : nt!memcpy+0xa7
0d fffff807`66943958 : 00000000`00000000 ffffd203`cca31010 00000000`00000000 00000000`00000000 : nt!RtlCopyUnicodeString+0x43
0e 00410041`00410041 : 00410041`00410041 00410041`00410041 00410041`00410041 00410041`00410041 : AsIO3+0x3958
0f 00410041`00410041 : 00410041`00410041 00410041`00410041 00410041`00410041 00410041`00410041 : 0x00410041`00410041
10 00410041`00410041 : 00410041`00410041 00410041`00410041 00410041`00410041 00410041`00410041 : 0x00410041`00410041
11 00410041`00410041 : 00410041`00410041 00410041`00410041 00410041`00410041 00410041`00410041 : 0x00410041`00410041
12 00410041`00410041 : 00410041`00410041 00410041`00410041 00410041`00410041 00410041`00410041 : 0x00410041`00410041
13 00410041`00410041 : 00410041`00410041 00410041`00410041 00410041`00410041 00410041`00410041 : 0x00410041`00410041
14 00410041`00410041 : 00410041`00410041 00410041`00410041 00410041`00410041 00410041`00410041 : 0x00410041`00410041
15 00410041`00410041 : 00410041`00410041 00410041`00410041 00410041`00410041 00410041`00410041 : 0x00410041`00410041
16 00410041`00410041 : 00410041`00410041 00410041`00410041 00410041`00410041 00410041`00410041 : 0x00410041`00410041
17 00410041`00410041 : 00410041`00410041 00410041`00410041 00410041`00410041 00410041`00410041 : 0x00410041`00410041
18 00410041`00410041 : 00410041`00410041 00410041`00410041 00410041`00410041 00410041`00410041 : 0x00410041`00410041
19 00410041`00410041 : 00410041`00410041 00410041`00410041 00410041`00410041 00410041`00410041 : 0x00410041`00410041
1a 00410041`00410041 : 00410041`00410041 00410041`00410041 00410041`00410041 00410041`00410041 : 0x00410041`00410041
1b 00410041`00410041 : 00410041`00410041 00410041`00410041 00410041`00410041 00410041`00410041 : 0x00410041`00410041
1c 00410041`00410041 : 00410041`00410041 00410041`00410041 00410041`00410041 00410041`00410041 : 0x00410041`00410041
1d 00410041`00410041 : 00410041`00410041 00410041`00410041 00410041`00410041 00410041`00410041 : 0x00410041`00410041
1e 00410041`00410041 : 00410041`00410041 00410041`00410041 00410041`00410041 00410041`00410041 : 0x00410041`00410041
1f 00410041`00410041 : 00410041`00410041 00410041`00410041 00410041`00410041 00410041`00410041 : 0x00410041`00410041
따라서, Win32PathToNtPath()
함수에서 파일 경로 길이를 검증하지 않고 ntImagePath 배열에 복사하기 때문에 \\?\
포함한 255 보다 긴 경로를 가진 파일을 통해 Stack Overflow 취약점을 트리거 할 수 있습니다.
Reference
본 글은 CC BY-SA 4.0 라이선스로 배포됩니다. 공유 또는 변경 시 반드시 출처를 남겨주시기 바랍니다.