[dreamhack] csrf-2

남다은·2023년 2월 20일
0

web hacking

목록 보기
11/12

문제의 시작 화면은 아래와 같다.

📌 코드 분석

#!/usr/bin/python3
from flask import Flask, request, render_template, make_response, redirect, url_for
from selenium import webdriver
import urllib
import os

app = Flask(__name__)
app.secret_key = os.urandom(32)

try:
    FLAG = open("./flag.txt", "r").read()
except:
    FLAG = "[**FLAG**]"

users = {
    'guest': 'guest',
    'admin': FLAG
}

session_storage = {}

def read_url(url, cookie={"name": "name", "value": "value"}):
    cookie.update({"domain": "127.0.0.1"})
    try:
        options = webdriver.ChromeOptions()
        for _ in [
            "headless",
            "window-size=1920x1080",
            "disable-gpu",
            "no-sandbox",
            "disable-dev-shm-usage",
        ]:
            options.add_argument(_)
        driver = webdriver.Chrome("/chromedriver", options=options)
        driver.implicitly_wait(3)
        driver.set_page_load_timeout(3)
        driver.get("http://127.0.0.1:8000/")
        driver.add_cookie(cookie)
        driver.get(url)
    except Exception as e:
        driver.quit()
        print(str(e))
        # return str(e)
        return False
    driver.quit()
    return True


def check_csrf(param, cookie={"name": "name", "value": "value"}):
    url = f"http://127.0.0.1:8000/vuln?param={urllib.parse.quote(param)}"
    return read_url(url, cookie)


@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"}')


@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


@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>'


@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>'


@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'

app.run(host="0.0.0.0", port=8000)

🔑 vuln 함수

def vuln():
    param = request.args.get("param", "").lower()
    xss_filter = ["frame", "script", "on"]
    for _ in xss_filter:
        param = param.replace(_, "*")
    return param

코드 중에서 vuln 함수의 코드를 보면, 리스트를 사용해서 "frame", "script", "on" 키워드가 존재할 경우 *로 필터링 해주는 걸 확인할 수 있다.
따라서 이들이 아닌 다른 태그를 사용해서 익스플로잇 코드를 작성해야할 것이다.

🔑 login 함수

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>'

GET 메소드인 경우 login.html은 리턴하여 username과 password를 입력받게 해주었다.

POST 메소드인 경우 username과 password를 입력받고, username이 users에 저장되어 있는 경우 코드를 계속 진행하도록 했다. if문을 통해 pw가 password과 같은 경우 resp를 리턴하게 해줬다.

🔑 flag 함수

def flag():
    if request.method == "GET":
        return render_template("flag.html")
    elif request.method == "POST":
        param = request.form.get("param", "")
        if not check_csrf(param):
            return '<script>alert("wrong??");history.go(-1);</script>'

        return '<script>alert("good");history.go(-1);</script>'

메소드가 GET인 경우에는 flag.html을 리턴해서 url을 입력하게 하고, 메소드가 POST인 경우 param 파라미터를 가져온 후 check_csrf 함수를 통해 접속을 요청해준다.
함수의 리턴 값이 true이면 good이라는 팝업창이 뜨게 하고, false이면 wrong??이라는 팝업창이 뜨도록 해주었다.

🔑 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'

패스워드를 바꾸는 코드이다. 이 함수를 이용해서 로그인할 때 username을 admin으로 하고 password를 원하는 것으로 바꿔주면 admin으로 로그인이 될 것이다.


📌 익스플로잇


flag 페이지에 들어간 다음 아래의 코드를 쳐서 admin에 대한 패스워드를 바꿔주었다.

<img src="/change_password?pw=[사용할 임의의 패스워드]">


그 다음 다시 로그인 페이지로 이동해서 바꾼 패스워드를 admin과 함께 입력하고 로그인해준다.
다시 첫 페이지로 이동하면 성공적으로 플래그를 얻은 걸 확인할 수 있다.

profile
차곡차곡 쌓아나가는 WEB FE 개발자

0개의 댓글