Dreamhack csrf-1 문제 풀이

    728x90

    1. 문제 설명

    csrf 취약점을 이용하여 FLAG값을 얻어내는 문제이다. 기존에 풀이했었던 xss와 동일한 방식으로 생각해보면 되지 않을까 싶다.

     

    2. 문제 풀이

    간단하게 기존 Web 문제랑 동일한 형식으로 페이지가 구성되어 있음을 확인 가능하다.

     

    vuln(csrf) page를 눌러보면 script가 필터링 된거 같이 나온다. 즉 script를 사용하지 못하는 것 같다.

     

    앞선 문제들 처럼 memo 페이지에 FLAG값을 출력하면 될 것 같다.

     

    notice_flag는 Access Denied가 뜨면서 접근이 불가능하다.

    소스코드를 살펴보자.

    #!/usr/bin/python3
    from flask import Flask, request, render_template
    from selenium import webdriver
    from selenium.webdriver.chrome.service import Service
    import urllib
    import os
    
    app = Flask(__name__)
    app.secret_key = os.urandom(32)
    
    try:
        FLAG = open("./flag.txt", "r").read()
    except:
        FLAG = "[**FLAG**]"
    
    
    def read_url(url, cookie={"name": "name", "value": "value"}):
        cookie.update({"domain": "127.0.0.1"})
        try:
            service = Service(executable_path="/chromedriver")
            options = webdriver.ChromeOptions()
            for _ in [
                "headless",
                "window-size=1920x1080",
                "disable-gpu",
                "no-sandbox",
                "disable-dev-shm-usage",
            ]:
                options.add_argument(_)
            driver = webdriver.Chrome(service=service, 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():
        return render_template("index.html")
    
    
    @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", "")
            if not check_csrf(param):
                return '<script>alert("wrong??");history.go(-1);</script>'
    
            return '<script>alert("good");history.go(-1);</script>'
    
    
    memo_text = ""
    
    
    @app.route("/memo")
    def memo():
        global memo_text
        text = request.args.get("memo", None)
        if text:
            memo_text += text
        return render_template("memo.html", memo=memo_text)
    
    
    @app.route("/admin/notice_flag")
    def admin_notice_flag():
        global memo_text
        if request.remote_addr != "127.0.0.1":
            return "Access Denied"
        if request.args.get("userid", "") != "admin":
            return "Access Denied 2"
        memo_text += f"[Notice] flag is {FLAG}\n"
        return "Ok"
    
    
    app.run(host="0.0.0.0", port=8000)

    Cross-site request forgery(CSRF) 방식을 이용하여 FLAG를 탈취하며, <script>가 사용이 안되는 부분에서 우리가 생각해볼 수 있는 것은, xss-2와 동일한 방식으로 img 태그로 우회를 통해 /admin 하위에 있는 notice_flag를 실행시킴과 동시에 userid가 admin이 아니면 Access Denied가 뜨게 되어 있으므로, userid값이 admin이 되어야 한다.

    <img src="/admin/notice_flag?userid=admin"> 이런 형태로 입력하면 위에 말한 조건을 전부 만족한다.

     

    입력을 했더니 good이라고 뜬다. 정상적으로 FLAG값이 출력되었는지 확인해보자.

     

    memo에서 정상적으로 FLAG값이 출력됨을 확인할 수 있다.

     

    3. 문제 정리

    img 태그로 우회와 userid를 admin으로 속이면서 접근하였을 때, 정상적으로 FLAG를 취득함을 확인할 수 있었다.

    댓글