11 KiB
Libc 보호 기능
htARTE (HackTricks AWS Red Team Expert)에서 **제로부터 영웅까지 AWS 해킹 배우기**!
HackTricks를 지원하는 다른 방법:
- 회사를 HackTricks에서 광고하거나 HackTricks를 PDF로 다운로드하고 싶다면 구독 요금제를 확인하세요!
- 공식 PEASS & HackTricks 스왜그를 구입하세요
- The PEASS Family를 발견하세요, 당사의 독점 NFTs 컬렉션
- 💬 Discord 그룹 또는 텔레그램 그룹에 가입하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 저장소에 PR을 제출하여 해킹 트릭을 공유하세요.
청크 정렬 강제
Malloc은 메모리를 8바이트(32비트) 또는 16바이트(64비트) 그룹으로 할당합니다. 이는 32비트 시스템에서 청크의 끝이 0x8에 맞춰지고, 64비트 시스템에서는 0x0에 맞춰져야 함을 의미합니다. 이 보안 기능은 각 청크가 특정 위치에서 올바르게 정렬되어 있는지 확인하고 나서 해당 위치에서 포인터를 사용하기 전에 체크합니다.
보안 이점
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)**에서 제공되는 기존의 무작위성을 활용합니다. 이는 공격자가 프로세스의 메모리 레이아웃을 예측하기 어렵게 만들기 위해 프로그램이 사용하는 주소를 무작위로 바꾸는 기술입니다.
난독화된 포인터를 해제하여 원래 주소를 검색하는 것은 동일한 XOR 연산을 사용하여 수행됩니다. 여기서 난독화된 포인터는 공식에서 P로 취급되며 변경되지 않은 저장 위치(L)와 XOR 연산을 수행하면 원래 포인터가 공개됩니다. 이 난독화 및 해독의 대칭성은 시스템이 메모리 포인터를 효율적으로 인코딩하고 디코딩할 수 있도록 하면서 메모리 포인터를 조작하는 공격에 대한 보안을 크게 향상시킵니다.
보안 이점
포인터 난독화는 힙 관리에서의 부분 및 전체 포인터 덮어쓰기를 방지하고 보안을 크게 향상시킵니다. 이 기능은 여러 가지 방법으로 악용 기술에 영향을 미칩니다:
- 바이트 바이트 상대적 덮어쓰기 방지: 이전에 공격자는 정확한 주소를 알지 못해도 힙 청크를 다른 위치로 리디렉션할 수 있는 일부 포인터의 일부를 변경할 수 있었습니다. 이러한 상대적 덮어쓰기는 더 이상 힙 누출 없이는 브루트 포싱이 필요하며, 이는 성공 가능성을 크게 줄입니다.
- Tcache Bin/Fastbin 공격의 어려움 증가: fastbin 또는 tcache 항목을 조작하여 함수 포인터(예:
__malloc_hook
)를 덮어쓰는 일반적인 공격이 방해됩니다. 예를 들어, 공격은 LibC 주소를 누출하고 tcache bin에 청크를 해제한 다음 Fd 포인터를__malloc_hook
로 리디렉션하여 임의의 코드 실행을 수행할 수 있습니다. 포인터 난독화로 인해 이러한 포인터는 올바르게 난독화되어야 하므로 정확한 조작을 위해 힙 누출이 필요하며, 이로써 악용 장벽이 높아집니다. - 비힙 위치에서의 힙 누출 요구: 비힙 영역(스택, .bss 섹션 또는 PLT/GOT)에 가짜 청크를 만드는 것은 이제 포인터 난독화가 필요하기 때문에 이제 힙 누출이 필요합니다. 이는 LibC 주소를 조작하는 것과 유사하게 이러한 영역을 악용하는 복잡성을 증가시킵니다.
- 힙 주소 누출이 더 어려워짐: 포인터 난독화는 fastbin 및 tcache bin의 Fd 포인터를 힙 주소 누출 소스로 사용하는 유용성을 제한합니다. 그러나 정렬되지 않은, 작은 및 큰 bin의 포인터는 여전히 난독화되지 않았으므로 주소 누출에 사용할 수 있습니다. 이 변화로 인해 공격자는 이러한 bin을 탐색하여 악용 가능한 정보를 찾아야 하지만 일부 기술은 여전히 누출 전에 포인터를 해독할 수 있을 수 있지만 제약이 따릅니다.
힙 누출로 포인터 해독
{% hint style="danger" %} 프로세스에 대한 자세한 설명을 보려면 여기에서 원본 게시물을 확인하세요. {% endhint %}
알고리즘 개요
포인터를 난독화하고 해독하는 데 사용되는 공식은 다음과 같습니다:
New_Ptr = (L >> 12) XOR P
여기서 L은 저장 위치이고 P는 Fd 포인터입니다. L을 오른쪽으로 12비트 이동하여 XOR 연산을 수행하면 P의 가장 상위 12비트를 획득하게 됩니다. 이는 XOR의 특성 때문에 비트가 자신과 XOR되면 0이 출력되기 때문입니다.
알고리즘의 주요 단계:
- 가장 상위 비트의 초기 누출: 이동된 L을 P와 XOR하여 P의 상위 12비트를 획득합니다. 이동된 L의 부분은 0이 되므로 P의 해당 비트는 변경되지 않습니다.
- 포인터 비트 복구: XOR가 역으로 가능하므로 결과와 하나의 피연산자를 알면 다른 피연산자를 계산할 수 있습니다. 이 속성은 알려진 비트 세트를 알고 있는 상태에서 난독화된 포인터의 일부와 연속적으로 XOR하여 P의 전체 비트 세트를 추론하는 데 사용됩니다.
- 반복적인 해독: 이 과정은 이전 단계에서 발견된 P의 비트를 사용하여 난독화된 포인터의 다음 세그먼트를 해독하는 데 반복됩니다. 모든 비트가 복구될 때까지 이 과정을 반복합니다.
- 결정론적 비트 처리: 이동으로 인해 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 기본 주소를 알고 임의의 메모리 위치를 읽을 수 있는 다중 스레드 응용 프로그램에서 포인터를 제어적으로 조작하여 포인터 가드 보호를 우회할 수 있습니다.