XSS(Cross-Site Scripting)는 공격자가 악성 스크립트를 다른 사용자의 브라우저에서 실행할 수 있도록 만드는 공격 기법이다. XSS는 웹 애플리케이션이 사용자 입력을 적절하게 처리하지 않고, 검증이나 인코딩 없이 그대로 출력할 때 발생한다.
Stored XSS
- 악성 스크립트가 서버에 저장되어 다른 사용자가 페이지를 방문할 때 실행
Reflected XSS
- 악성 스크립트가 URL이나 요청에 포함되어 서버 응답 시 즉시 실행
DOM-based XSS
- 클라이언트 측에서 DOM(Document Object Mode)을 조작하는 과정에서 악성 스크립트가 실행
DOM
#!/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**]"
# URL과 쿠키를 받아서 쿠키를 업데이트하고 URL을 읽는 함수
def read_url(url, cookie={"name": "name", "value": "value"}):
cookie.update({"domain": "127.0.0.1"}) # 쿠키의 도메인을 로컬 서버로 업데이트
try:
service = Service(executable_path="/chromedriver") # ChromeDriver 서비스 설정
options = webdriver.ChromeOptions() # Chrome 옵션 설정
for _ in [
"headless", # 브라우저 창 없이 실행 (헤드리스 모드)
"window-size=1920x1080", # 가상 브라우저 창 크기 설정
"disable-gpu", # GPU 사용 비활성화
"no-sandbox", # 샌드박스 모드 비활성화
"disable-dev-shm-usage", # /dev/shm 사용 비활성화 (메모리 제한 문제 방지)
]:
options.add_argument(_) # 옵션을 Chrome에 추가
driver = webdriver.Chrome(service=service, options=options) # WebDriver 객체 생성
driver.implicitly_wait(3) # 암묵적 대기 시간 설정 (3초)
driver.set_page_load_timeout(3) # 페이지 로드 타임아웃 설정 (3초)
driver.get("http://127.0.0.1:8000/") # 기본 페이지 로드 (로컬 서버)
driver.add_cookie(cookie) # 설정된 쿠키를 브라우저에 추가
driver.get(url) # 지정된 URL로 이동
except Exception as e:
driver.quit() # 예외 발생 시 브라우저 종료
# return str(e)
return False # 실패 시 False 반환
driver.quit() # 정상적으로 실행된 경우 브라우저 종료
return True # 성공 시 True 반환
# XSS(크로스 사이트 스크립팅) 공격이 발생할 수 있는지 확인하는 함수
def check_xss(param, cookie={"name": "name", "value": "value"}):
url = f"http://127.0.0.1:8000/vuln?param={urllib.parse.quote(param)}" # XSS 공격을 테스트할 URL 생성
return read_url(url, cookie) # 생성된 URL을 read_url 함수로 전달하여 XSS 테스트 수행
# index.html을 렌더링
@app.route("/")
def index():
return render_template("index.html")
# 사용자 입력을 그대로 반환 (XSS 취약성 존재)
@app.route("/vuln")
def vuln():
param = request.args.get("param", "")
return param
# POST 요청에서 XSS 공격 성공 여부에 따라 다른 응답 반환
@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")
# XSS 공격 성공 여부 확인, 실패 시 경고창 출력
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)
경로
- / - 메인 페이지
- /memo
사용자에게 get 요청으로 memo 값 받아서 길고 기본값은 hello- /vuln
URL 파라미터 그대로 반환- /flag
get - flag.html 받아옴
post를 통해 제출시 xss 공격이 들어가는지 체크한다
/memo에서 직접적으로 스크립트를 실행할 방법은 따로 보이지 않으므로 /flag에서 진행하는것이 맞다고 판단하였다.
/vuln에서는 사용자 입력을 그대로 반환하지만 flag 얻을 방법이 보이지 않는다.
따라서 /flag 경로에서 친절히 /vuln경로를 쓸수 있게 지정 해주었으므로 이를 활용 하였다.
/flag 경로에서는 사용자가 POST 요청으로 제출한 param 값을 check_xss 함수에 전달하는데 이 함수는 내부적으로 read_url 함수를 호출하여 Selenium을 통해 /vuln경로 이동하여 쿠키를 업데이트한다. 이때 쿠키는 flag 값이 된다. 이 쿠키를 /memo를 통해 기록해주면 될 것이다.
<script>location.href = '/memo?memo='+document.cookie;</script>
location.href는 자바스크립트에서 현재 페이지의 URL을 가리키는 속성
위 코드를 삽입하면 memo 에 flag값인 쿠키를 작성할 수 있다.