Dreamhack/Dreamhack Wargame (Challenge)

[73] IT 비전공자 [dreamhack]File Vulnerability Advanced for linux문제 풀기

imaginefuture-1 2024. 11. 21. 11:23

web 문제다

 

 

첫페이지 들어가니 api index문장이있다

 

 

 

 

### **API란?**

**API (Application Programming Interface)**는 소프트웨어 간의 **통신**이나 **데이터 교환**을 가능하게 하는 **인터페이스**를 의미합니다. 간단히 말하면, 서로 다른 프로그램이 "소통"할 수 있도록 도와주는 일종의 규칙이나 도구입니다.

---

### **API의 주요 목적**
1. **데이터 접근 및 교환**:
   - 다른 애플리케이션이나 시스템이 데이터를 요청하거나 제공할 수 있도록 지원합니다.
   - 예: 날씨 앱이 기상청의 API를 사용해 날씨 데이터를 가져옵니다.

2. **기능 제공**:
   - 복잡한 작업이나 서비스를 재사용할 수 있게 합니다.
   - 예: 결제 시스템에서 PayPal API를 사용하면 직접 결제 시스템을 구현할 필요가 없습니다.

3. **통합 및 확장성**:
   - 다양한 시스템과 애플리케이션을 통합하여 새로운 기능을 개발하거나 서비스를 확장할 수 있습니다.

---

### **API의 동작 방식**
1. **요청(Request)**:
   - 클라이언트(앱, 브라우저 등)가 API를 호출하여 데이터를 요청합니다.
   - 일반적으로 HTTP를 사용하며, 요청에는 URL, 메서드(GET, POST 등), 헤더, 본문 등이 포함됩니다.

2. **응답(Response)**:
   - API 서버는 요청을 처리한 후, 클라이언트에 데이터를 반환합니다.
   - 응답은 JSON, XML 등의 형식으로 제공됩니다.

---

### **API의 예시**
1. **REST API**:
   - 웹에서 가장 널리 사용되는 방식.
   - URL 경로를 통해 자원을 요청하고, 응답은 JSON 형식으로 전달.
   - 예: 
     - `GET https://api.weather.com/forecast` → 날씨 데이터 요청
     - `POST https://api.example.com/users` → 사용자 정보 저장

2. **GraphQL API**:
   - 클라이언트가 필요한 데이터만 명시적으로 요청할 수 있는 API.
   - 데이터 효율성이 뛰어남.

3. **OpenAPI**:
   - 문서화된 표준 API.
   - 예: 구글 맵 API, 트위터 API.

---

### **API의 일상적인 예**
- **소셜 로그인**:
  - Facebook/Google API를 사용해 "소셜 로그인"을 구현.
- **결제 시스템**:
  - PayPal, Stripe 등의 결제 API를 사용.
- **날씨 앱**:
  - 기상청 API에서 데이터를 받아 사용자가 볼 수 있도록 표시.
- **지도 서비스**:
  - Google Maps API로 길 찾기나 위치 검색 기능 제공.

---

### **API가 왜 중요한가?**
- **효율성**: 기존 기능을 재사용하므로 개발 시간을 단축.
- **확장성**: 다양한 플랫폼과 통합 가능.
- **표준화**: 시스템 간 통신 방식을 일정하게 유지.
- **보안**: 데이터를 안전하게 교환.

---

### **비유를 통한 이해**
API를 **레스토랑의 메뉴**에 비유할 수 있습니다:
- **클라이언트**: 손님
- **API**: 메뉴
- **서버**: 주방
- 손님이 메뉴(API)를 보고 원하는 음식을 주문(Request)하면, 주방(서버)이 음식을 만들어 응답(Response)으로 제공합니다. 손님은 주방 내부를 몰라도 메뉴를 통해 원하는 것을 얻을 수 있습니다.

---

API는 현대 소프트웨어 개발에서 필수적인 요소로, 앱이나 서비스를 개발할 때 데이터와 기능을 효율적으로 사용할 수 있도록 돕는 도구입니다.

 

 

ㅇㅎㅇㅎ 메뉴 주방 손님!

 

 

 

 

소스코드 스윽 보고 한번 루트 들어가봤다

/admin 들어갔더니 액세스 디나이 ㅠ
/file 했더니 에러..

 

 

 

소스코드 자세히 보러가자 

 

 

 

import os, subprocess
from functools import wraps
from flask import Flask, request

app = Flask(__name__)
API_KEY = os.environ.get('API_KEY', None)

