[하루한줄] 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)
본 글은 CC BY-SA 4.0 라이선스로 배포됩니다. 공유 또는 변경 시 반드시 출처를 남겨주시기 바랍니다.