🧩 서버사이드 템플릿 인젝션(SSTI) 시뮬레이터 Burp 00101080 · SSTI

사용자 입력이 템플릿 엔진의 식으로 실행되면, 산술 프루브 하나로 서버 엔진이 노출되고 코드 실행으로 번질 수 있습니다. Burp Suite가 실제로 쓰는 SSTI 페이로드 45개를 가상 타겟에 직접 쏴 봅니다.

① 취약한 엔드포인트 (수동 탐침) VULNERABLE

아래 입력은 서버가 템플릿 엔진(Jinja2 같은)으로 렌더합니다. 산술 표현식을 넣으면 서버가 계산해 응답에 반영되는지 확인해 보세요.

🔬 실전 스캐너 - Burp Suite 기법 적용

아래 스캐너는 실제 Burp Suite Pro가 사용하는 페이로드 전량을 이 페이지 안의 가상 타겟에 대입해 스캔합니다. 방어를 켜고 다시 스캔하면 같은 페이로드가 전부 차단되는 것을 확인할 수 있습니다. (모든 요청은 브라우저 안에서만 처리 - 실제 네트워크 전송 없음)

② 취약 vs 안전 코드

VULNERABLE
// Flask + Jinja2 - 검증 없는 render_template_string
@app.route("/render")
def render_page():
    tpl = request.args.get("tpl", "")     # 사용자 입력
    # 🚨 문자열을 '템플릿 소스'로 컴파일 - 입력이 식으로 실행됨
    return render_template_string("<h1>인사말: " + tpl + "</h1>")
SECURE
// 리터럴 렌더 + 변수 바인딩으로만 값 전달
@app.route("/render")
def render_page():
    tpl = request.args.get("tpl", "")
    # 입력은 '값'으로 바인딩 - 식으로 평가되지 않음
    return render_template_string(
        "<h1>인사말: {{ greeting }}</h1>",
        greeting=tpl)   # ✅ 실행 컨텍스트에서 분리
# 추가 방어: 템플릿 파일은 고정 자산으로 관리, 사용자 입력은 절대 템플릿 소스에 합치지 않는다
# (SandboxedEnvironment, jinja2 거부 목록도 병행 가능)
방어 핵심: 사용자 입력을 템플릿 소스에 합치지 말고 값(변수)으로 바인딩할 것 · 산술 프루브({{7*7}})는 스캐너의 SSTI 탐지 원리이기도 하다 · 에러 메시지에 엔진 내부 정보(jinja2.exceptions 등)를 노출하지 말 것.
⚠️ 교육용 시뮬레이션입니다. 모든 공격은 페이지 내부 가상 타겟에서만 시연되며 실제 네트워크 전송이나 외부 시스템 접근은 없습니다. 페이로드 집합은 Burp Suite Pro 기법 카탈로그의 교육용 엄선 서브셋입니다.