def key_required(view):
    @wraps(view)
    def wrapped_view(**kwargs):
        apikey = request.args.get('API_KEY', None)
        if API_KEY and apikey:
            if apikey == API_KEY:
                return view(**kwargs)
        return 'Access Denied !'
    return wrapped_view


@app.route('/', methods=['GET'])
def index():
    return 'API Index'


@app.route('/file', methods=['GET'])
def file():
    path = request.args.get('path', None)
    if path:
        data = open('./files/' + path).read()
        return data
    return 'Error !'


@app.route('/admin', methods=['GET'])
@key_required
def admin():
    cmd = request.args.get('cmd', None)
    if cmd:
        result = subprocess.getoutput(cmd)
        return result
    else:
        return 'Error !'


if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=True)

 

 

 

This Flask application code implements a basic API with routes that perform different tasks. However, there are several security risks and poor practices in the code. Let's analyze it step by step:

---

### 1. **API Key Handling**
   ```python
   API_KEY = os.environ.get('API_KEY', None)
   ```
   - **Functionality**: It retrieves the API key from the environment variable `API_KEY`.
   - **Risk**: If the environment variable is not set, the `API_KEY` is `None`, which disables API key authentication completely.

   #### **Suggested Improvement**
   - Add a default invalid value or enforce the existence of an API key during startup.
     ```python
     if not API_KEY:
         raise ValueError("API_KEY must be set in environment variables.")
     ```

---

### 2. **`key_required` Decorator**
   ```python
   def key_required(view):
       @wraps(view)
       def wrapped_view(**kwargs):
           apikey = request.args.get('API_KEY', None)
           if API_KEY and apikey:
               if apikey == API_KEY:
                   return view(**kwargs)
           return 'Access Denied !'
       return wrapped_view
   ```
   - **Functionality**: Ensures that certain routes are protected by an API key.
   - **Risk**: The API key is passed in the URL query string, which is insecure because query parameters can be logged by servers, proxies, or browsers.

   #### **Suggested Improvement**
   - Use headers instead of query parameters for the API key.
     ```python
     apikey = request.headers.get('X-API-KEY')
     ```

---

### 3. **Routes**
#### **Index Route**
   ```python
   @app.route('/', methods=['GET'])
   def index():
       return 'API Index'
   ```
   - **Functionality**: Returns a simple string indicating the API index.
   - **Risk**: No significant risks.

#### **File Route**
   ```python
   @app.route('/file', methods=['GET'])
   def file():
       path = request.args.get('path', None)
       if path:
           data = open('./files/' + path).read()
           return data
       return 'Error !'
   ```
   - **Functionality**: Reads and returns the contents of a file in the `./files/` directory.
   - **Risks**:
     - **Path Traversal Attack**: An attacker can manipulate the `path` parameter (e.g., `path=../../etc/passwd`) to access unauthorized files.
     - **Error Handling**: If the file does not exist, an unhandled exception occurs.

   #### **Suggested Improvements**
   - Validate the `path` parameter to prevent path traversal:
     ```python
     from werkzeug.utils import secure_filename
     safe_path = secure_filename(path)
     ```
   - Use `try...except` blocks to handle file errors.
   - Limit file access to a specific directory.

---

#### **Admin Route**
   ```python
   @app.route('/admin', methods=['GET'])
   @key_required
   def admin():
       cmd = request.args.get('cmd', None)
       if cmd:
           result = subprocess.getoutput(cmd)
           return result
       else:
           return 'Error !'
   ```
   - **Functionality**: Executes shell commands specified by the `cmd` parameter.
   - **Risks**:
     - **Remote Code Execution**: This is a critical security vulnerability. An attacker with an API key can execute arbitrary system commands on the server.

   #### **Suggested Improvements**
   - Remove this functionality or whitelist specific commands.
   - If command execution is absolutely necessary, use controlled subprocess calls with a whitelist of allowed commands:
     ```python
     ALLOWED_COMMANDS = ['ls', 'pwd', 'df -h']
     if cmd in ALLOWED_COMMANDS:
         result = subprocess.getoutput(cmd)
     else:
         result = "Command not allowed."
     ```

---

### 4. **Debug Mode**
   ```python
   if __name__ == '__main__':
       app.run(host='0.0.0.0', debug=True)
   ```
   - **Risk**: Running in debug mode exposes sensitive information if the application crashes.
   - **Suggested Improvement**: Disable debug mode in production.

---

