1. SSTI란 무엇인가?
SSTI (Server-Side Template Injection)는 이름 그대로 서버 측 템플릿 엔진의 취약점을 이용한 공격 기법.
- 정의: 공격자가 템플릿 구문(코드)을 입력 데이터에 주입하여, 서버가 이를 일반 텍스트가 아닌 실행 가능한 코드로 인식하게 만드는 취약점.
- 결과: 사용자가 서버 측에서 임의의 코드를 실행할 수 있게 되어, 서버 장악까지 가능해짐.
템플릿 엔진(Template Engine)이란?
웹 서비스는 HTML을 동적으로 생성하기 위해 템플릿 엔진을 사용함. 예를 들어, <p>Hello, {{ username }}!</p>라는 코드가 있다면, 서버는 {{ username }} 부분에 실제 사용자 이름을 넣어 HTML을 완성함.
주요 언어별 템플릿 엔진:
- Python: Jinja2, Mako
- Node.js: EJS, Pug (Jade)
- Java: Thymeleaf, FreeMarker
2. 취약점 발생 원리: "텍스트가 아닌 코드로 인식"
SSTI는 개발자가 사용자의 입력을 **템플릿의 일부로 직접 연결(Concatenation)**할 때 발생함.
취약한 코드 예시 (Python Flask + Jinja2):
# 취약한 코드
name = request.args.get("name")
# 사용자의 입력을 템플릿 문자열에 그대로 합쳐버림
return render_template_string("Hello " + name)
위와 같이 코드를 작성하면, 공격자가 name 파라미터에 {{ 7*7 }}을 입력했을 때, 서버는 이를 문자열 {{ 7*7 }}이 아닌 수식으로 파싱하여 연산 결과인 Hello 49를 반환합니다.
핵심: 개발자는 "문자열 출력"을 기대했지만, 템플릿 엔진은 이를 "문법"으로 해석해버린 것.
3. 공격의 파급력: 단순 연산에서 RCE까지
SSTI가 확인되면 공격자는 다음과 같은 피해를 입힐 수 있음.
-
- 정보 탈취: 서버 내부의 환경 변수나 설정 파일 열람.
- RCE (Remote Code Execution): 템플릿 엔진의 내부 객체에 접근하여 서버에서 임의의 명령어를 실행.
특히 Jinja2(Python) 환경에서는 Python 언어의 특성을 이용해 샌드박스를 우회하고 OS 명령어까지 실행하는 것이 가능함. 이를 이해하기 위해서는 Python의 객체 구조를 알아야 함.
4. 심화: Python 객체 구조와 RCE 페이로드 구성
PPT에서는 Python이 C언어와 달리 인터프리터 언어이며, 실행 시 모든 것을 객체(Object)로 관리한다는 점을 강조함.
공격자는 이 "객체 연결 고리"를 타고 올라가 os 모듈을 로드하고 쉘 명령어를 실행. 이를 Magic Method Chain이라고 부름.
공격 페이로드 분석 (Jinja2)
목표는 os.popen('/bin/bash').read()를 실행하는 것. 하지만 템플릿 내에서는 import os를 직접 할 수 없으므로, 이미 로드된 모듈을 찾아야 함.
Step 1: 현재 객체에서 클래스로 접근 빈 문자열('')도 객체입니다. 이를 통해 str 클래스에 접근.
''.__class__ # <class 'str'>
Step 2: 상위 클래스(Object)로 이동 MRO(Method Resolution Order)나 __base__를 통해 최상위 클래스인 object로 올라감.
''.__class__.__base__ # <class 'object'>
Step 3: 모든 서브클래스 확인 object를 상속받는 모든 클래스 리스트를 가져옵니다. 이 중에는 우리가 원하는 모듈을 포함하고 있는 클래스가 숨어있음.
''.__class__.__base__.__subclasses__()
Step 4: 취약한 클래스 선정 및 전역 변수 접근 보통 warnings.catch_warnings 같은 클래스(인덱스 144번 등 환경마다 다름)를 타겟으로 함. 이 클래스의 __init__ 메서드 내부의 전역 변수(__globals__)에는 sys 모듈이 포함되어 있기 때문.
Step 5: os 모듈 로드 및 명령어 실행 sys 모듈을 통해 이미 로드되어 있는 os 모듈을 가져오고, popen 함수를 실행합니다.
# 완성된 페이로드 예시
{{ ''.__class__.__base__.__subclasses__()[144].__init__.__globals__['sys'].modules['os'].popen('id').read() }}
이 코드가 서버에 주입되면, 서버는 id 명령어를 실행하고 그 결과를 웹 페이지에 띄워주게 됨.
5. 대응 방안 (Mitigation)
SSTI를 예방하는 가장 확실한 방법은 사용자의 입력을 절대 템플릿 코드로 해석되지 않도록 하는 것.
- 입력값 검증: 사용자 입력이 템플릿 구문과 섞이지 않도록 함.
- 데이터 바인딩 사용: render_template_string("Hello " + name) 대신, render_template("hello.html", name=user_input) 처럼 템플릿 엔진의 변수 전달 기능을 사용해야 함. 이렇게 하면 엔진은 입력을 단순 문자열로 처리.
6. 문제풀기
https://dreamhack.io/wargame/challenges/39
로그인 | Dreamhack
dreamhack.io
풀이(아직안함)
'설명' 카테고리의 다른 글
| 시스템 해킹 입문: Buffer Overflow와 스택 메모리 구조 (0) | 2026.01.16 |
|---|---|
| Mac에서 CTF / pwn 실습용 원격 개발 환경 구축 (0) | 2026.01.15 |
| Web Shell과 Reverse Shell (0) | 2026.01.05 |
| XSS란? (0) | 2026.01.03 |
| SQL injection 이란? (0) | 2025.12.31 |