Libc Protections
청크 정렬 강제
Malloc은 메모리를 8바이트(32비트) 또는 16바이트(64비트) 그룹으로 할당합니다. 이는 32비트 시스템에서 청크의 끝이 0x8에 맞춰지고, 64비트 시스템에서는 0x0에 맞춰져야 함을 의미합니다. 이 보안 기능은 각 청크가 특정 위치에 올바르게 정렬되어 있는지 확인한 후에 bin에서 포인터를 사용하기 전에 이를 검사합니다.
보안 이점
64비트 시스템에서 청크 정렬을 강제함으로써 Malloc의 보안이 크게 향상됩니다. 이는 가짜 청크의 배치를 주소 중 16개 중 1개만 허용하도록 하여 공격을 복잡하게 만들어 성공적인 공격을 더 어렵게 합니다. 특히 사용자가 입력 값을 제어할 수 있는 상황에서 공격을 더 복잡하고 성공하기 어렵게 만듭니다.
__malloc_hook에 대한 Fastbin 공격
Malloc의 새로운 정렬 규칙은 __malloc_hook
을 이용한 공격을 방지합니다. 이전에 공격자는 청크 크기를 조작하여 이 함수 포인터를 덮어쓰고 코드 실행을 얻을 수 있었습니다. 이제 엄격한 정렬 요구 사항으로 인해 이러한 조작이 더 이상 유효하지 않아져 공통적인 공격 경로를 차단하고 전반적인 보안을 향상시킵니다.
fastbins 및 tcache에서의 포인터 Mangling
포인터 Mangling은 메모리 관리 작업에서 fastbin 및 tcache Fd 포인터를 보호하는 보안 기능입니다. 이 기술은 메모리 정보 누출이 필요 없거나 알려진 위치에 직접적으로 메모리 위치를 조작하는 일부 메모리 공격 전술을 방지하는 데 도움이 됩니다.
이 기술의 핵심은 다음과 같은 난해한 공식입니다:
New_Ptr = (L >> 12) XOR P
L은 포인터의 저장 위치입니다.
P는 실제 fastbin/tcache Fd 포인터입니다.
저장 위치(L)를 XOR 연산 전에 12비트 오른쪽으로 비트 이동시키는 이유는 중요합니다. 이 조작은 주소의 가장 낮은 12비트의 결정적인 성격에 대한 취약점을 해결합니다. 이 비트를 이동시킴으로써 예측 가능한 부분을 제거하여 새로운, 혼란스러운 포인터의 무작위성을 향상시키고 이 비트의 예측 가능성에 의존하는 공격에 대비합니다.
이 혼란스러운 포인터는 프로그램이 사용하는 주소를 무작위로 변경하는 **주소 공간 배치 무작위화(ASLR)**에 의해 제공되는 기존의 무작위성을 활용합니다. 이는 공격자가 프로세스의 메모리 레이아웃을 예측하기 어렵게 만들기 위해 프로그램이 사용하는 주소를 무작위로 변경합니다.
포인터 Mangling은 힙 관리에서의 부분 및 전체 포인터 덮어쓰기를 방지하기 위해 목표로 합니다. 이 기능은 여러 가지 방법으로 공격 기술에 영향을 미칩니다:
바이트 바이트 상대적 덮어쓰기 방지: 이전에 공격자는 정확한 주소를 알지 못해 힙 청크를 다른 위치로 재지정하는 부분적인 포인터 변경을 할 수 있었습니다. 이는 누출 없는 House of Roman 공격에서 나타나는 기술입니다. 포인터 Mangling으로 이러한 상대적 덮어쓰기는 이제 힙 누출 없이는 브루트 포싱이 필요하므로 성공 가능성이 크게 줄어듭니다.
Tcache Bin/Fastbin 공격의 어려움 증가: fastbin 또는 tcache 항목을 조작하여 함수 포인터(예:
__malloc_hook
)를 덮어쓰는 일반적인 공격이 방해됩니다. 예를 들어, 공격은 LibC 주소를 누출하고 tcache bin에 청크를 해제한 다음 Fd 포인터를__malloc_hook
로 재지정하여 임의의 코드 실행을 수행할 수 있습니다. 포인터 Mangling으로 인해 이러한 포인터는 올바르게 Mangling되어야 하므로 정확한 조작을 위해 힙 누출이 필요하며, 이로써 공격 장벽이 높아집니다.비힙 위치에서의 힙 누출 요구: 비힙 영역(스택, .bss 섹션 또는 PLT/GOT)에 가짜 청크를 만드는 것은 이제 포인터 Mangling이 필요하기 때문에 힙 누출이 필요합니다. 이는 LibC 주소를 조작하는 것과 유사하게 이러한 영역을 공격하는 복잡성을 증가시킵니다.
힙 주소 누출이 더 어려워짐: 포인터 Mangling은 fastbin 및 tcache bin의 Fd 포인터를 힙 주소 누출 소스로 제한합니다. 그러나 정렬되지 않은, 작은 및 큰 bin의 포인터는 Mangling되지 않으므로 여전히 주소 누출에 사용할 수 있습니다. 이 변화로 인해 공격자는 이러한 bin에서 취약한 정보를 탐색해야 하지만 일부 기술은 여전히 누출 전에 포인터를 Demangling할 수 있지만 제약이 있습니다.
힙 누출로 포인터 Demangling
프로세스에 대한 더 나은 설명을 위해 여기의 원본 게시물을 확인하세요.
알고리즘 개요
포인터를 Mangling하고 Demangling하는 데 사용되는 공식은 다음과 같습니다:
New_Ptr = (L >> 12) XOR P
여기서 L은 저장 위치이고 P는 Fd 포인터입니다. L을 오른쪽으로 12비트 이동시킨 후 P와 XOR 연산을 수행하면, XOR가 자신과 XOR되었을 때 0을 출력하는 특성으로 인해 P의 가장 상위 12비트를 효과적으로 얻게 됩니다.
알고리즘의 주요 단계:
가장 상위 비트의 초기 누출: 이동된 L을 P와 XOR하여, 이동된 L의 부분이 0이 되어 P의 해당 비트가 변경되지 않도록 하여 P의 상위 12비트를 얻습니다.
포인터 비트 복구: XOR가 역으로 가능하므로 결과와 하나의 피연산자를 알면 다른 피연산자를 계산할 수 있습니다. 이 속성을 사용하여 Mangling된 포인터의 일부와 알려진 비트 세트를 연속적으로 XOR하여 P의 전체 비트 세트를 추론합니다.
반복적인 Demangling: 이 과정은 이전 단계에서 발견된 P의 비트를 사용하여 Mangling된 포인터의 다음 세그먼트를 디코딩하는 데 사용되며, 모든 비트가 복구될 때까지 반복됩니다.
결정적 비트 처리: 이동으로 인해 L의 최종 12비트가 손실되지만, 이들은 결정적이며 후처리에서 재구성할 수 있습니다.
이 알고리즘의 구현은 여기에서 찾을 수 있습니다: https://github.com/mdulin2/mangle
포인터 가드
포인터 가드는 glibc에서 사용되는 익스플로잇 방지 기술로, 특히 atexit()
와 같은 라이브러리 호출에 의해 등록된 함수 포인터를 보호하는 데 사용됩니다. 이 보호는 포인터를 스레드 데이터(fs:0x30
)에 저장된 비밀과 XOR 연산 및 비트 회전을 적용하여 포인터를 섞는 것을 포함합니다. 이 메커니즘은 함수 포인터를 덮어쓰는 공격자로부터 제어 흐름을 탈취하는 것을 방지하기 위한 것입니다.
리크를 사용한 포인터 가드 우회
포인터 가드 작업 이해: 포인터의 섞기 작업은
PTR_MANGLE
매크로를 사용하여 수행되며, 이 매크로는 포인터를 64비트 비밀로 XOR 연산한 다음 0x11 비트를 왼쪽으로 회전합니다. 원래 포인터를 복구하기 위한 역 연산은PTR_DEMANGLE
에서 처리됩니다.공격 전략: 이 공격은 알려진 평문 접근 방식에 기반하며, 공격자는 섞인 포인터의 원본과 섞인 버전을 알아야만 섞는 데 사용된 비밀을 추론할 수 있습니다.
알려진 평문 활용:
고정 함수 포인터 식별: glibc 소스 코드를 조사하거나
__libc_pthread_functions
와 같은 초기화된 함수 포인터 테이블을 살펴봄으로써 공격자는 예측 가능한 함수 포인터를 찾을 수 있습니다.비밀 계산:
__pthread_attr_destroy
와 같은 알려진 함수 포인터 및 해당 함수 포인터 테이블에서의 섞인 버전을 사용하여, 섞인 포인터를 역 회전(오른쪽 회전)하고 해당 함수의 주소와 XOR 연산하여 비밀을 계산할 수 있습니다.
대체 평문: 공격자는 알려진 값(예: 0 또는 -1)으로 포인터를 섞어서 메모리에서 식별 가능한 패턴을 생성하는지 확인하기 위해 실험할 수 있으며, 이러한 패턴이 메모리 덤프에서 발견될 때 비밀을 노출시킬 수 있습니다.
실제 적용: 비밀을 계산한 후, 공격자는 libc 기본 주소를 알고 임의의 메모리 위치를 읽을 수 있는 다중 스레드 응용 프로그램에서 포인터를 제어적으로 조작하여 포인터 가드 보호를 우회할 수 있습니다.
참고 자료
Last updated