diff --git a/src/binary-exploitation/libc-heap/heap-overflow.md b/src/binary-exploitation/libc-heap/heap-overflow.md index 606c9eb95..1d775e173 100644 --- a/src/binary-exploitation/libc-heap/heap-overflow.md +++ b/src/binary-exploitation/libc-heap/heap-overflow.md @@ -4,34 +4,34 @@ ## 기본 정보 -힙 오버플로우는 [**스택 오버플로우**](../stack-overflow/index.html)와 비슷하지만 힙에서 발생합니다. 기본적으로 이는 힙에 데이터를 저장하기 위해 예약된 공간이 있고 **저장된 데이터가 예약된 공간보다 더 크다는 것을 의미합니다.** +힙 오버플로우는 [**스택 오버플로우**](../stack-overflow/index.html)와 비슷하지만 힙에서 발생합니다. 기본적으로 이는 힙에 데이터를 저장하기 위해 예약된 공간이 있고 **저장된 데이터가 예약된 공간보다 크다는 것을 의미합니다.** -스택 오버플로우에서는 명령 포인터나 스택 프레임과 같은 일부 레지스터가 스택에서 복원될 것이며 이를 악용할 수 있는 가능성이 있습니다. 힙 오버플로우의 경우, **기본적으로 힙 청크에 저장된 민감한 정보는 없습니다**. 그러나 민감한 정보나 포인터일 수 있으므로, 이 취약점의 **중요성**은 **어떤 데이터가 덮어씌워질 수 있는지**와 공격자가 이를 어떻게 악용할 수 있는지에 따라 **달라집니다**. +스택 오버플로우에서는 명령 포인터나 스택 프레임과 같은 일부 레지스터가 스택에서 복원될 것이며 이를 악용할 수 있는 가능성이 있습니다. 힙 오버플로우의 경우, **기본적으로 힙 청크에 민감한 정보가 저장되지 않습니다.** 그러나 민감한 정보나 포인터일 수 있으므로, 이 취약점의 **중요성**은 **어떤 데이터가 덮어씌워질 수 있는지**와 공격자가 이를 어떻게 악용할 수 있는지에 따라 **달라집니다.** > [!TIP] -> 오버플로우 오프셋을 찾기 위해 [**스택 오버플로우**](../stack-overflow/index.html#finding-stack-overflows-offsets)와 같은 패턴을 사용할 수 있습니다. +> 오버플로우 오프셋을 찾기 위해 [**스택 오버플로우**](../stack-overflow/index.html#finding-stack-overflows-offsets)와 동일한 패턴을 사용할 수 있습니다. ### 스택 오버플로우 vs 힙 오버플로우 스택 오버플로우에서는 취약점이 발생할 때 스택에 존재할 데이터와 배열이 상당히 신뢰할 수 있습니다. 이는 스택이 선형적이며, 항상 충돌하는 메모리에서 증가하고, **프로그램 실행의 특정 위치에서 스택 메모리는 보통 유사한 종류의 데이터를 저장하며** 각 함수에서 사용되는 스택 부분의 끝에 특정 구조와 포인터가 있기 때문입니다. -그러나 힙 오버플로우의 경우, 사용된 메모리는 선형적이지 않으며 **할당된 청크는 보통 메모리의 분리된 위치에 있습니다** (서로 인접하지 않음) **빈과 영역**이 크기에 따라 할당을 분리하고 **이전의 해제된 메모리가** 새로운 청크를 할당하기 전에 사용되기 때문입니다. **어떤 객체가 힙 오버플로우에 취약한 객체와 충돌할 것인지 아는 것은 복잡합니다.** 따라서 힙 오버플로우가 발견되면, **오버플로우된 객체가 메모리에서 원하는 객체와 인접하도록 만드는 신뢰할 수 있는 방법을 찾아야 합니다.** +그러나 힙 오버플로우의 경우, 사용된 메모리는 선형적이지 않으며 **할당된 청크는 보통 메모리의 분리된 위치에 있습니다** (서로 인접하지 않음) **크기별로 할당을 구분하는 빈과 영역** 때문에 그리고 **이전의 해제된 메모리가** 새로운 청크를 할당하기 전에 사용되기 때문입니다. **힙 오버플로우에 취약한 객체와 충돌할 객체를 아는 것은 복잡합니다.** 따라서 힙 오버플로우가 발견되면, **오버플로우될 수 있는 객체와 메모리에서 다음에 올 객체를 만들기 위한 신뢰할 수 있는 방법을 찾아야 합니다.** -이를 위해 사용되는 기술 중 하나는 **힙 그루밍**입니다. 예를 들어 [**이 게시물**](https://azeria-labs.com/grooming-the-ios-kernel-heap/)에서 설명됩니다. 이 게시물에서는 iOS 커널에서 메모리 청크를 저장할 공간이 부족할 때, 커널 페이지로 확장하고 이 페이지를 예상 크기의 청크로 나누어 순서대로 사용되는 방법을 설명합니다 (iOS 버전 9.2까지, 이후에는 이러한 청크가 무작위로 사용되어 공격의 악용을 어렵게 만듭니다). +이를 위해 사용되는 기술 중 하나는 **힙 그루밍**입니다. 예를 들어 [**이 포스트**](https://azeria-labs.com/grooming-the-ios-kernel-heap/)에서 설명됩니다. 이 포스트에서는 iOS 커널에서 메모리 청크를 저장할 공간이 부족할 때, 커널 페이지로 확장하고 이 페이지를 예상 크기의 청크로 나누어 순서대로 사용된다고 설명합니다 (iOS 버전 9.2까지, 이후에는 이러한 청크가 무작위로 사용되어 공격의 악용을 어렵게 만듭니다). -따라서 힙 오버플로우가 발생하는 이전 게시물에서, 오버플로우된 객체가 피해자 객체와 충돌하도록 강제하기 위해 여러 **`kalloc`이 여러 스레드에 의해 강제되어 모든 무료 청크가 채워지고 새로운 페이지가 생성되도록 시도합니다.** +따라서 힙 오버플로우가 발생하는 이전 포스트에서는, 오버플로우된 객체가 피해자 객체와 충돌하도록 강제하기 위해 여러 **`kalloc`이 여러 스레드에 의해 강제되어 모든 무료 청크가 채워지고 새로운 페이지가 생성되도록 시도합니다.** 특정 크기의 객체로 이 채우기를 강제하기 위해, **iOS 맥 포트와 관련된 아웃 오브 라인 할당**이 이상적인 후보입니다. 메시지의 크기를 조정함으로써 `kalloc` 할당의 크기를 정확히 지정할 수 있으며, 해당 맥 포트가 파괴되면 해당 할당이 즉시 `kfree`로 반환됩니다. -그런 다음 이러한 자리 표시자 중 일부를 **해제**할 수 있습니다. **`kalloc.4096` 무료 목록은 후입선출 방식으로 요소를 해제합니다**, 이는 기본적으로 일부 자리 표시자가 해제되고 익스플로잇이 오버플로우에 취약한 객체를 할당하려고 할 때, 이 객체가 피해자 객체 뒤에 올 가능성이 높다는 것을 의미합니다. +그런 다음 이러한 자리 표시자 중 일부를 **해제**할 수 있습니다. **`kalloc.4096` 무료 목록은 후입선출 방식으로 요소를 해제합니다.** 이는 기본적으로 일부 자리 표시자가 해제되고 익스플로잇이 오버플로우에 취약한 객체를 할당하려고 할 때, 이 객체가 피해자 객체 뒤에 올 가능성이 높다는 것을 의미합니다. ### 예제 libc -[**이 페이지**](https://guyinatuxedo.github.io/27-edit_free_chunk/heap_consolidation_explanation/index.html)에서는 다음 청크의 prev in use 비트와 prev 크기를 덮어쓰는 방법을 보여주는 기본 힙 오버플로우 에뮬레이션을 찾을 수 있습니다. 이를 통해 **사용 중인 청크를 통합**(사용되지 않는 것으로 인식하게 함)하고 **다시 할당하여** 다른 포인터에서 사용 중인 데이터를 덮어쓸 수 있습니다. +[**이 페이지**](https://guyinatuxedo.github.io/27-edit_free_chunk/heap_consolidation_explanation/index.html)에서는 다음 청크의 prev in use 비트와 prev size의 위치를 덮어쓰는 방법을 보여주는 기본 힙 오버플로우 에뮬레이션을 찾을 수 있습니다. 이를 통해 **사용된 청크를 통합**(사용되지 않은 것으로 착각하게 만듦)하고 **다시 할당하여 다른 포인터에서 사용 중인 데이터를 덮어쓸 수 있습니다.** -[**protostar heap 0**](https://guyinatuxedo.github.io/24-heap_overflow/protostar_heap0/index.html)에서 또 다른 예제는 **힙 오버플로우**를 악용하여 승리 함수 호출을 통해 **플래그를 얻는** 매우 기본적인 CTF 예제를 보여줍니다. +[**protostar heap 0**](https://guyinatuxedo.github.io/24-heap_overflow/protostar_heap0/index.html)에서의 또 다른 예제는 **힙 오버플로우**를 악용하여 승리 함수 호출을 통해 **플래그를 얻는** 매우 기본적인 CTF 예제를 보여줍니다. -[**protostar heap 1**](https://guyinatuxedo.github.io/24-heap_overflow/protostar_heap1/index.html) 예제에서는 버퍼 오버플로우를 악용하여 **근처 청크에 주소를 덮어쓸 수 있는 방법**을 볼 수 있습니다. 여기서 **사용자로부터 임의의 데이터**가 기록될 것입니다. +[**protostar heap 1**](https://guyinatuxedo.github.io/24-heap_overflow/protostar_heap1/index.html) 예제에서는 버퍼 오버플로우를 악용하여 **근처 청크에 주소를 덮어쓸 수 있는 방법**을 보여줍니다. 여기서 **사용자의 임의 데이터**가 기록될 것입니다. ### 예제 ARM64 @@ -43,6 +43,38 @@ python3 -c 'print("/"*0x400+"/bin/ls\x00")' > hax.txt - [**Auth-or-out. Hack The Box**](https://7rocky.github.io/en/ctf/htb-challenges/pwn/auth-or-out/) - 우리는 Integer Overflow 취약점을 사용하여 Heap Overflow를 발생시킵니다. -- 우리는 오버플로우된 청크의 `struct` 내부에 있는 함수에 대한 포인터를 손상시켜 `system`과 같은 함수를 설정하고 코드 실행을 얻습니다. +- 우리는 오버플로된 청크의 `struct` 내부에 있는 함수에 대한 포인터를 손상시켜 `system`과 같은 함수를 설정하고 코드 실행을 얻습니다. + +### Real-World Example: CVE-2025-40597 – Misusing `__sprintf_chk` + +SonicWall SMA100 펌웨어 10.2.1.15에서 리버스 프록시 모듈 `mod_httprp.so`는 **0x80-byte** 힙 청크를 할당한 다음 `__sprintf_chk`를 사용하여 여러 문자열을 연결합니다: +```c +char *buf = calloc(0x80, 1); +/* … */ +__sprintf_chk(buf, /* destination (0x80-byte chunk) */ +-1, /* <-- size argument !!! */ +0, /* flags */ +"%s%s%s%s", /* format */ +"/", "https://", path, host); +``` +`__sprintf_chk`는 **_FORTIFY_SOURCE**의 일부입니다. 양수 `size` 매개변수를 받으면 결과 문자열이 대상 버퍼에 맞는지 확인합니다. **`-1` (0xFFFFFFFFFFFFFFFF)**을 전달함으로써 개발자들은 효과적으로 **경계 검사를 비활성화**하여 강화된 호출을 다시 고전적인, 안전하지 않은 `sprintf`로 되돌렸습니다. + +따라서 지나치게 긴 **`Host:`** 헤더를 제공하면 공격자가 **0x80 바이트 청크를 오버플로우하고 다음 힙 청크의 메타데이터를 덮어쓸 수 있습니다** (할당자에 따라 tcache / fast-bin / small-bin). 충돌은 다음과 같이 재현할 수 있습니다: +```python +import requests, warnings +warnings.filterwarnings('ignore') +requests.get( +'https://TARGET/__api__/', +headers={'Host': 'A'*750}, +verify=False +) +``` +실용적인 악용은 **heap grooming**을 필요로 하여 취약한 청크 바로 뒤에 제어 가능한 객체를 배치해야 하지만, 근본 원인은 두 가지 중요한 교훈을 강조합니다: + +1. **_FORTIFY_SOURCE는 만능 해결책이 아니다** – 오용하면 보호 기능이 무효화될 수 있다. +2. 항상 **올바른 버퍼 크기**를 `_chk` 계열에 전달해야 한다 (또는 더 나은 방법으로 `snprintf`를 사용하라). + +## References +* [watchTowr Labs – Stack Overflows, Heap Overflows and Existential Dread (SonicWall SMA100)](https://labs.watchtowr.com/stack-overflows-heap-overflows-and-existential-dread-sonicwall-sma100-cve-2025-40596-cve-2025-40597-and-cve-2025-40598/) {{#include ../../banners/hacktricks-training.md}} diff --git a/src/binary-exploitation/stack-overflow/README.md b/src/binary-exploitation/stack-overflow/README.md index 0862f0872..899dae055 100644 --- a/src/binary-exploitation/stack-overflow/README.md +++ b/src/binary-exploitation/stack-overflow/README.md @@ -6,11 +6,11 @@ A **stack overflow**는 프로그램이 할당된 것보다 더 많은 데이터를 스택에 기록할 때 발생하는 취약점입니다. 이 초과 데이터는 **인접한 메모리 공간을 덮어쓰게** 되어 유효한 데이터가 손상되고, 제어 흐름이 방해받으며, 잠재적으로 악성 코드가 실행될 수 있습니다. 이 문제는 종종 입력에 대한 경계 검사를 수행하지 않는 안전하지 않은 함수의 사용으로 인해 발생합니다. -이 덮어쓰기의 주요 문제는 **저장된 명령 포인터(EIP/RIP)**와 **저장된 기본 포인터(EBP/RBP)**가 이전 함수로 돌아가기 위해 **스택에 저장**되기 때문입니다. 따라서 공격자는 이를 덮어쓰고 **프로그램의 실행 흐름을 제어**할 수 있습니다. +이 덮어쓰기의 주요 문제는 **저장된 명령 포인터 (EIP/RIP)**와 **저장된 기준 포인터 (EBP/RBP)**가 이전 함수로 돌아가기 위해 **스택에 저장**되기 때문입니다. 따라서 공격자는 이를 덮어쓰고 **프로그램의 실행 흐름을 제어**할 수 있습니다. 취약점은 일반적으로 함수가 **스택에 할당된 양보다 더 많은 바이트를 복사할 때** 발생하여 스택의 다른 부분을 덮어쓸 수 있게 됩니다. -이와 같은 취약한 일반적인 함수는: **`strcpy`, `strcat`, `sprintf`, `gets`**... 또한 **`fgets`**, **`read` & `memcpy`**와 같이 **길이 인수**를 사용하는 함수는 지정된 길이가 할당된 것보다 클 경우 취약하게 사용될 수 있습니다. +이와 같은 취약점이 있는 일반적인 함수는: **`strcpy`, `strcat`, `sprintf`, `gets`**... 또한 **`fgets`**, **`read` & `memcpy`**와 같이 **길이 인수**를 사용하는 함수는 지정된 길이가 할당된 것보다 클 경우 취약하게 사용될 수 있습니다. 예를 들어, 다음 함수들이 취약할 수 있습니다: ```c @@ -23,11 +23,11 @@ printf("You entered: %s\n", buffer); ``` ### 스택 오버플로우 오프셋 찾기 -스택 오버플로우를 찾는 가장 일반적인 방법은 매우 큰 `A` 입력을 주는 것입니다 (예: `python3 -c 'print("A"*1000)'`) 그리고 **주소 `0x41414141`에 접근하려고 시도했다는** 것을 나타내는 `Segmentation Fault`를 기대하는 것입니다. +스택 오버플로우를 찾는 가장 일반적인 방법은 매우 큰 입력의 `A`s를 주는 것입니다 (예: `python3 -c 'print("A"*1000)'`) 그리고 **주소 `0x41414141`에 접근하려고 시도했다는** `Segmentation Fault`를 기대하는 것입니다. -게다가, 스택 오버플로우 취약점이 발견되면 **리턴 주소를 덮어쓸 수 있는 오프셋**을 찾아야 합니다. 이를 위해 일반적으로 **De Bruijn 수열**이 사용됩니다. 주어진 크기 _k_의 알파벳과 길이 _n_의 부분 수열에 대해, **길이 _n_의 모든 가능한 부분 수열이 정확히 한 번씩 연속 부분 수열로 나타나는 순환 수열**입니다. +게다가, 스택 오버플로우 취약점이 발견되면 **리턴 주소를 덮어쓸 수 있는 오프셋**을 찾아야 합니다. 이를 위해 일반적으로 **De Bruijn 시퀀스**가 사용됩니다. 주어진 크기 _k_의 알파벳과 길이 _n_의 부분 수열에 대해, **길이 _n_의 모든 가능한 부분 수열이 정확히 한 번씩 연속 부분 수열로 나타나는 순환 시퀀스**입니다. -이렇게 하면 EIP를 제어하는 데 필요한 오프셋을 수동으로 파악할 필요 없이 이러한 수열 중 하나를 패딩으로 사용하고, 그 패딩을 덮어쓴 바이트의 오프셋을 찾을 수 있습니다. +이렇게 하면 EIP를 제어하는 데 필요한 오프셋을 수동으로 파악할 필요 없이 이러한 시퀀스 중 하나를 패딩으로 사용하고, 그 패딩을 덮어쓴 바이트의 오프셋을 찾을 수 있습니다. 이를 위해 **pwntools**를 사용할 수 있습니다: ```python @@ -50,14 +50,14 @@ pattern search $rsp #Search the offset given the content of $rsp ``` ## 스택 오버플로우 악용 -오버플로우가 발생하는 동안(오버플로우 크기가 충분히 큰 경우) **스택** 내의 지역 변수 값을 **덮어쓸** 수 있습니다. **EBP/RBP와 EIP/RIP(또는 그 이상)**에 도달할 때까지 가능합니다.\ -이러한 유형의 취약점을 악용하는 가장 일반적인 방법은 **반환 주소를 수정하는 것**입니다. 이렇게 하면 함수가 끝날 때 **제어 흐름이 사용자가 지정한 위치로 리디렉션됩니다**. +오버플로우가 발생하는 동안(오버플로우 크기가 충분히 큰 경우) **스택** 내의 지역 변수 값을 **덮어쓸** 수 있습니다. **EBP/RBP 및 EIP/RIP(또는 그 이상)**에 도달할 때까지 가능합니다.\ +이러한 유형의 취약점을 악용하는 가장 일반적인 방법은 **반환 주소를 수정하는 것**입니다. 이렇게 하면 함수가 끝날 때 **제어 흐름이 사용자가 지정한 포인터로 리디렉션됩니다**. -그러나 다른 시나리오에서는 **스택의 일부 변수 값을 덮어쓰는 것**만으로도 악용이 충분할 수 있습니다(예: 쉬운 CTF 챌린지에서). +그러나 다른 시나리오에서는 **스택의 일부 변수 값을 덮어쓰는 것**만으로도 악용이 충분할 수 있습니다(예: 쉬운 CTF 챌린지). ### Ret2win -이러한 유형의 CTF 챌린지에서는 **결코 호출되지 않는** **함수**가 **바이너리 내부에** 있으며, **이 함수를 호출해야 승리**할 수 있습니다. 이러한 챌린지에서는 **반환 주소를 덮어쓸 오프셋을 찾고** 호출할 **함수의 주소를 찾기만 하면** 됩니다(일반적으로 [**ASLR**](../common-binary-protections-and-bypasses/aslr/index.html)가 비활성화됨). 따라서 취약한 함수가 반환될 때 숨겨진 함수가 호출됩니다: +이러한 유형의 CTF 챌린지에서는 **결코 호출되지 않는** **함수**가 바이너리 내에 있으며, **이 함수를 호출해야 승리**할 수 있습니다. 이러한 챌린지에서는 **반환 주소를 덮어쓸 오프셋을 찾고** 호출할 **함수의 주소를 찾기만 하면** 됩니다(일반적으로 [**ASLR**](../common-binary-protections-and-bypasses/aslr/index.html)가 비활성화됨). 따라서 취약한 함수가 반환될 때 숨겨진 함수가 호출됩니다: {{#ref}} ret2win/ @@ -73,7 +73,7 @@ stack-shellcode/ ### ROP 및 Ret2... 기술 -이 기술은 이전 기술의 주요 보호 장치를 우회하기 위한 기본 프레임워크입니다: **실행 불가능한 스택(NX)**. 그리고 기존의 바이너리 명령어를 악용하여 임의의 명령을 실행하는 여러 다른 기술(ret2lib, ret2syscall...)을 수행할 수 있게 해줍니다: +이 기술은 이전 기술의 주요 보호 장치를 우회하기 위한 기본 프레임워크입니다: **실행 불가능한 스택(NX)**. 그리고 이는 기존 바이너리의 명령어를 악용하여 임의의 명령을 실행하는 여러 다른 기술(ret2lib, ret2syscall...)을 수행할 수 있게 해줍니다: {{#ref}} ../rop-return-oriented-programing/ @@ -95,4 +95,32 @@ stack-shellcode/ ../common-binary-protections-and-bypasses/ {{#endref}} +### 실제 사례: CVE-2025-40596 (SonicWall SMA100) + +**`sscanf`는 신뢰할 수 없는 입력을 파싱하는 데 절대 신뢰해서는 안 된다**는 좋은 사례가 2025년 SonicWall의 SMA100 SSL-VPN 장치에서 나타났습니다. `/usr/src/EasyAccess/bin/httpd` 내의 취약한 루틴은 `/__api__/`로 시작하는 URI에서 버전과 엔드포인트를 추출하려고 시도합니다. +```c +char version[3]; +char endpoint[0x800] = {0}; +/* simplified proto-type */ +sscanf(uri, "%*[^/]/%2s/%s", version, endpoint); +``` +1. 첫 번째 변환(`%2s`)은 `version`에 **두** 바이트를 안전하게 저장합니다 (예: `"v1"`). +2. 두 번째 변환(`%s`)은 **길이 지정자가 없으므로**, `sscanf`는 **첫 번째 NUL 바이트**까지 계속 복사합니다. +3. `endpoint`는 **스택**에 위치하고 **0x800 바이트 길이**이므로, 0x800 바이트보다 긴 경로를 제공하면 버퍼 뒤에 있는 모든 것을 손상시킵니다 ‑ **스택 카나리**와 **저장된 반환 주소**를 포함하여. + +인증 **이전**에 충돌을 유발하기 위한 단일 행 개념 증명이 충분합니다: +```python +import requests, warnings +warnings.filterwarnings('ignore') +url = "https://TARGET/__api__/v1/" + "A"*3000 +requests.get(url, verify=False) +``` +스택 카나리가 프로세스를 중단시키더라도, 공격자는 여전히 **서비스 거부** 원시 기능을 얻습니다 (추가 정보 유출이 있을 경우, 코드 실행도 가능할 수 있습니다). 교훈은 간단합니다: + +* 항상 **최대 필드 너비**를 제공하세요 (예: `%511s`). +* `snprintf`/`strncpy_s`와 같은 더 안전한 대안을 선호하세요. + +## References +* [watchTowr Labs – Stack Overflows, Heap Overflows and Existential Dread (SonicWall SMA100)](https://labs.watchtowr.com/stack-overflows-heap-overflows-and-existential-dread-sonicwall-sma100-cve-2025-40596-cve-2025-40597-and-cve-2025-40598/) + {{#include ../../banners/hacktricks-training.md}}