### 5. **General Security Improvements**
   - Use HTTPS to secure data transmission.
   - Employ proper logging mechanisms.
   - Add authentication and authorization for sensitive routes.
   - Implement rate limiting to prevent abuse.
   - Run the Flask app behind a reverse proxy, such as Nginx or Apache.

 


해당 웹사이트에는 총 2개의 페이지가 있다.
1. /file : 'path' 파라미터값에 "/etc/passwd"와 같은 경로를 입력하면 해당 파일을 보여준다.
2. /admin : 이름 그대로 admin 페이지로 'cmd' 명령어를 사용할 수 있다.
출처ㅣ https://snowjeon2.tistory.com/59

 

 

@app.route('/admin', methods=['GET'])
@key_required

 

/admin 페이지에 접속하기 위해선 key가 필요하다

def key_required(view):
    @wraps(view)
    def wrapped_view(**kwargs):
        apikey = request.args.get('API_KEY', None)
        if API_KEY and apikey:
            if apikey == API_KEY:
                return view(**kwargs)
        return 'Access Denied !'
    return wrapped_view

 

 

 

1. /file 페이지에서 환경변수 파일을 통해 'API_KEY' 획득
2. 1번에서 얻은 'API_KEY' 를 통해 'cmd' 명령어로 FLAG 획득
 
먼저 문제 파일중 'nginx.conf' 파일을 통해 'access_log' 파일을 살펴보았다.
conf는 configuration의 약자로 프로그램의 구성 또는 설정 파일을 말한다.

출처ㅣ https://snowjeon2.tistory.com/59

 

 

 

/file?path=../../..//var/log/nginx/nginx_access.log

 

 

 

 

 

`../`는 **상위 디렉터리로 이동**하는 파일 시스템 경로를 나타냅니다. 

- `../` 한 번은 현재 디렉터리에서 바로 상위 디렉터리로 이동.
- 반복적으로 사용하면 여러 단계의 상위 디렉터리로 이동할 수 있습니다.

---

### **왜 `../`를 세 번 사용하나요?**
경로 `../../..//var/log/nginx/nginx_access.log`는 아래와 같은 방식으로 동작합니다:

1. `../` → 현재 디렉터리의 상위 디렉토리로 이동.
2. `../../` → 두 번째 `../`로 인해 두 단계 상위 디렉토리로 이동.
3. `../../../` → 세 번째 `../`로 인해 세 단계 상위 디렉토리로 이동.

#### **결론**:
`../../..`는 현재 디렉터리에서 **세 단계 위로 이동**한다는 의미입니다. 이후, `//var/log/nginx/nginx_access.log` 경로로 접근하여 파일을 찾으려고 합니다.

---

### **왜 사용자가 이렇게 입력했을까요?**
이런 경로 입력은 주로 **Path Traversal 공격**의 일부일 가능성이 높습니다. 공격자는 `../`를 반복적으로 사용하여 애플리케이션의 파일 시스템 루트(`/`)에 도달하려고 합니다. 이렇게 하면 애플리케이션이 보호해야 할 민감한 파일에 접근할 수 있습니다.

#### **예:**
1. 애플리케이션의 `file` 엔드포인트가 파일을 읽는 기능을 제공한다고 가정.
   ```python
   @app.route('/file', methods=['GET'])
   def file():
       path = request.args.get('path', None)
       if path:
           data = open('./files/' + path).read()
           return data
       return 'Error !'
   ```

2. 공격자는 경로를 아래와 같이 조작합니다:
   ```
   /file?path=../../..//var/log/nginx/nginx_access.log
   ```
   - `../../..`를 사용하여 현재 디렉토리의 세 단계를 올라갑니다.
   - 이후 `/var/log/nginx/nginx_access.log` 파일에 접근을 시도합니다.

3. 결과:
   - 애플리케이션이 안전하지 않은 경우, 공격자가 **웹 서버의 로그 파일**이나 다른 민감한 파일에 접근할 수 있습니다.

---

### **예방 방법**
#### 1. **경로 검증**
   - 입력 경로를 검증하여 `../`와 같은 **상위 디렉토리 이동 명령어를 금지**해야 합니다.
   ```python
   import os

   def safe_path(path):
       base_dir = os.path.abspath('./files')  # Base directory
       full_path = os.path.abspath(os.path.join(base_dir, path))  # Resolve absolute path
       if not full_path.startswith(base_dir):  # Ensure path is within the base directory
           raise ValueError("Invalid path!")
       return full_path
   ```

