[하루한줄] CVE-2025-23271: NVIDIA nvdisasm의 heap overflow 취약점

URL

Target

  • NVIDIA nvdisasm 12.8.90

Explain

NVIDIA nvdisasm는 CUDA toolkit에 포함된 도구로 CUDA ELF 파일의 disassembly, control low graphs, register life-ranges, debug 정보를 표시하는 데 사용됩니다.

typedef uint16 Elf32_Half;
typedef uint32 Elf32_Word;
typedef uint32 Elf32_Addr;
typedef uint32 Elf32_Off;

typedef struct {
    Elf32_Word sh_name;
    Elf32_Word sh_type;
    Elf32_Word sh_flags;
    Elf32_Addr sh_addr;
    Elf32_Off  sh_offset;
    Elf32_Word sh_size;
    Elf32_Word sh_link;
    Elf32_Word sh_info;
    Elf32_Word sh_addralign;
    Elf32_Word sh_entsize;
} Elf32_Shdr;

ELF 파일의 .reloc 섹션은 바이너리가 메모리에 로드될 때 필요한 재배치(relocation) 정보를 나타냅니다.

해당 취약점은 nvdisasm이 32bit ELF 파일의 .reloc 섹션을 처리하면서 발생했습니다.

0045c699  mov     rsi, qword [rsp {var_b8_1}]
0045c69d  mov     rdi, qword [rax+0x18]
0045c6a1  shr     rsi, 0x3                     (1) 
0045c6a5  shl     rsi, 0x4                     (2)
0045c6a9  call    sub_40a610                   (3)

nvdisasm은 ELF 파일의 .reloc 섹션을 0x45ada0 함수에서 파싱 합니다.

offset 0x45c699에서 현재 .reloc 섹션의 sh_size를 rsi 레지스터로 옮기고 (1),(2)를 통해 sh_size 값을 2배로 만듭니다. 이때, sh_size 값은 메모리 할당 크기로 사용됩니다.

0045c07b  lea     r14, [rbx+rsi]               
...
0045c6ef  lea     rax, [r14+0x7]               
0045c6f3  lea     rsi, [rbx+0x8]               
0045c6f7  sub     rax, rsi                     

...

0045c6fa  mov     r8, rax     
0045c6fd  shr     r8, 0x3     
0045c701  add     r8, 0x1     
...
0045c70f  mov     rdi, r8     
...
0045c718  shr     rdi, 0x2
loop_counter = (((sh_size-1) >> 3) + 1) >> 2

위 어셈블리를 해석하면 sh_size(rsi)을 통해 반복문 카운트 값을 계산합니다.

0045c720  movdqu  xmm0, xmmword [rdx+0x10]
0045c725  pxor    xmm6, xmm6
0045c729  add     rsi, 0x1                    
0045c72d  add     rdx, 0x20                   
0045c731  add     rax, 0x40                   (4)
...
0045c79d  movups  xmmword [rax-0x30], xmm5    (5.a)
0045c7a1  movups  xmmword [rax-0x40], xmm1    (5.b)
...
0045c7b1  movups  xmmword [rax-0x10], xmm4    (5.c)
0045c7b5  movups  xmmword [rax-0x20], xmm1    (5.d)
0045c7b9  cmp     rdi, rsi
0045c7bc  ja      0x45c720

이후 반복문을 통해 AVX 셔플 연산을 수행하는 데 (4),(5.a)~(5.d)를 통해 복사되는 heap 영역을 가리키는 rax가 0x40씩 증가하는 것을 확인할 수 있습니다.

buffer_size = (sh_size >> 3) << 4

loop_counter = (((sh_size-1) >> 3) + 1) >> 2
assumed_size = loop_counter * 0x40

앞서 sh_size 값을 통해 heap size와 반복문 횟수를 구하는 코드를 다시 살펴보면, 특정 sh_size 값을 통해 assumed_size가 buffer_size 보다 크게 만들어져 heap overflow가 발생합니다.

sh_size: 0x19, buffer size: 0x30, assumed size: 0x40
sh_size: 0x1a, buffer size: 0x30, assumed size: 0x40
sh_size: 0x1b, buffer size: 0x30, assumed size: 0x40
sh_size: 0x1c, buffer size: 0x30, assumed size: 0x40
sh_size: 0x1d, buffer size: 0x30, assumed size: 0x40
sh_size: 0x1e, buffer size: 0x30, assumed size: 0x40
sh_size: 0x1f, buffer size: 0x30, assumed size: 0x40
sh_size: 0x39, buffer size: 0x70, assumed size: 0x80
...

따라서 공격자는 ELF 파일의 .reloc 섹션 sh_size 값을 위와 같이 사용해 heap overflow로 인해 RCE를 트리거 할 수 있습니다.

==152542== Memcheck, a memory error detector
==152542== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al. 
==152542== Using Valgrind-3.23.0 and LibVEX; rerun with -h for copyright info
==152542== Command: ./nvdisasm-12.8.90 ./ac0bcb07
==152542== Parent PID: 132852
==152542== 
==152542== Invalid write of size 8
==152542==    at 0x45C7B1: ??? (in /home/dtatsis/nvdisasm/trizzle-new/0x444961-write8-a572c03c/nvdisasm-12.8.90)
==152542==    by 0x45CEA5: ??? (in /home/dtatsis/nvdisasm/trizzle-new/0x444961-write8-a572c03c/nvdisasm-12.8.90)
==152542==    by 0x40307A: ??? (in /home/dtatsis/nvdisasm/trizzle-new/0x444961-write8-a572c03c/nvdisasm-12.8.90)
==152542==    by 0x48A21C9: (below main) (libc_start_call_main.h:58)
==152542==  Address 0x4a99368 is 0 bytes after a block of size 184 alloc'd
==152542==    at 0x484680F: malloc (vg_replace_malloc.c:446)
==152542==    by 0x465719: ??? (in /home/dtatsis/nvdisasm/trizzle-new/0x444961-write8-a572c03c/nvdisasm-12.8.90)
==152542==    by 0x40A999: ??? (in /home/dtatsis/nvdisasm/trizzle-new/0x444961-write8-a572c03c/nvdisasm-12.8.90)
==152542==    by 0x45C6AD: ??? (in /home/dtatsis/nvdisasm/trizzle-new/0x444961-write8-a572c03c/nvdisasm-12.8.90)
==152542==    by 0x45CEA5: ??? (in /home/dtatsis/nvdisasm/trizzle-new/0x444961-write8-a572c03c/nvdisasm-12.8.90)
==152542==    by 0x40307A: ??? (in /home/dtatsis/nvdisasm/trizzle-new/0x444961-write8-a572c03c/nvdisasm-12.8.90)
==152542==    by 0x48A21C9: (below main) (libc_start_call_main.h:58)
==152542== 
==152542== 
==152542== HEAP SUMMARY:
==152542==     in use at exit: 24,556 bytes in 154 blocks
==152542==   total heap usage: 228 allocs, 74 frees, 39,566 bytes allocated
==152542== 
==152542== LEAK SUMMARY:
==152542==    definitely lost: 248 bytes in 8 blocks
==152542==    indirectly lost: 8,062 bytes in 123 blocks
==152542==      possibly lost: 16,246 bytes in 23 blocks
==152542==    still reachable: 0 bytes in 0 blocks
==152542==         suppressed: 0 bytes in 0 blocks
==152542== Rerun with --leak-check=full to see details of leaked memory
==152542== 
==152542== For lists of detected and suppressed errors, rerun with: -s
==152542== ERROR SUMMARY: 2 errors from 1 contexts (suppressed: 0 from 0)