여러 기능과 입력받은 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 |