https://dreamhack.io/wargame/challenges/269/
@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"}')
메인 페이지의 코드를 분석해보면 sessionid
를 받고 session_storage
에 넣어준 후 admin 계정이면 flag를 출력한다. 쿠키가 존재하지 않으면 'please login'을 출력한다. 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
param이라는 파라미터를 소문자로 변경한 후 저장한다. xss 공격을 피하기 위해 필터링해준 후 화면에 출력한다.
@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>'
GET 메서드일 때는 위와 같이 출력된다.
POST 메서드일 때는 입력받은 문자열을 param
에 저장하고, admin의 sessionid
를 생성한다. admin 계정으로 check_csrf()
와 read_url()
를 거쳐 /vuln
에 접속해준다.
@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>'
마찬가지로 POST 메서드를 중심으로 코드를 분석해보자.
username
과 password
를 입력받고 일치하는 계정이 존재하면, sessionid
를 생성하고 session_storage
에 저장한 후 메인 페이지로 이동한다.
메인 페이지에 존재하지 않는 /change_password
페이지가 있다.
@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'
pw
라는 파라미터로 변경할 비밀번호를 입력받고 해당 sessionid
를 갖는 계정의 비밀번호를 변경해준다.
/change_password
에서 admin의 비밀번호를 바꾸어 로그인하면 되겠구나 생각했다. 이 페이지에 접속하기 위해 /flag
페이지를 활용했다. /flag
에서는 param
을 입력하는 순간 admin의 sessionid
를 생성해주기 때문에 /flag
에서 /change_password
에 접속하면 admin의 비밀번호를 변경할 수 있게 된다.
따라서 /flag
페이지에 접속해 <img src="/change_password?pw=admin"/>
를 입력했고, /login
페이지에서 바꾸어준 비밀번호로 로그인하여 flag를 획득했다.
DH{c57d0dc12bb9ff023faf9a0e2b49e470a77271ef}