#### 2. **`secure_filename` 사용**
   - `werkzeug`의 `secure_filename` 함수로 사용자 입력을 안전하게 처리.
   ```python
   from werkzeug.utils import secure_filename
   safe_file = secure_filename(path)
   ```

#### 3. **권한 설정**
   - 애플리케이션이 민감한 디렉토리나 파일에 접근할 수 없도록 제한.

#### 4. **Sandbox 환경**
   - 애플리케이션의 파일 시스템 접근을 컨테이너나 샌드박스 환경으로 격리.

---

### **결론**
`../`의 반복은 디렉토리 트리에서 상위로 이동하려는 시도이며, 이는 주로 **보안 취약점(경로 조작)**을 노린 악의적인 행동일 가능성이 높습니다. 이를 막으려면 경로 검증 및 보안 조치를 반드시 구현해야 합니다.

 

 


 

 

`../`를 사용하는 횟수에는 **이론적인 제한은 없습니다**. 즉, 파일 시스템 경로 상에서 상위 디렉터리로 이동하는 `../`는 몇 번이든 사용할 수 있습니다. 하지만 실제로는 **경로의 루트 디렉토리(`/`)에 도달하면 더 이상 상위 디렉토리로 이동할 수 없습니다**. 

---

### **상위 디렉토리 이동 횟수와 제한**
1. **루트 디렉토리 제한**:
   - 파일 시스템의 루트 디렉토리(`/`)에 도달하면 더 이상 `../`가 동작하지 않습니다.
   - 예:
     ```
     /etc/nginx/../..
     ```
     위 경로는 두 번의 상위 이동을 시도하지만, 루트 디렉토리(`/`) 이상으로는 이동할 수 없습니다. 따라서 최종 경로는 여전히 `/`입니다.

2. **무한 반복 방지**:
   - 대부분의 파일 시스템에서는 `../`를 루트 이상으로 이동하려는 시도를 단순히 무시합니다.
   - 예를 들어, 아래 경로는 동일하게 루트로 해석됩니다:
     ```
     ../../../../../../../etc/passwd
     ```

3. **운영체제와 파일 시스템의 역할**:
   - 운영체제가 파일 시스템 경로를 해석할 때 루트를 넘는 경로를 막아줍니다. 이는 파일 시스템의 기본 동작으로, `../`가 아무리 많아도 루트를 벗어나지 않습니다.

---

### **보안 상의 문제점**
비록 루트를 넘어갈 수는 없지만, `../`의 반복 사용은 여전히 보안 위험을 초래할 수 있습니다. 예를 들어:
- 애플리케이션이 `chroot` 또는 특정 디렉토리 내부로 제한되지 않은 경우, 공격자는 애플리케이션 루트(`./files/`)를 벗어나 민감한 파일에 접근할 수 있습니다.

---

### **예시: 횟수 제한 없이 접근**
```bash
/file?path=../../../../../etc/passwd
```
- 위 경로는 루트(`/`) 이상으로 이동할 수 없으므로 최종적으로 `/etc/passwd`를 가리킵니다.
- 따라서 횟수 제한은 없지만, 목표 파일에 도달하면 이동이 멈추게 됩니다.

---

### **방어 방법**
`../` 횟수에 제한을 두는 것보다, 경로 자체를 안전하게 검증하는 것이 더 중요합니다.

1. **경로를 반드시 특정 디렉토리 내로 제한**:
   ```python
   import os

   def safe_path(path):
       base_dir = os.path.abspath('./files')  # 안전한 기본 디렉토리
       requested_path = os.path.abspath(os.path.join(base_dir, path))  # 절대 경로 변환
       if not requested_path.startswith(base_dir):  # 기본 디렉토리 안에 있는지 확인
           raise ValueError("잘못된 경로 요청!")
       return requested_path
   ```

2. **`secure_filename` 사용**:
   - `../`와 같은 경로 조작 시도를 제거하기 위해:
     ```python
     from werkzeug.utils import secure_filename
     safe_file = secure_filename(path)
     ```

3. **시스템 권한 설정**:
   - 애플리케이션 계정이 민감한 파일이나 디렉토리에 접근할 수 없도록 제한.

4. **샌드박스 환경**:
   - 애플리케이션 프로세스를 독립된 샌드박스나 컨테이너 환경에서 실행.

---

### **결론**
- `../`의 사용 횟수에 이론적인 제한은 없지만, 루트 디렉토리(`/`)를 벗어날 수는 없습니다.
- 하지만 애플리케이션이 적절히 경로를 검증하지 않으면 민감한 파일에 접근할 수 있는 취약점이 발생할 수 있습니다.
- **경로 검증**과 **권한 설정**으로 보안을 강화하는 것이 핵심입니다.

 


 

 

