본문 바로가기
웹 해킹/드림핵

[Dreamhack] csrf-1

by L3m0n S0ju 2021. 7. 15.

 

이번 문제는 CSRF 취약점을 이용해 플래그를 획득해야 한다. CSRF란 사용자를 직접 공격하는 XSS 공격과 다르게 다른 권한을 가진 사용자에게 특정 행동을 유도하는 공격기법이다. 문제에서 주어진 코드는 아래와 같다.

 

 

 

 

 

app.py


#!/usr/bin/python3
from flask import Flask, request, render_template
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**]"


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/csrf?csrf={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)


코드를 살펴보면 vuln, flag, memo, admin/notice_flag 이름을 가진 4개의 페이지가 존재한다. vuln을 보면 script, frame 같은 단어는 사용할 수 없으므로 xss 공격은 사용하기 어렵다. 문제의 핵심은 접근 제어가 동작하는 /admin/notice_flag페이지에 있음을 직감적으로 알 수 있다.

 

/admin/notice_flag 코드를 살펴보면 2가지 조건이 있다.

 

1) 127.0.0.1로 접근할 것  

2) userid = admin

 

조건 (1) 로컬 주소를 사용하기 위해서는 check_csrf 함수를 거쳐야 한다. 그리고 check_csrf는 flag 페이지에서 호출하므로 flag 페이지에서 모든 공격코드를 주입해야한다. flag 페이지는 아래 그림과 같다.

 

 

flag 페이지 입력카에 입력한 문자열은 check_csrf에 인자로 입력되는데 코드는 아래와 같다.

 

http://127.0.0.1:8000/csrf?csrf={urllib.parse.quote(param)}

 

이곳에 <img src="/admin/notice_flag?userid=admin"> 을 입력하면

http://127.0.0.1:8000/csrf?csrf=<img src="/admin/notice_flag?userid=admin">으로 전체 코드가 완성되고 127.0.0.1:8000 안에서 상대주소인 /admin/notice_flag?userid=admin에 접근하면 flag_notice 페이지의 두 가지 조건을 모두 만족시키게 되고 memo 페이지에 플래그가 출력된다.

 

 

플래그

'웹 해킹 > 드림핵' 카테고리의 다른 글

[Dreamhack] pathtraversal  (0) 2021.07.16
[Dreamhack] proxy-1  (0) 2021.07.16
[Dreamhack] xss-1  (0) 2021.07.15
[Dreamhack] Carve Party  (0) 2021.04.22
[Dreamhack] web-misconf-1  (0) 2021.03.29

댓글