### **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는 현대 소프트웨어 개발에서 필수적인 요소로, 앱이나 서비스를 개발할 때 데이터와 기능을 효율적으로 사용할 수 있도록 돕는 도구입니다.
ㅇㅎㅇㅎ 메뉴 주방 손님!
소스코드 스윽 보고 한번 루트 들어가봤다
소스코드 자세히 보러가자
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 웹 서버가 클라이언트 요청 기록을 저장하는 **로그 파일**을 나타냅니다.
GET /admin?API_KEY=d22cb18e86fc9e23996650150461c9f794ad3a4f&cmd=uname%20-a HTTP/1.1" 200 87
/admin?API_KEY=d22cb18e86fc9e23996650150461c9f794ad3a4f&cmd=ls -al /
/admin?API_KEY=d22cb18e86fc9e23996650150461c9f794ad3a4f&cmd=/flag
'Dreamhack > Dreamhack Wargame (Challenge)' 카테고리의 다른 글
[75] IT 비전공자 [dreamhack]SingleByteXor문제 풀기 (0) | 2024.11.23 |
---|---|
[74] IT 비전공자 [dreamhack]Apache htaccess문제 풀기 (0) | 2024.11.22 |
[72] IT 비전공자 [dreamhack]Secure Mail문제 풀기 (2) | 2024.11.20 |
[71] IT 비전공자 [dreamhack] Basic_Forensics_1문제 풀기 (2) | 2024.11.19 |
[70] IT 비전공자 [dreamhack] baby-union문제 풀기 (2) | 2024.11.18 |