`var`와 `log`는 **리눅스/유닉스 기반 시스템의 파일 시스템 구조**에서 특정 디렉토리를 나타내며, 각각의 역할과 의미가 있습니다.

---

### **1. `var` 디렉토리**
- **의미**: "Variable"의 약자로, 시스템에서 **변경 가능한 데이터**(즉, 동적으로 생성되거나 변경되는 데이터)가 저장되는 디렉토리입니다.
- **위치**: 루트 디렉토리(`/`) 아래에 위치하며, `/var`로 표시됩니다.
- **주요 역할**:
  - 시스템 실행 중 생성되는 **로그 파일**, **캐시**, **메일 큐** 등의 데이터를 저장.
  - **애플리케이션 데이터**(예: 데이터베이스 파일)와 같이 크기가 유동적으로 변화하는 파일을 관리.

- **주요 하위 디렉토리**:
  - `/var/log`: 시스템 로그 파일이 저장됨.
  - `/var/tmp`: 임시 파일이 저장됨(부팅 후에도 삭제되지 않는 경우 많음).
  - `/var/cache`: 애플리케이션의 캐시 데이터 저장.
  - `/var/spool`: 대기 중인 작업(예: 프린트 작업, 메일 큐 등) 저장.

---

### **2. `log` 디렉토리**
- **의미**: "Log"는 시스템이나 애플리케이션의 **작동 기록**을 저장하는 디렉토리입니다.
- **위치**: `/var/log` 아래에 위치.
- **주요 역할**:
  - 시스템과 애플리케이션의 **운영 상태**를 기록.
  - 문제 발생 시 **디버깅**이나 **모니터링**을 위한 정보를 제공.
  - 보안 및 사용자 활동을 추적할 수 있는 기록 저장.

- **주요 파일 및 하위 디렉토리**:
  - `/var/log/syslog`: 시스템 관련 주요 로그 파일(리눅스 배포판에 따라 다를 수 있음).
  - `/var/log/messages`: 일반적인 시스템 메시지 로그.
  - `/var/log/nginx/`: **Nginx 웹 서버** 관련 로그 저장 디렉토리.
    - `nginx_access.log`: 클라이언트 요청 기록.
    - `nginx_error.log`: 웹 서버의 에러 기록.
  - `/var/log/auth.log`: 인증 관련 로그(로그인 성공/실패 등).
  - `/var/log/dmesg`: 부팅 시 발생한 메시지.

---

### **예시: `/var/log/nginx/nginx_access.log`**
1. **`/var`**:
   - 변경 가능한 데이터가 저장되는 디렉토리.

2. **`/var/log`**:
   - 로그 파일이 저장되는 디렉토리.

3. **`/var/log/nginx`**:
   - **Nginx 웹 서버**와 관련된 로그를 저장하는 디렉토리.

4. **`nginx_access.log`**:
   - Nginx 웹 서버에 들어온 클라이언트 요청 기록을 저장.
   - 예: 
     ```
     192.168.0.1 - - [21/Nov/2024:12:00:00 +0000] "GET /index.html HTTP/1.1" 200 1024
     ```
     - **IP 주소**: 192.168.0.1
     - **요청 시간**: 21/Nov/2024 12:00:00
     - **HTTP 메서드와 경로**: GET /index.html
     - **응답 코드**: 200 (성공)
     - **응답 크기**: 1024바이트

---

### **요약**
- **`var`**: 변경 가능하고 동적으로 생성되는 데이터를 저장하는 디렉토리.
- **`log`**: 시스템, 애플리케이션, 네트워크 활동 등의 로그 파일을 저장하는 디렉토리.
- **경로 `/var/log/nginx/nginx_access.log`**는 Nginx 웹 서버가 클라이언트 요청 기록을 저장하는 **로그 파일**을 나타냅니다.

 

 


 

 

 

저멀리 api key가 보인다

 

GET /admin?API_KEY=d22cb18e86fc9e23996650150461c9f794ad3a4f&cmd=uname%20-a HTTP/1.1" 200 87 

 

 

/admin?API_KEY=d22cb18e86fc9e23996650150461c9f794ad3a4f&cmd=ls -al /

 

 

저 멀리 flag 파일이보인다

 

 

/admin?API_KEY=d22cb18e86fc9e23996650150461c9f794ad3a4f&cmd=/flag

 

 

flag다!!