[문제]
[풀이]
@app.route("/vuln")
def vuln():
param = request.args.get("param", "").lower()
xss_filter = ["frame", "script", "on"]
for _ in xss_filter:
param = param.replace(_, "*")
return param
/vuln 페이지를 구성하는 코드이다. 코드를 살펴보면 이용자가 전달한 param 파라미터의 값을 출력한다. 이때, 이용자의 파라미터에 "frame", "script", "on" 3가지의 악성 키워드가 포함되어 있으면 이를 '*' 문자로 치환한다. 해당 키워드 필터링은 XSS 공격을 방지하기 위한 목적으로 존재한다.
/vuln 페이지에 접속하면 <script> 태그가 <*>로 치환된 것을 확인할 수 있다.
@app.route("/flag", methods=["GET", "POST"])
def flag():
if request.method == "GET":
return render_template("flag.html")
elif request.method == "POST":
param = request.form.get("param", "")
session_id = os.urandom(16).hex()
session_storage[session_id] = 'admin'
if not check_csrf(param, {"name":"sessionid", "value": session_id}):
return '<script>alert("wrong??");history.go(-1);</script>'
return '<script>alert("good");history.go(-1);</script>'
/flag 페이지를 구성하는 코드이다. 코드를 살펴보면 메서드에 따른 요청마다 다른 기능을 수행한다. GET 메서드일 경우 이용자에게 URL을 입력받는 페이지를 제공한다. POST 메서드일 경우 admin에 대한 session_id를 생성하고, param 파라미터 값과 함께 check_csrf 함수의 인자로 넣고 호출한다.
@app.route("/change_password")
def change_password():
pw = request.args.get("pw", "")
session_id = request.cookies.get('sessionid', None)
try:
username = session_storage[session_id]
except KeyError:
return render_template('index.html', text='please login')
users[username] = pw
return 'Done'
/change_password 페이지를 구성하는 코드이다. 코드를 살펴보면 GET 메서드로 pw 파라미터 값과 session_id 값을 가져오고 있다. session_id 값을 통해 username을 찾은 다음 해당 username의 pw를 pw 파라미터 값으로 변경한다.
users = {
'guest': 'guest',
'admin': FLAG
}
코드를 살펴보면 admin 계정으로 로그인해야 flag를 얻을 수 있음을 확인할 수 있다. /vuln 페이지를 방문하는 로컬 호스트 이용자가 /change_password 페이지로 admin 계정에 해당하는 password 값과 함께 요청을 전송하게끔 하는 것이 이 문제의 핵심으로 보인다.
/flag 페이지에서 로컬 호스트 이용자가 /change_password 페이지로 방문하도록 하는 코드를 이어서 작성한다. 이때, admin 계정에 해당하는 password 값과 함께 방문해야하기 때문에 <img src="/change_password?pw=admin" />와 같이 익스플로잇 코드를 입력한다.
변경한 password를 통해 admin 계정으로 로그인을 진행한 다음 메인 화면으로 돌아가면 플래그를 확인할 수 있다.