csrf-2
wargame, web
문제
CSRF 공격으로 플래그를 얻는 문제이다.
문제 분석
이 문제도 이전 csrf-1 과 유사하게 클라이언트의 취약점이기 때문에, 이를 트리거할 봇이 백엔드에 구현되어 있다.
서버 소스를 다운로드 받아 확인해보았다.
@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"}')
세션 아이디를 사용해 사용자를 식별하고, 사용자가 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
또한 이전 문제와 마찬가지로 vuln 엔드포인트에서 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>'
랜덤으로 만들어진 관리자의 세션 ID를 session_storage 딕셔너리에 저장하고 이전 문제의 check_csrf 코드처럼 서버에서 브라우저를 실행하고 그 브라우저에 쿠키로 관리자의 세션 ID를 설정한다.
이전 문제에서는 FLAG를 메모에 추가하는 엔드포인트를 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 엔드포인트가 있는 것을 볼 수 있다. 이 엔드포인트는 로그인되어있는 사용자의 비밀번호를 변경하는 엔드포인트이다.
풀이
이전 문제와 유사하게 <link> 태그를 이용하여 CSRF 공격을 수행하였다.
<link rel="stylesheet" href="http://localhost:8000/change_password?pw=1234" />
위 페이로드를 /flag 페이지 폼에 넣어 전송하면, 서버에서 실행 중인 관리자 권한을 가진 브라우저가 위 페이로드가 담긴 페이지를 로드하게 되고, 서버의 브라우저는 페이로드의 스타일시트를 로드하기 위해 /change_password 엔드포인트에 요청을 보내게 된다. 서버는 이 요청을 신뢰하여 admin 계정의 비밀번호를 1234 로 변경하게 된다.
이후 admin 계정으로 로그인하면

위 사진과 같이 플래그를 획득하게 된다.