https://dreamhack.io/wargame/challenges/269/
#!/usr/bin/python3
from flask import Flask, request, render_template, make_response, redirect, url_for
from selenium import webdriver
import urllib
import os
app = Flask(__name__)
app.secret_key = os.urandom(32)
try:
FLAG = open("./flag.txt", "r").read()
except:
FLAG = "[**FLAG**]"
users = {
'guest': 'guest',
'admin': FLAG
}
session_storage = {}
def read_url(url, cookie={"name": "name", "value": "value"}):
cookie.update({"domain": "127.0.0.1"})
try:
options = webdriver.ChromeOptions()
for _ in [
"headless",
"window-size=1920x1080",
"disable-gpu",
"no-sandbox",
"disable-dev-shm-usage",
]:
options.add_argument(_)
driver = webdriver.Chrome("/chromedriver", options=options)
driver.implicitly_wait(3)
driver.set_page_load_timeout(3)
driver.get("http://127.0.0.1:8000/")
driver.add_cookie(cookie)
driver.get(url)
except Exception as e:
driver.quit()
print(str(e))
# return str(e)
return False
driver.quit()
return True
def check_csrf(param, cookie={"name": "name", "value": "value"}):
url = f"http://127.0.0.1:8000/vuln?param={urllib.parse.quote(param)}"
return read_url(url, cookie)
@app.route("/")
def index():
session_id = request.cookies.get('sessionid', None)
try:
username = session_storage[session_id]
except KeyError:
return render_template('index.html', text='please login')
return render_template('index.html', text=f'Hello {username}, {"flag is " + FLAG if username == "admin" else "you are not an admin"}')
@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", "")
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('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login.html')
elif request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
try:
pw = users[username]
except:
return '<script>alert("not found user");history.go(-1);</script>'
if pw == password:
resp = make_response(redirect(url_for('index')) )
session_id = os.urandom(8).hex()
session_storage[session_id] = username
resp.set_cookie('sessionid', session_id)
return resp
return '<script>alert("wrong password");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'
app.run(host="0.0.0.0", port=8000)
풀이
메인 화면이다.
메인화면에서 vuln(csrf) page를 클릭해서 이동해보면
param에 <script></script> 구문이 인자로 들어간 것을 볼 수 있고,
이 값은 그대로 화면에 출력된다. 다만 소스를 보면 frame, script, on 키워드에 대해 필터링을 하고 있어서 *로 치환된 것을 알 수 있다.
아 ! vuln에 인자로 전달된 값은 html 영역에 실려서 출력되는 구나.
그렇다면 스크립트가 이 화면을 통해서 실행될 수 있구나. (XSS 가능) (1)
그 다음 flag를 클릭해서 이동하면
이런 화면이 나오게 된다.
소스를 확인해보면 flag 페이지의 param 값에 무언가를 입력하고 POST 요청을 발생하는 순간
admin에 대한 session ID를 생성하고 이를 쿠키로 만들어서 param에 입력한 값과 , 쿠키를 함꼐 read_url의 인자로 보내고있다. (2)
read_url에서의 동작을 살펴보자.
localhost의 8000포트 즉 지금 웹 어플리케이션이 동작하는 웹 브라우저를 하나 띄우고 admin에 대한 쿠키 값을 새로 등록한다.
그리고 vuln에 입력한 값을 param으로 보내는 url을 새로 띄운다. (3)
정리하면
param에 무언가를 입력하면 admin에 대한 쿠키 값이 브라우저에 저장되고,
vuln는 param 값을 그대로 화면에 출력하는 페이지이므로, 내가 입력한 param이 출력되거나 실행된다.
소스하단에 보면 change_password라는 API가 있는데, admin이 바뀐 상태에서 change_password를 수행하면 admin 계정의 password를 내가 설정할 수 있다.
vuln은 script , on , frame에 대해 막혀있기 때문에 <img src="/change_password?param=1"> 을 param으로 입력하면 admin 쿠키를 획득한 다음 차례로 password까지 바꿀 수 있다.
이후 변경한 password로 로그인을 수행하면 아래와 같이 키 값 획득 가능.
* param을 통해 admin password를 수정하는 로직을 수행하지 않는 경우,
read_url에서 admin 쿠키를 심은 브라우저를 띄우고 url을 실행해도 아무동작도 이뤄지지 않고 해당 브라우저가 닫히기 때문에 내 브라우저에서는 admin 쿠키는 남아있지않고, password도 변경할 수 없다.
'Security' 카테고리의 다른 글
[wargame] xss-2 (1) | 2021.09.26 |
---|---|
[wargame] xss-1 (0) | 2021.09.25 |
[Webhacking] file vulnerability (0) | 2021.09.24 |
[Webhacking] server side request forgery (SSRF) (0) | 2021.09.24 |
[Webhacking] path traversal (0) | 2021.09.24 |