본문 바로가기

드림핵 워게임

[드림핵/워게임] csrf-1 write-up

https://dreamhack.io/wargame/challenges/26/

 

csrf-1

여러 기능과 입력받은 URL을 확인하는 봇이 구현된 서비스입니다. CSRF 취약점을 이용해 플래그를 획득하세요. 문제 수정 내역 2023.07.18 css, html 제공 및 read_url() 코드 일부가 변경되었습니다. Referen

dreamhack.io

 

xss 문제들과 거의 똑같은 페이지이다.

 

먼저 vuln 페이지이다. parameter로 넘겨 준 script 태그가 *로 치환된 것을 보아 필터가 있는 모양이다.

 

 

admin/notice_flag 페이지를 들어가면 다음과 같이 뜬다. memo, flag 페이지는 xss 문제와 동일하므로 생략하겠다.


코드

vuln

@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

1. parameter로 받은 입력값을 param 변수에 저장

2. xss를 막기 위해 frame, script, on을 *로 바꿔 준 후, param 반환

 

 

admin/notice_flag

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

1. 접속자의 ip가 127.0.0.1이고, parameter로 받은 userid가 admin이면 플래그 출력

 

 

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("/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>'

1. GET 방식 -> html 반환

2. POST 방식 -> param 변수에 parameter로 받은 param 저장

3. param 변수를 vuln 페이지의 parameter로 주고, URL 접속

 

xss 문제들과 다르게 쿠키에 flag를 저장하지 않는다.


익스플로잇

1. 접속자의 아이피를 127.0.0.1로 맞춰 주기 위해 flag 페이지에서 admin/notice_flag 페이지 접근

2. 접근할 때 parameter로 userid에 admin 값 넣어 주기

3. vuln 페이지의 필터링에 걸리지 않도록 하기

 

img 태그의 src 속성을 이용해 익스플로잇 코드를 넣어 주었다.

<img src="/admin/notice_flag?userid=admin"/>

코드를 제출하고 memo 페이지에 들어가 보면 flag 가 잘 떠 있다.