본문 바로가기
Dreamhack WarGame

[Dreamhack] xss-1 문제 풀이

by whiteTommy 2024. 1. 6.

 

 

여러 기능과 입력받은 URL을 확인하는 봇이 구현된 웹 서비스이므로 웹으로 접속한다.

 

 

 

XSS에 관한 문제로 보이며 vlun(xss) page와 memo, flag 매뉴얼이 존재한다.

 

 

여기서, 각각의 기능에 대해 알기 위해서 주어진 문제 파일의 압축을 풀고 python으로 작성된 파일을 열어보자.

#!/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()
        # return str(e)
        return False
    driver.quit()
    return True

def check_xss(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", "")
    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_xss(param, {"name": "flag", "value": FLAG.strip()}):
            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", "")
    memo_text += text + "\n"
    return render_template("memo.html", memo=memo_text)

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

  

위의 코드는 python 코드로 작성된 Flask 프레임워크이다. 문제에서 찾고자 하는 플래그는 FLAG 변수에 저장되어 있고, XSS 취약점을 이용해서 찾으면 될 것으로 보인다.

 

우선, read_url, check_xss 메서드를 살펴보자.

  • read_url 메서드
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()
        # return str(e)
        return False
    driver.quit()
    return True

 

위의 코드는 Selenium을 사용하여 지정된 URL과 쿠키를 입력받아서 페이지를 읽는 메서드이다. 

 

  • check_xss 메서드
def check_xss(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)

 

위의 코드는 param 이라는 입력값과 쿠키를 받고, param을 이용해서 url을 만들어서  https://127.0.0.1:8000/vuln 로컬 서버로 요청을 보낸다. 이 url을  read_url의 param의 인자로 넘겨줘서 해당 URL을 통해 페이지를 읽어서 XSS 공격이 가능한지를 확인하는 메서드이다. 왜냐하면, 앞서 언급한 read_url 메서드에서 예외가 발생했다는 것은 false를 return 한다는 것이고, 이는 xss 공격이 불가능하다는 것을 의미하기 때문이다. 

 

다음으로, 엔드포인트를 분석해보자.

  • / 엔드포인트 : index.html 페이지를 보여준다.
  • /vuln 엔드포인트: param 값을 얻어서 입력값 자체를 보여준다.

 

 

 

 

  • /memo 엔드포인트: memo값을 얻어서 text에 저장한 후 이를 전역변수 memo_text에 저장한다. memo.html로 렌더링 하여 클라이언트에 보여준다.

 

 

  • /flag 엔드포인트: 플래그 값이 들어 있는 것으로 보이며, 메서드 방식에 따라 동작 방식이 다르다.
@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_xss(param, {"name": "flag", "value": FLAG.strip()}):
            return '<script>alert("wrong??");history.go(-1);</script>'

        return '<script>alert("good");history.go(-1);</script>'
  • GET 메서드: flag.html 페이지를 렌더링하여 클라이언트에 보여준다.
  • POST 메서드: 입력된 param 값을 param 변수에 저장하고, 이 값과 name, value 값을 check_css에 던져서 xss 공격이 가능하지 않으면 "good"을 팝업에 띄워주고, 가능하다면 즉, XSS 취약점이 있다면 "wrong??" 팝업을 띄워준다.

 

결국, 우리가 얻고자 하는 값인 플래그는 /flag 엔드포인트에서 check_xss로 넘겨주는 cookie에 들어가 있는 값이다. 이제, /flag를 확인해 보자.

 

param 값에 어떠한 값을 주어야 flag를 얻을 수 있는지 생각해 보자.

 

def check_xss(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)

 

위의 코드를 보면 param에 주는 값이 urllib.parse.quote(param)이 되고, 이 값은 param값으로 url 주소를 인코딩해주는 코드이다. 즉, 사용자가 입력한 param 값을 127.0.0.1:8000  로컬 url에서  /vuln 엔드포인트에 url 주소 자체를 출력해 준다. 

 앞서 언급했지만, /vuln 엔드포인트를 보면 host3.dreamhack.games:13947 서버에서 param값에 <script> alert(1)</script>가 있었지만, 이제는 다른 로컬 url에서 flag가 있는 cookie에 param값과 함께 넘겨줄 수 있다.

 이제, flag가 들어있는 cookie의 정보를 알기 위해서 param값에 자바스크립트 코드를 입력하여 탈취를 해보자.

 

/memo 엔드포인트에서 param 값을 memo.html 에 렌더링 하여 사용자에게 보여준다고 하였다. memo에 찾고자 하는 플래그를 출력하기 위해 /memo 엔드포인트에 document.cookie 값을 memo에 저장하여 이를 location.href의 url 주소로 넘겨주면 memo에서 플래그를 얻을 수 있다.

 

http://127.0.0.1:8000/vuln?param = <script> location.href = "/memo? memo=" + document.cookie; </script> 입력하면,

 

아래와 같은 팝업이 출력되고,

 

/memo 매뉴얼에 들어가서 출력값을 확인해 보면 플래그를 찾을 수 있다.

 

'Dreamhack WarGame' 카테고리의 다른 글

[Dreamhack] csrf-2 문제 풀이  (0) 2024.03.15
[Dreamhack] csrf-1 문제 풀이  (0) 2024.03.15
[Dreamhack] Cookie 문제 풀이  (2) 2024.01.04
[Dreamhack] blue-whale 문제 풀이  (1) 2023.11.30
[Dreamhack] phpreg 문제 풀이  (1) 2023.11.28