Dreamhack CTF Season 6 Round #10 (🌱Div2) dowell 풀이보고 혼자 실습
char st[] = "echo \"I do well at getting the flag\n\"";
int main()
{
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
setvbuf(stderr, 0, 2, 0);
system(st);
printf("one chance for flag");
printf("\n");
char *addr;
printf("pt: ");
scanf("%d", (int *)&addr);
printf("input: ");
scanf("%s", addr);
printf("your input:");
puts(addr);
}
소스코드
주어진 C 코드는 사용자로부터 입력을 받아 이를 출력하는 프로그램입니다. 또한, `system` 함수를 사용하여 시스템 명령어를 실행하고 있습니다. 코드의 각 부분을 분석해보겠습니다.
### 코드 분석
```c
#include <stdio.h>
#include <stdlib.h>
char st[] = "echo \"I do well at getting the flag\n\"";
int main()
{
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
setvbuf(stderr, 0, 2, 0);
system(st);
printf("one chance for flag");
printf("\n");
char *addr;
printf("pt: ");
scanf("%d", (int *)&addr);
printf("input: ");
scanf("%s", addr);
printf("your input:");
puts(addr);
}
```
### 주요 부분 설명
1. **`setvbuf`**:
```c
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
setvbuf(stderr, 0, 2, 0);
```
- `setvbuf` 함수는 버퍼링 방식을 설정합니다. 여기서 `0`은 기본 버퍼링을 사용하도록 하고, `2`는 `_IONBF`(무버퍼링)를 나타냅니다. 즉, 입력과 출력을 즉시 수행하도록 설정합니다.
2. **`system` 함수 호출**:
```c
system(st);
```
- `system` 함수는 `st` 배열에 저장된 명령어를 실행합니다. 여기서는 `echo "I do well at getting the flag\n"` 명령어가 실행되어 "I do well at getting the flag"라는 문자열을 출력합니다.
3. **사용자 입력**:
```c
char *addr;
printf("pt: ");
scanf("%d", (int *)&addr);
```
- 사용자에게 메모리 주소를 입력받습니다. 그러나 `scanf("%d", (int *)&addr);`는 잘못된 방식입니다. `scanf`에서 `%d` 포맷을 사용하여 정수를 입력받고 이를 주소로 사용하려고 하지만, 메모리 주소는 `int` 타입이 아니므로 올바르지 않습니다. 이 부분에서 버퍼 오버플로우 공격을 위한 취약점이 발생할 수 있습니다.
4. **입력 및 출력**:
```c
printf("input: ");
scanf("%s", addr);
```
- 사용자로부터 입력을 받아 `addr`이 가리키는 메모리 위치에 저장합니다. 만약 사용자가 제공하는 문자열이 `addr`이 가리키는 메모리 공간보다 크면 버퍼 오버플로우가 발생합니다. 이로 인해 프로그램의 흐름을 조작하거나 메모리에 의도치 않은 내용을 쓸 수 있는 취약점이 발생합니다.
5. **최종 출력**:
```c
printf("your input:");
puts(addr);
```
- 사용자 입력을 출력합니다. `puts` 함수는 문자열을 출력하고 문자열 끝에 있는 NULL 문자까지 출력합니다.
### 보안 취약점
- **버퍼 오버플로우**:
- `scanf("%s", addr);`에서 `addr`이 사용자 입력에 의해 조작될 수 있으므로, 이 부분은 버퍼 오버플로우 공격에 매우 취약합니다. 공격자는 이 부분을 이용하여 스택의 데이터를 조작하거나, 반환 주소를 변경하여 악의적인 코드를 실행할 수 있습니다.
- **주소 입력 오류**:
- `scanf("%d", (int *)&addr);`는 올바르지 않은 방식으로 주소를 입력받고 있습니다. `%d`는 정수를 읽는 포맷이므로, 주소를 잘못 해석할 수 있습니다. `%p`를 사용하여 포인터 주소를 안전하게 읽어오는 것이 바람직합니다.
### 결론
이 코드는 시스템 명령어를 실행하고, 사용자 입력을 받는 간단한 구조를 가지고 있지만, 버퍼 오버플로우와 잘못된 주소 입력 방식으로 인해 심각한 보안 취약점을 가지고 있습니다. 이 취약점을 악용하면 코드 실행이나 메모리 손상을 일으킬 수 있으므로, 코드의 안전성을 높이기 위한 추가적인 조치가 필요합니다.
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
주어진 출력은 바이너리의 보안 속성과 관련된 정보입니다. 각 항목에 대해 설명하겠습니다.
### 1. **Arch: amd64-64-little**
- **Arch**는 바이너리가 어떤 아키텍처를 사용하는지를 나타냅니다.
- **amd64**는 AMD에서 개발한 64비트 아키텍처를 의미하며, 일반적으로 x86-64 아키텍처로도 알려져 있습니다.
- **64-little**은 이 아키텍처가 리틀 엔디안 방식으로 데이터를 저장한다는 것을 의미합니다. 리틀 엔디안은 낮은 주소에서 높은 바이트를 먼저 저장하는 방식입니다.
### 2. **RELRO: Partial RELRO**
- **RELRO (Read-Only Relocations)**는 메모리 보호 기법으로, 프로그램의 특정 데이터를 읽기 전용으로 설정하여 공격자가 이를 변경하지 못하도록 합니다.
- **Partial RELRO**는 일부, 즉 데이터 영역(예: GOT)만 보호한다는 것을 의미합니다. 완전한 RELRO에서는 모든 리로케이션이 완료된 후 해당 메모리 영역이 읽기 전용으로 설정됩니다. Partial RELRO는 완전한 보호를 제공하지 않으므로, 더 많은 공격 벡터가 존재할 수 있습니다.
### 3. **Stack: Canary found**
- **Canary**는 스택 버퍼 오버플로우 공격을 방지하기 위한 기법입니다.
- 함수가 호출될 때 스택에 보호 값을 삽입하고, 함수가 종료될 때 이 값을 검사합니다. 값이 변경되었다면 스택이 오버플로우되었다고 판단하고 프로그램을 종료합니다.
- **Canary found**는 프로그램이 이 보호 기능을 사용하고 있으며, 스택 오버플로우가 감지되었음을 나타냅니다.
### 4. **NX: NX enabled**
- **NX (No eXecute)**는 메모리 보호 기법으로, 특정 메모리 영역(예: 스택 및 힙)을 실행 불가능하게 설정하여 악의적인 코드를 실행하는 것을 방지합니다.
- **NX enabled**는 프로그램이 이 기능을 활성화하여, 스택과 힙에서 코드 실행이 불가능하도록 설정되어 있다는 것을 의미합니다. 이로 인해 스택에서 악의적인 코드가 실행되는 것을 방지할 수 있습니다.
### 5. **PIE: No PIE (0x400000)**
- **PIE (Position Independent Executable)**는 실행 파일이 메모리에서 로드될 때의 위치에 관계없이 작동할 수 있도록 설계된 것입니다. 이는 공격자가 코드의 주소를 미리 알고 공격하는 것을 어렵게 만듭니다.
- **No PIE**는 이 바이너리가 위치 독립적 실행 파일이 아니라는 것을 의미합니다. 즉, 바이너리가 특정 메모리 주소(여기서는 `0x400000`)에 고정되어 실행됩니다. 이로 인해 공격자는 공격하기 쉬워질 수 있습니다.
### 결론
이 정보는 바이너리의 보안 특성을 파악하는 데 매우 중요합니다. 각 속성은 프로그램의 보안 강도를 결정하는 요소이며, 공격자가 특정 취약점을 이용할 가능성을 평가하는 데 도움이 됩니다. 이러한 정보는 CTF(해킹 대회)나 보안 분석에서 중요한 요소로 작용합니다.
GOT(Global Offset Table)의 동작 원리와 `partial RELRO` 상태에서의 보안 취약점에 대해 설명하겠습니다.
### GOT (Global Offset Table)
1. **GOT의 정의**:
- GOT는 실행 중에 동적으로 연결된 라이브러리의 함수 주소를 저장하는 테이블입니다. 프로그램이 실행될 때, 특정 함수의 실제 메모리 주소는 그 함수가 로드될 때 결정되므로, GOT는 이 주소를 추적하는 데 사용됩니다.
2. **동작 원리**:
- 프로그램이 동적 링크 라이브러리를 호출할 때, 해당 라이브러리의 함수 주소는 GOT를 통해 참조됩니다. 초기에는 GOT에 해당 함수의 실제 주소가 아닌, 이를 로드하기 위한 코드가 들어 있습니다.
- 프로그램 실행 중, 최초의 호출 시, GOT의 항목이 실제 주소로 바뀌고 이후에는 이 주소를 참조하여 호출합니다.
### RELRO와 GOT의 관계
1. **RELRO (Read-Only Relocations)**:
- RELRO는 메모리의 일부를 읽기 전용으로 설정하여 공격자가 해당 영역을 수정하지 못하도록 하는 보안 기법입니다. 이는 주로 GOT의 보호와 관련이 있습니다.
- **Partial RELRO**는 GOT의 특정 부분만 보호하는 상태입니다. 이 상태에서는 GOT의 일부(예: PLT, Procedure Linkage Table)가 보호되지 않아서 공격자가 이 영역을 수정할 수 있습니다.
2. **Partial RELRO의 의미**:
- Partial RELRO에서는 GOT의 항목이 초기화된 후에도 수정될 수 있습니다. 즉, 공격자는 GOT에 있는 함수의 주소를 덮어쓸 수 있으며, 이를 통해 원하는 함수를 호출하도록 조작할 수 있습니다.
### 예시: GOT를 덮어 함수 호출하기
1. **악의적인 접근**:
- 공격자는 버퍼 오버플로우와 같은 취약점을 이용하여 GOT의 특정 항목을 변경할 수 있습니다. 예를 들어, `system` 함수의 주소를 다른 함수(예: `exit` 또는 악성 코드)의 주소로 덮어쓸 수 있습니다.
2. **과정**:
- 프로그램이 `system` 함수를 호출해야 할 때, GOT의 해당 항목이 덮어써진 주소를 참조하게 됩니다.
- 결과적으로, 원래의 함수 대신 공격자가 원하는 함수가 실행됩니다.
### 결론
`partial RELRO` 상태에서는 GOT의 항목이 수정 가능하므로, 공격자가 이를 이용하여 프로그램의 흐름을 제어할 수 있는 취약점이 존재합니다. 이를 통해 공격자는 원하지 않는 함수를 호출하거나 악성 코드를 실행하는 등의 악의적인 행동을 할 수 있습니다. 이와 같은 원리는 보안 분석 및 CTF(해킹 대회) 문제에서 매우 중요합니다.
https://velog.io/@noob3er/%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%B3%B4%ED%98%B8%EA%B8%B0%EB%B2%95
주어진 C 코드에서 GOT(Global Offset Table)의 주소를 확인하는 방법을 설명하겠습니다. GOT는 동적 링크된 함수의 실제 주소를 저장하는 테이블로, 보안 공격이나 취약점을 분석할 때 중요한 정보를 제공합니다.
### GOT 주소 확인 방법
1. **GDB를 사용하여 주소 확인**:
- GDB(GNU Debugger)는 바이너리를 분석하고 메모리 주소를 확인하는 데 유용한 도구입니다. 다음 단계를 통해 GOT 주소를 확인할 수 있습니다.
**단계**:
1. 바이너리를 GDB로 실행합니다:
```bash
gdb ./your_binary
```
2. 바이너리의 세부 정보를 로드합니다:
```gdb
(gdb) info functions
```
3. GOT와 관련된 주소를 확인하려면:
```gdb
(gdb) disassemble main
```
또는
```gdb
(gdb) info symbol <address>
```
<address>는 관심 있는 주소를 입력합니다. 이를 통해 GOT에 접근하는 방법과 관련된 기호를 찾을 수 있습니다.
4. GOT 항목을 확인하려면:
```gdb
(gdb) x/20gx <address_of_got>
```
여기서 `<address_of_got>`는 GOT의 시작 주소입니다.
2. **Readelf 또는 objdump를 사용하여 확인**:
- `readelf` 또는 `objdump`와 같은 도구를 사용하여 ELF 바이너리의 정보를 추출할 수 있습니다.
**예시**:
```bash
readelf -r ./your_binary
```
이 명령은 리로케이션 정보(예: GOT의 주소)를 포함한 내용을 보여줍니다.
또는:
```bash
objdump -R ./your_binary
```
이 명령은 GOT의 기호와 주소를 표시합니다.
3. **C 코드에서 직접 주소 참조**:
- C 코드에서 특정 함수가 GOT에 저장된 주소를 참조할 수 있습니다. 예를 들어, 다음 코드를 추가하여 `printf` 함수의 GOT 주소를 출력할 수 있습니다:
```c
printf("Address of printf in GOT: %p\n", &printf);
```
이 방법은 컴파일된 바이너리에서 해당 함수의 GOT 주소를 직접 출력합니다.
### 요약
- GDB와 같은 디버거를 사용하여 바이너리를 분석하고 GOT 주소를 확인할 수 있습니다.
- `readelf` 및 `objdump` 도구를 사용하여 ELF 파일의 GOT 정보를 조회할 수 있습니다.
- C 코드에서 직접 GOT 주소를 출력하도록 수정할 수 있습니다.
이러한 방법을 통해 GOT의 주소를 찾고, 해당 주소를 기반으로 취약점을 분석하거나 공격을 계획할 수 있습니다.
주어진 설명은 CTF( Capture The Flag)나 보안 분석 관련 작업에서 바이너리의 보안 속성을 평가하고, 특정 공격 벡터를 찾는 과정을 설명하고 있습니다. 여기서는 `checksec`를 사용하여 바이너리의 보안 기능을 확인하고, GOT(Global Offset Table) 주소를 찾는 방법을 설명하겠습니다.
### 1. **Partial RELRO와 GOT 주소**
- **Partial RELRO**:
- Partial RELRO 상태에서는 GOT의 일부가 보호되지만, 여전히 수정이 가능하다는 것을 의미합니다. 즉, 공격자가 GOT의 항목을 덮어쓸 수 있는 가능성이 있다는 것입니다.
- **PIE (Position Independent Executable)**:
- PIE가 비활성화되어 있다는 것은 프로그램이 항상 동일한 주소에서 실행된다는 것을 의미합니다. 이로 인해 GOT의 주소를 쉽게 예측할 수 있습니다.
### 2. **GOT 주소를 아는 방법**
GOT 주소를 찾는 과정은 다음과 같습니다:
#### 1. **GDB를 사용하여 GOT 주소 확인**
GDB를 사용하여 실행 파일의 GOT 주소를 확인할 수 있습니다. 다음 단계로 진행합니다:
- **GDB 실행**:
```bash
gdb ./prob
```
- **바이너리 정보 확인**:
다음 명령어를 통해 GOT 주소를 확인할 수 있습니다.
```gdb
(gdb) info functions
```
- **GOT의 주소 확인**:
GOT 주소를 확인하기 위해, `main` 함수의 어셈블리 코드를 살펴볼 수 있습니다.
```gdb
(gdb) disassemble main
```
- **GOT 항목 출력**:
특정 GOT 항목(예: `printf` 또는 `system`)의 주소를 확인할 수 있습니다.
```gdb
(gdb) x/20gx <address_of_got>
```
여기서 `<address_of_got>`는 GOT의 시작 주소입니다.
#### 2. **Readelf 또는 Objdump 사용**
- **Readelf**:
GOT의 정보를 확인하려면 다음 명령어를 사용할 수 있습니다:
```bash
readelf -r ./prob
```
- **Objdump**:
objdump를 사용하여 GOT 정보를 얻을 수 있습니다:
```bash
objdump -R ./prob
```
이 명령들은 GOT의 기호와 해당 주소를 보여줍니다.
### 3. **메모리 덮어쓰기**
- **main()의 동작을 이용한 덮어쓰기**:
- 위의 방법으로 GOT의 주소를 찾은 후, 해당 주소를 이용하여 프로그램의 흐름을 조작할 수 있습니다. 예를 들어, 사용자가 입력하는 주소를 통해 특정 GOT 항목(예: `system` 함수)의 주소를 변경하여 의도한 함수를 호출하도록 할 수 있습니다.
- `scanf` 함수에서 입력받은 주소를 GOT 주소로 덮어쓰면, 프로그램의 흐름을 공격자가 제어할 수 있게 됩니다.
### 결론
- **GOT 주소를 알기 위해**: GDB, Readelf, 또는 objdump와 같은 도구를 사용하여 GOT의 주소를 확인할 수 있습니다.
- **Partial RELRO 상태**에서는 GOT 주소를 덮어쓰는 것이 가능하며, 이를 통해 특정 함수를 호출하는 공격이 가능하다는 것을 의미합니다.
이렇게 하면 GOT의 주소를 찾고, 이를 이용해 공격할 수 있는 방법을 이해할 수 있습니다. 추가적으로 궁금한 점이 있다면 말씀해 주세요!
주어진 설명은 `puts()` 함수의 GOT(Global Offset Table) 항목을 `system()` 함수의 PLT(Procedure Linkage Table) 주소로 덮어써서, 프로그램의 제어를 공격자가 원하는 대로 바꾸는 방법을 설명하고 있습니다. 이 내용을 좀 더 구체적으로 분석해 보겠습니다.
### 핵심 개념
1. **GOT와 PLT**:
- **GOT**: 실행 중에 동적으로 연결된 함수의 주소를 저장하는 테이블입니다. 함수가 처음 호출될 때, GOT의 해당 항목이 해당 함수의 실제 주소로 업데이트됩니다.
- **PLT**: 동적 라이브러리의 함수 호출을 처리하는 테이블로, GOT와 연결되어 있습니다. PLT는 주로 함수의 첫 호출 시에 실제 주소를 가져오기 위해 사용됩니다.
2. **`puts()`와 `system()`**:
- `puts()`는 주어진 문자열을 출력하는 함수입니다.
- `system()`은 주어진 명령어를 실행하는 함수로, 이 함수를 통해 쉘을 얻을 수 있습니다.
### 설명 내용
#### 1. **GOT 항목 덮어쓰기**
- **`puts()`의 GOT 주소를 `system()`의 PLT 주소로 덮어쓴다는 의미**:
- 바이너리에서 `puts()`의 GOT 항목을 `system()`의 PLT 주소로 변경하면, 이후 프로그램이 `puts()`를 호출할 때 실제로 `system()`이 호출됩니다.
#### 2. **`addr`의 설정**
- **`addr`이 `system()`의 인자로 사용된다는 점**:
- `puts()`를 호출할 때 `addr`에 어떤 값을 제공하면, `system()`이 해당 주소를 인자로 받습니다.
- 예를 들어, `puts(addr)` 대신 `system(addr)`가 호출되면, `addr`에 설정된 문자열이 `system()`의 인자로 전달됩니다.
#### 3. **어떻게 동작하는지**
- **`addr`을 `puts()`의 GOT - 3으로 설정**:
- 여기서 `puts()`의 GOT 주소에서 3을 뺀 값을 `addr`으로 설정합니다. 이 값이 `system()`의 PLT 주소로 가리키게 됩니다.
- **`system("sh; ...")`의 실행**:
- 공격자는 `addr`에 쉘을 실행하는 명령어를 포함시킬 수 있습니다. 예를 들어:
- `addr`을 `"sh; ..."`와 같은 문자열로 설정하여, `system()`이 `"sh; ..."`를 실행하도록 할 수 있습니다.
### 최종 결과
- **최종적으로 `system("sh; ...")`가 실행되어 쉘을 얻는다**:
- `puts()`의 GOT가 `system()`의 PLT 주소로 덮어씌워진 후, 프로그램은 `puts()`를 호출하는 대신 `system()`을 호출하게 되고, 이로 인해 공격자는 쉘을 얻을 수 있게 됩니다.
### 결론
이 과정은 버퍼 오버플로우 또는 GOT 덮어쓰기 공격을 통해 이루어질 수 있습니다. 공격자는 이러한 취약점을 이용하여 프로그램의 흐름을 제어하고, 원하지 않는 코드(여기서는 쉘)를 실행할 수 있는 기회를 얻는 것입니다. 이와 같은 기법은 보안 분석 및 CTF 문제에서 매우 일반적입니다. 추가적인 질문이 있으면 말씀해 주세요!
The reference to subtracting -3 in the context of your previous questions relates to how you might manipulate function pointers in the Global Offset Table (GOT) to achieve certain exploit goals, specifically when trying to execute the system function in place of puts. Let's break down your output and explain what is meant in that context.
GOT and PLT Entries Explained
In your output:
- GOT Entries:
- Each line in the output represents an entry in the GOT. The OFFSET indicates the location in the binary where the address of the function will be stored after the program has been linked and executed.
- The entry for puts indicates where the actual address of the puts function is stored, and similarly for system.
The Address Manipulation
When you manipulate the GOT entry for puts to point to system, you effectively change what happens when puts is called:
- Purpose of -3:
- If you need to change the GOT entry of puts to point to the PLT entry of system, the -3 might refer to an offset based on the specific memory layout of the binary you're working with.
- For example, if the GOT entry for puts is at 0x0404020 and the PLT entry for system is located at 0x0404030, you might need to adjust your input such that the calculated address ends up pointing to system when puts is called.
- Why Subtract?:
- In practice, -3 may refer to a specific memory layout where the addresses are close, and you need to provide a pointer that is offset correctly.
- If you determine that puts and system are very close in memory (for example, puts at 0x0404020 and you want to point to the address of system), subtracting an appropriate offset (in this case, -3) could mean navigating back to the correct function address.
Example of Address Calculation
If your GOT entry for puts is at 0x0404020, and you want to make it point to the system PLT entry located at 0x0404030, you would manipulate it as follows:
- Current Address:
- puts GOT: 0x0404020 (this points to puts)
- system PLT: 0x0404030
- Setting addr:
- You would adjust the addr variable in the input to make it point to system.
- If you find that your input to change the address of puts needs to be 0x0404030 - 3, you'd provide 0x0404020 + (-3) as input to make it point to the desired function.
Conclusion
The -3 in your context serves as an offset to correctly manipulate the GOT entry so that when the program calls puts, it instead jumps to system. This kind of manipulation is typical in exploitation techniques, particularly in buffer overflow attacks where the attacker aims to control execution flow by overwriting function pointers in memory.
If you have any further questions or need more clarity on specific parts, feel free to ask!
하.. 막혔다
'Dreamhack > CTF' 카테고리의 다른 글
Dreamhack CTF Season 6 Round #10 (🌱Div2) Secure Secret (0) | 2024.10.28 |
---|---|
Dreamhack CTF Season 6 Round #10 (🌱Div2) Recover (0) | 2024.10.27 |