Translated ['src/binary-exploitation/basic-stack-binary-exploitation-met

This commit is contained in:
Translator 2025-08-18 20:36:05 +00:00
parent 85a2a3b86e
commit 88d329ce67

View File

@ -47,12 +47,14 @@ Segment Sections...
바이너리를 메모리에 로드하는 데 사용할 로더의 경로를 나타냅니다.
> 팁: 정적으로 링크된 또는 정적-PIE 바이너리는 `INTERP` 항목이 없습니다. 이러한 경우에는 동적 로더가 관여하지 않으므로 이를 기반으로 하는 기술(예: `ret2dlresolve`)이 비활성화됩니다.
### LOAD
이 헤더는 **바이너리를 메모리에 로드하는 방법**을 나타내는 데 사용됩니다.\
**LOAD** 헤더는 **메모리**의 영역(크기, 권한 및 정렬)을 나타내고, 그곳에 복사할 ELF **바이너리의 바이트**를 나타냅니다.
**LOAD** 헤더는 **메모리**의 영역(크기, 권한 및 정렬)을 나타내고 ELF **바이너리에서 복사할 바이트**를 나타냅니다.
예를 들어, 두 번째 헤더는 크기가 0x1190이며, 0x1fc48에 위치해야 하고, 읽기 및 쓰기 권한을 가지며, 오프셋 0xfc48에서 0x528로 채워집니다(모든 예약된 공간을 채우지 않습니다). 이 메모리에는 섹션 `.init_array .fini_array .dynamic .got .data .bss`가 포함됩니다.
예를 들어, 두 번째 헤더는 크기가 0x1190이며, 0x1fc48에 위치해야 하고 읽기 및 쓰기 권한을 가지며 오프셋 0xfc48에서 0x528로 채워집니다(모든 예약된 공간을 채우지 않). 이 메모리에는 섹션 `.init_array .fini_array .dynamic .got .data .bss`가 포함됩니다.
### DYNAMIC
@ -62,6 +64,12 @@ Segment Sections...
이것은 바이너리에 대한 공급업체 메타데이터 정보를 저장합니다.
- x86-64에서 `readelf -n``.note.gnu.property` 내에 `GNU_PROPERTY_X86_FEATURE_1_*` 플래그를 표시합니다. `IBT` 및/또는 `SHSTK`가 보이면, 바이너리는 CET(간접 분기 추적 및/또는 섀도 스택)으로 빌드되었습니다. 이는 ROP/JOP에 영향을 미치며, 간접 분기 대상은 `ENDBR64` 명령어로 시작해야 하고 반환은 섀도 스택에 대해 확인됩니다. 자세한 내용과 우회 노트는 CET 페이지를 참조하세요.
{{#ref}}
../common-binary-protections-and-bypasses/cet-and-shadow-stack.md
{{#endref}}
### GNU_EH_FRAME
디버거와 C++ 예외 처리 런타임 함수에서 사용하는 스택 언와인드 테이블의 위치를 정의합니다.
@ -70,13 +78,21 @@ Segment Sections...
스택 실행 방지 방어의 구성을 포함합니다. 활성화되면 바이너리는 스택에서 코드를 실행할 수 없습니다.
- `readelf -l ./bin | grep GNU_STACK`로 확인하세요. 테스트 중에 강제로 전환하려면 `execstack -s|-c ./bin`을 사용할 수 있습니다.
### GNU_RELRO
바이너리의 RELRO(재배치 읽기 전용) 구성을 나타냅니다. 이 보호는 프로그램이 로드된 후 실행되기 전에 메모리의 특정 섹션(예: `GOT` 또는 `init``fini` 테이블)을 읽기 전용으로 표시합니다.
이전 예제에서는 0x3b8 바이트를 0x1fc48 읽기 전용으로 복사하여 섹션 `.init_array .fini_array .dynamic .got .data .bss`에 영향을 미칩니다.
이전 예제에서는 0x3b8 바이트를 0x1fc48 읽기 전용으로 복사하여 섹션 `.init_array .fini_array .dynamic .got .data .bss`에 영향을 미칩니다.
RELRO는 부분적이거나 전체적일 수 있으며, 부분 버전은 **`.plt.got`** 섹션을 보호하지 않으며, 이는 **지연 바인딩**에 사용되며 라이브러리의 주소를 처음 검색할 때 이 메모리 공간에 **쓰기 권한**이 필요합니다.
RELRO는 부분적 또는 전체적일 수 있으며, 부분 버전은 **`.plt.got`** 섹션을 보호하지 않으며, 이는 **지연 바인딩**에 사용되며 라이브러리의 주소를 처음 검색할 때 이 메모리 공간에 **쓰기 권한**이 필요합니다.
> 익스플로잇 기술 및 최신 우회 노트에 대한 정보는 전용 페이지를 확인하세요:
{{#ref}}
../common-binary-protections-and-bypasses/relro.md
{{#endref}}
### TLS
@ -145,11 +161,11 @@ CONTENTS, READONLY
25 .gnu_debuglink 00000034 0000000000000000 0000000000000000 000101bc 2**2
CONTENTS, READONLY
```
그것은 위치, 오프셋, 권한뿐만 아니라 섹션의 **데이터 유형**도 나타냅니다.
It also indicates the location, offset, permissions but also the **데이터 유형** it section has.
### 메타 섹션
- **문자열 테이블**: ELF 파일에 필요한 모든 문자열을 포함하고 있습니다(하지만 프로그램에서 실제로 사용되는 문자열은 아닙니다). 예를 들어, `.text` 또는 `.data`와 같은 섹션 이름을 포함합니다. 그리고 문자열 테이블에서 `.text`가 오프셋 45에 있다면 **이름** 필드에 숫자 **45**를 사용합니다.
- **문자열 테이블**: ELF 파일에 필요한 모든 문자열을 포함하고 있습니다(하지만 프로그램에서 실제로 사용되는 문자열은 아닙니다). 예를 들어, `.text` 또는 `.data`와 같은 섹션 이름을 포함합니다. 그리고 문자열 테이블에서 `.text`45의 오프셋에 있다면 **이름** 필드에 숫자 **45**를 사용합니다.
- 문자열 테이블이 어디에 있는지 찾기 위해 ELF는 문자열 테이블에 대한 포인터를 포함합니다.
- **심볼 테이블**: 이름(문자열 테이블의 오프셋), 주소, 크기 및 심볼에 대한 추가 메타데이터와 같은 심볼에 대한 정보를 포함합니다.
@ -188,12 +204,16 @@ Num: Value Size Type Bind Vis Ndx Name
각 심볼 항목은 다음을 포함합니다:
- **이름**
- **바인딩 속성** (약한, 로컬 또는 전역): 로컬 심볼은 프로그램 자체에서만 접근할 수 있으며, 전역 심볼은 프로그램 외부에서 공유됩니다. 약한 객체는 예를 들어 다른 함수에 의해 재정의될 수 있는 함수입니다.
- **유형**: NOTYPE (유형 지정되지 않음), OBJECT (전역 데이터 변수), FUNC (함수), SECTION (섹션), FILE (디버거용 소스 코드 파일), TLS (스레드 로컬 변수), GNU_IFUNC (재배치를 위한 간접 함수)
- **바인딩 속성** (약한, 로컬 또는 글로벌): 로컬 심볼은 프로그램 자체에서만 접근할 수 있으며, 글로벌 심볼은 프로그램 외부에서 공유됩니다. 약한 객체는 예를 들어 다른 함수로 재정의될 수 있는 함수입니다.
- **유형**: NOTYPE (유형 지정되지 않음), OBJECT (글로벌 데이터 변수), FUNC (함수), SECTION (섹션), FILE (디버거용 소스 코드 파일), TLS (스레드 로컬 변수), GNU_IFUNC (재배치를 위한 간접 함수)
- **섹션** 인덱스 (위치)
- **값** (메모리 내 주소)
- **크기**
#### GNU 심볼 버전 관리 (dynsym/dynstr/gnu.version)
현대 glibc는 심볼 버전을 사용합니다. `.gnu.version``.gnu.version_r`에서 항목을 볼 수 있으며, `strlen@GLIBC_2.17`와 같은 심볼 이름이 있습니다. 동적 링커는 심볼을 해결할 때 특정 버전을 요구할 수 있습니다. 수동 재배치를 만들 때 (예: ret2dlresolve) 올바른 버전 인덱스를 제공해야 하며, 그렇지 않으면 해결이 실패합니다.
## 동적 섹션
```
readelf -d lnstat
@ -229,11 +249,28 @@ Tag Type Name/Value
0x000000006ffffff9 (RELACOUNT) 15
0x0000000000000000 (NULL) 0x0
```
NEEDED 디렉토리는 프로그램이 계속 진행하기 위해 **언급된 라이브러리를 로드해야 함**을 나타냅니다. NEEDED 디렉토리는 공유 **라이브러리가 완전히 작동하고 사용 준비되었을 때** 완료됩니다.
The NEEDED directory indicates that the program **필요한 라이브러리를 로드해야 함**을 나타냅니다. NEEDED 디렉토리는 공유 **라이브러리가 완전히 작동하고 준비되었을 때** 완료됩니다.
## Relocations
### 동적 로더 검색 순서 (RPATH/RUNPATH, $ORIGIN)
로더는 또한 로드한 후 종속성을 재배치해야 합니다. 이러한 재배치는 REL 또는 RELA 형식의 재배치 테이블에 표시되며, 재배치 수는 동적 섹션 RELSZ 또는 RELASZ에 제공됩니다.
항목 `DT_RPATH` (사용 중단) 및/또는 `DT_RUNPATH`는 동적 로더가 종속성을 검색하는 위치에 영향을 미칩니다. 대략적인 순서:
- `LD_LIBRARY_PATH` (setuid/sgid 또는 기타 "보안 실행" 프로그램에 대해 무시됨)
- `DT_RPATH` (단, `DT_RUNPATH`가 없을 경우)
- `DT_RUNPATH`
- `ld.so.cache`
- 기본 디렉토리 예: `/lib64`, `/usr/lib64` 등.
`$ORIGIN`은 RPATH/RUNPATH 내에서 주요 객체의 디렉토리를 참조하는 데 사용될 수 있습니다. 공격자의 관점에서 이는 파일 시스템 레이아웃이나 환경을 제어할 때 중요합니다. 강화된 바이너리(AT_SECURE)의 경우 대부분의 환경 변수는 로더에 의해 무시됩니다.
- 검사 방법: `readelf -d ./bin | egrep -i 'r(path|unpath)'`
- 빠른 테스트: `LD_DEBUG=libs ./bin 2>&1 | grep -i find` (검색 경로 결정 표시)
> Priv-esc 팁: 귀하가 소유한 쓰기 가능한 RUNPATH 또는 잘못 구성된 `$ORIGIN` 상대 경로를 악용하는 것을 선호하십시오. LD_PRELOAD/LD_AUDIT는 보안 실행(setuid) 컨텍스트에서 무시됩니다.
## 재배치
로더는 종속성을 로드한 후에도 재배치해야 합니다. 이러한 재배치는 REL 또는 RELA 형식의 재배치 테이블에 표시되며, 재배치 수는 동적 섹션 RELSZ 또는 RELASZ에 제공됩니다.
```
readelf -r lnstat
@ -274,7 +311,6 @@ Offset Info Type Sym. Value Sym. Name + Addend
00000001fea0 000900000402 R_AARCH64_JUMP_SL 0000000000000000 perror@GLIBC_2.17 + 0
00000001fea8 000b00000402 R_AARCH64_JUMP_SL 0000000000000000 __cxa_finalize@GLIBC_2.17 + 0
00000001feb0 000c00000402 R_AARCH64_JUMP_SL 0000000000000000 putc@GLIBC_2.17 + 0
00000001feb8 000d00000402 R_AARCH64_JUMP_SL 0000000000000000 opendir@GLIBC_2.17 + 0
00000001fec0 000e00000402 R_AARCH64_JUMP_SL 0000000000000000 fputc@GLIBC_2.17 + 0
00000001fec8 001100000402 R_AARCH64_JUMP_SL 0000000000000000 snprintf@GLIBC_2.17 + 0
00000001fed0 001200000402 R_AARCH64_JUMP_SL 0000000000000000 __snprintf_chk@GLIBC_2.17 + 0
@ -308,23 +344,41 @@ Offset Info Type Sym. Value Sym. Name + Addend
```
### 정적 재배치
프로그램이 **선호하는 주소**(보통 0x400000)와 다른 위치에 로드되면(주소가 이미 사용 중이거나 **ASLR** 또는 기타 이유로), 정적 재배치가 **포인터를 수정**하여 이진 파일이 선호하는 주소에 로드될 것으로 예상했던 값을 수정합니다.
프로그램이 **선호하는 주소**(보통 0x400000)와 다른 위치에 로드되면(주소가 이미 사용 중이거나 **ASLR** 또는 기타 이유로), 정적 재배치는 선호하는 주소에 바이너리가 로드될 것으로 예상했던 값을 가진 포인터를 **수정**합니다.
예를 들어, `R_AARCH64_RELATIV` 유형의 섹션은 재배치 편향에 추가 값(addend value)을 더한 주소를 수정해야 합니다.
예를 들어, `R_AARCH64_RELATIV` 유형의 섹션은 재배치 편향에 주소를 수정하고 추가 값(addend value)을 더해야 합니다.
### 동적 재배치 및 GOT
재배치는 외부 기호(종속성의 함수와 같은)를 참조할 수도 있습니다. 예를 들어, libC의 malloc 함수입니다. 그런 다음 로더는 libC를 로드할 때 malloc 함수가 로드된 주소를 확인하고, 이 주소를 GOT(전역 오프셋 테이블) 테이블(재배치 테이블에 표시됨)에 기록합니다. 여기서 malloc의 주소가 지정되어야 합니다.
재배치는 외부 기호(종속성의 함수와 같은)를 참조할 수도 있습니다. 예를 들어, libC의 malloc 함수입니다. 그런 다음 로더는 libC를 로드할 때 malloc 함수가 로드된 주소를 확인하고, 이 주소를 GOT(전역 오프셋 테이블) 테이블에 기록합니다(재배치 테이블에 표시된 대로) 여기서 malloc의 주소가 지정되어야 합니다.
### 프로시저 링크 테이블
PLT 섹션은 지연 바인딩을 수행할 수 있게 해주며, 이는 함수의 위치 해석이 처음 접근할 때 수행된다는 것을 의미합니다.
PLT 섹션은 지연 바인딩(lazy binding)을 수행할 수 있게 해주며, 이는 함수의 위치 해석이 처음 접근할 때 수행된다는 것을 의미합니다.
따라서 프로그램이 malloc을 호출할 때, 실제로는 PLT의 `malloc`에 해당하는 위치(`malloc@plt`)를 호출합니다. 처음 호출될 때 `malloc`의 주소를 해석하고 저장하므로 다음에 `malloc`이 호출될 때는 PLT 코드 대신 그 주소가 사용됩니다.
따라서 프로그램이 malloc을 호출할 때, 실제로는 PLT의 `malloc`에 해당하는 위치(`malloc@plt`)를 호출합니다. 처음 호출할 때 `malloc`의 주소를 해석하고 저장하므로 다음에 `malloc`이 호출될 때는 PLT 코드 대신 그 주소가 사용됩니다.
#### 익스플로잇에 영향을 미치는 현대 링크 동작
- `-z now` (전체 RELRO)는 지연 바인딩을 비활성화합니다; PLT 항목은 여전히 존재하지만 GOT/PLT는 읽기 전용으로 매핑되므로 **GOT 덮어쓰기** 및 **ret2dlresolve**와 같은 기술은 주요 바이너리에 대해 작동하지 않습니다(라이브러리는 여전히 부분적으로 RELRO일 수 있습니다). 참조:
{{#ref}}
../common-binary-protections-and-bypasses/relro.md
{{#endref}}
- `-fno-plt`는 컴파일러가 PLT 스텁을 거치지 않고 **GOT 항목을 직접** 통해 외부 함수를 호출하게 만듭니다. `call func@plt` 대신 `mov reg, [got]; call reg`와 같은 호출 시퀀스를 보게 될 것입니다. 이는 추측 실행 남용을 줄이고 PLT 스텁 주위의 ROP 가젯 탐색을 약간 변경합니다.
- PIE vs static-PIE: PIE(ET_DYN 및 `INTERP` 포함)는 동적 로더가 필요하며 일반적인 PLT/GOT 기계 장치를 지원합니다. Static-PIE(ET_DYN 및 `INTERP` 없음)는 커널 로더에 의해 재배치가 적용되며 `ld.so`가 없습니다; 런타임에서 PLT 해석이 없을 것으로 예상됩니다.
> GOT/PLT가 옵션이 아닌 경우, 다른 쓰기 가능한 코드 포인터로 전환하거나 libc로 고전적인 ROP/SROP를 사용하십시오.
{{#ref}}
../arbitrary-write-2-exec/aw2exec-got-plt.md
{{#endref}}
## 프로그램 초기화
프로그램이 로드된 후 실행할 시간입니다. 그러나 실행되는 첫 번째 코드는 **항상 `main`** 함수가 아닙니다. 예를 들어 C++에서 **전역 변수가 클래스의 객체인 경우**, 이 객체는 main이 실행되기 **전에** **초기화**되어야 합니다.
프로그램이 로드된 후 실행할 시간입니다. 그러나 실행되는 첫 번째 코드는 항상 `main` 함수가 아닙니다. 예를 들어 C++에서 **전역 변수가 클래스의 객체**인 경우, 이 객체는 main이 실행되기 **전에** **초기화**되어야 합니다, 예를 들어:
```cpp
#include <stdio.h>
// g++ autoinit.cpp -o autoinit
@ -345,38 +399,75 @@ printf("Main\n");
return 0;
}
```
러한 전역 변수는 `.data` 또는 `.bss`에 위치하지만, `__CTOR_LIST__``__DTOR_LIST__` 목록에는 초기화 및 소멸할 객체가 저장되어 이들을 추적할 수 있습니다.
전역 변수들은 `.data` 또는 `.bss`에 위치하지만, `__CTOR_LIST__``__DTOR_LIST__` 목록에는 초기화 및 소멸할 객체들이 저장되어 이들을 추적할 수 있습니다.
C 코드에서는 GNU 확장을 사용하여 동일한 결과를 얻을 수 있습니다:
```c
__attributte__((constructor)) //Add a constructor to execute before
__attributte__((destructor)) //Add to the destructor list
```
컴파일러 관점에서, `main` 함수가 실행되기 전과 후에 이러한 작업을 실행하기 위해 `init` 함수와 `fini` 함수를 생성할 수 있으며, 이는 동적 섹션에서 **`INIT`** 및 **`FIN`**으로 참조됩니다. 그리고 ELF의 `init``fini` 섹션에 배치됩니다.
From a compiler perspective, to execute these actions before and after the `main` function is executed, it's possible to create a `init` function and a `fini` function which would be referenced in the dynamic section as **`INIT`** and **`FIN`**. and are placed in the `init` and `fini` sections of the ELF.
언급된 다른 옵션은 동적 섹션의 **`INIT_ARRAY`** 및 **`FINI_ARRAY`** 항목에서 **`__CTOR_LIST__`** 및 **`__DTOR_LIST__`** 목록을 참조하는 것이며, 이들의 길이는 **`INIT_ARRAYSZ`** 및 **`FINI_ARRAYSZ`**로 표시됩니다. 각 항목은 인수 없이 호출될 함수 포인터입니다.
The other option, as mentioned, is to reference the lists **`__CTOR_LIST__`** and **`__DTOR_LIST__`** in the **`INIT_ARRAY`** and **`FINI_ARRAY`** entries in the dynamic section and the length of these are indicated by **`INIT_ARRAYSZ`** and **`FINI_ARRAYSZ`**. Each entry is a function pointer that will be called without arguments.
또한 **`PREINIT_ARRAY`**를 가질 수 있으며, 이는 **`INIT_ARRAY`** 포인터가 실행되기 **전**에 실행될 **포인터**를 포함합니다.
Moreover, it's also possible to have a **`PREINIT_ARRAY`** with **pointers** that will be executed **before** the **`INIT_ARRAY`** pointers.
### 초기화 순서
#### Exploitation note
1. 프로그램이 메모리에 로드되고, 정적 전역 변수가 **`.data`**에서 초기화되며, 초기화되지 않은 변수는 **`.bss`**에서 0으로 설정됩니다.
2. 프로그램 또는 라이브러리에 대한 모든 **종속성**이 **초기화**되고 **동적 링크**가 실행됩니다.
- Under Partial RELRO these arrays live in pages that are still writable before `ld.so` flips `PT_GNU_RELRO` to read-only. If you get an arbitrary write early enough or you can target a librarys writable arrays, you can hijack control flow by overwriting an entry with a function of your choice. Under Full RELRO they are read-only at runtime.
- For lazy binding abuse of the dynamic linker to resolve arbitrary symbols at runtime, see the dedicated page:
{{#ref}}
../rop-return-oriented-programing/ret2dlresolve.md
{{#endref}}
### Initialization Order
1. 프로그램이 메모리에 로드되고, 정적 전역 변수가 **`.data`**에서 초기화되며 초기화되지 않은 변수는 **`.bss`**에서 0으로 설정됩니다.
2. 프로그램 또는 라이브러리의 모든 **종속성**이 **초기화**되고 **동적 링크**가 실행됩니다.
3. **`PREINIT_ARRAY`** 함수가 실행됩니다.
4. **`INIT_ARRAY`** 함수가 실행됩니다.
5. **`INIT`** 항목이 있으면 호출됩니다.
6. 라이브러리인 경우, dlopen이 여기서 끝나고, 프로그램인 경우 **실제 진입점**(`main` 함수)을 호출할 시간입니다.
## 스레드 로컬 저장소 (TLS)
## Thread-Local Storage (TLS)
C++에서 **`__thread_local`** 키워드를 사용하거나 GNU 확장 **`__thread`**를 사용하여 정의됩니다.
They are defined using the keyword **`__thread_local`** in C++ or the GNU extension **`__thread`**.
각 스레드는 이 변수에 대해 고유한 위치를 유지하므로 오직 해당 스레드만 자신의 변수를 접근할 수 있습니다.
Each thread will maintain a unique location for this variable so only the thread can access its variable.
이것이 사용될 때 ELF에서 **`.tdata`** 및 **`.tbss`** 섹션이 사용됩니다. 이는 TLS를 위한 `.data` (초기화됨) 및 `.bss` (초기화되지 않음)와 유사합니다.
When this is used the sections **`.tdata`** and **`.tbss`** are used in the ELF. Which are like `.data` (initialized) and `.bss` (not initialized) but for TLS.
각 변수는 TLS 헤더에 크기와 TLS 오프셋을 지정하는 항목을 가지며, 이는 스레드의 로컬 데이터 영역에서 사용할 오프셋입니다.
Each variable will hace an entry in the TLS header specifying the size and the TLS offset, which is the offset it will use in the thread's local data area.
`__TLS_MODULE_BASE`는 스레드 로컬 저장소의 기본 주소를 참조하는 데 사용되는 기호이며, 모듈의 모든 스레드 로컬 데이터를 포함하는 메모리 영역을 가리킵니다.
The `__TLS_MODULE_BASE` is a symbol used to refer to the base address of the thread local storage and points to the area in memory that contains all the thread-local data of a module.
## Auxiliary Vector (auxv) and vDSO
The Linux kernel passes an auxiliary vector to processes containing useful addresses and flags for the runtime:
- `AT_RANDOM`: points to 16 random bytes used by glibc for the stack canary and other PRNG seeds.
- `AT_SYSINFO_EHDR`: base address of the vDSO mapping (handy to find `__kernel_*` syscalls and gadgets).
- `AT_EXECFN`, `AT_BASE`, `AT_PAGESZ`, etc.
As an attacker, if you can read memory or files under `/proc`, you can often leak these without an infoleak in the target process:
```bash
# Show the auxv of a running process
cat /proc/$(pidof target)/auxv | xxd
# From your own process (helper snippet)
#include <sys/auxv.h>
#include <stdio.h>
int main(){
printf("AT_RANDOM=%p\n", (void*)getauxval(AT_RANDOM));
printf("AT_SYSINFO_EHDR=%p\n", (void*)getauxval(AT_SYSINFO_EHDR));
}
```
`AT_RANDOM`를 누출하면 해당 포인터를 역참조할 수 있다면 카나리 값을 얻을 수 있습니다; `AT_SYSINFO_EHDR`는 가젯을 찾거나 빠른 시스템 호출을 직접 호출하기 위한 vDSO 기반을 제공합니다.
## References
- ld.so(8) 동적 로더 검색 순서, RPATH/RUNPATH, 보안 실행 규칙 (AT_SECURE): https://man7.org/linux/man-pages/man8/ld.so.8.html
- getauxval(3) 보조 벡터 및 AT_* 상수: https://man7.org/linux/man-pages/man3/getauxval.3.html
{{#include ../../banners/hacktricks-training.md}}