https://xss-game.appspot.com
Level 1: Hello, world of XSS

문제 풀이
- 공격 유형: Reflected XSS (입력값이 필터링 없이 HTML에 삽입됨)
- 취약점: query 값을 HTML에 escape 없이 직접 삽입함
- 소스코드→ 간단하게 <script> 태그 안에서 alert()을 실행하면 브라우저가 태그를 인식함
- *페이로드
*페이로드(Payload): 공격자가 시스템에 전달하는 실행 가능한 명령어나 코드<script>alert()</script> - 완성된 URL
https://xss-game.appspot.com/level1/frame?query=<script>alert()</script> // 입력값이 필터링 없이 그대로 삽입되어 XSS 성공
Level 2: Persistence is key

문제 풀이
- 공격 유형: Stored XSS
- 취약점: <script> 태그는 필터링되지만 이미지의 onerror는 필터링 안 됨
* <script>alert()</script> → 실패 (스크립트 필터링) - 우회 페이로드
<img src=nothing onerror=alert()> <!-- <img> 태그의 onerror 속성을 이용한 스크립트 실행 --> <!-- src="x"로 인해 이미지 로드 실패 → onerror 트리거 → alert() 실행 --> <!-- Tip: CheatSheet에서 찾아서 넣어보기 --> <img src/onerror=alert(1)>
Level 3: That sinking feeling

문제 풀이
- 공격 유형: *DOM-based XSS (frontend JavaScript가 URL fragment를 처리함)
*DOM(Document Object Model): 웹 브라우저가 HTML 문서를 구조화해서 표현한 트리 구조
⇒ HTML 문서의 각 요소(태그)를 JS로 제어할 수 있게 만든 구조
*DOM-based XSS: 공격자가 실행 중인 JS를 조작해서 악성 스크립트를 DOM 내부에 삽입하거나 실행시키는 공격 - 취약점: URL fragment(#..)가 chooseTab(num) 함수에서 innerHTML에 넘어가면서 <img src=...>에 포함됨
- 공격 방식
- URL 끝에 존재하지 않는 이미지 번호 전달 (#4)
- 이미지 로드 실패 시 onerror 발생
- onerror에 스크립트 삽입
- Hints 분석
- 버그의 원인을 찾으려면 JavaScript를 검토하여 사용자가 제공한 입력을 처리하는 위치 확인
= 페이지에서 사용자 입력이 어디에서 쓰이는 지 확인하기 → URL fragment 부분(ex. #2)이 입력으로 간주됨
즉, *window.location.hash 값을 JS에서 처리하고 있으며, 해당 값이 이미지 src 속성에 사용되고 있음
* window.location.hash: URL에서 # 뒤에 있는 값 - window.location 객체의 데이터는 공격자의 영향을 받을 수 있음
= window.location.hash는 사용자가 조작할 수 있는 부분이므로 취약한 값임 - 주입 지점을 확인한 후, 새로운 HTML 요소를 몰래 들여오기 위해 무엇을 해야 하는지 생각
→ 이미지 태그 속성에 조작된 문자열(hash 값)을 삽입할 수 있음<script> function chooseTab(num) { // Dynamically load the appropriate image. var html = "Image " + parseInt(num) + "<br>"; html += "<img src='/static/level3/cloud" + num + ".jpg' />"; // 입력값 주입 지점 $('#tabContent').html(html); window.location.hash = num; ... - 이전과 마찬가지로, 페이지 로드 후 브라우저가 추가된 스크립트를 실행하지 않기 때문에 <script>...</script>는 미작동
= 브라우저 보안 정책상 innerHTML로 삽입된 <script> 태그는 실행되지 않음
→ 이벤트 기반 XSS(<img onerror=...>)를 사용해야 함
- 버그의 원인을 찾으려면 JavaScript를 검토하여 사용자가 제공한 입력을 처리하는 위치 확인
- 페이로드
#4' onerror='alert(1)
Level 4: Context matters

문제 풀이
- 공격 유형: JavaScript 속 문자열 조작을 통한 XSS (= JS context 주입)
- 취약점: timer 값이 onload="startTimer('{{ timer }}');"에 삽입됨
- Hints 분석
- startTimer 함수 호출 방식 확인
= 브라우저가 onload 이벤트에서 startTimer('{{timer}}')를 호출하고 있음<body id="level4"> <img src="/static/logos/level4.png" /> <br> <img src="/static/loading.gif" onload="startTimer('{{ timer }}');" /> // --> 인자가 '로 감싸져 있음 <br> <div id="message">Your timer will execute in {{ timer }} seconds.</div> </body>
→ startTimer('5')와 같이 인자가 '로 감싸져 있음 - 브라우저가 태그 속성을 구문 분석할 때, 값을 HTML로 먼저 디코딩 함 <foo bar='z'> = <foo bar='z'
= HTML 속성 값에서 특수 문자가 HTML 인코딩되어 있어도 브라우저는 최종적으로 해석된 값을 사용함
→ 문자열을 정확한 위치에 끼워넣으면 HTML 인코딩 여부와 상관없이 JavaScript에 영향 줄 수 있음 - 하나의 인용문(')을 입력하고 오류 콘솔을 확인
→ 입력에 ' 삽입 시 문자열이 끊김 (JavaScript는 문자열이 제대로 닫히지 않으면 에러 발생)
→ 그 뒤에 삽입한 코드를 독립적인 JavaScript로 실행시킬 수 있음
- startTimer 함수 호출 방식 확인
- 페이로드 시도:
- 5'); alert(1)// → 실패(타이머 정상 작동)
- 주의: ' 형식에 맞춰 닫아야 함
- 페이로드
3'); alert('1 <!-- 3'); alert('1)도 가능! 1) 자체를 문자열로 인식함 -->
Level 5: Breaking protocol

문제 풀이
- 공격 유형: DOM-based / URL Redirection XSS
- 취약점: next 파라미터가 <a href="{{ next }}"> 또는 window.location="{{ next }}"에 삽입됨
- Hints 분석
- 레벨 제목이 힌트 → "프로토콜을 깨는 것"이 핵심
= https://, http:// 말고도 다른 프로토콜(ex. javascript:)이 있다는 것
→ javascript: 로 시작하는 URL을 링크에 넣으면, 링크 클릭 시 JS 코드가 실행됨 - signup 프레임의 소스 코드를 보면 URL 파라미터가 어디에 쓰이는지 알 수 있음
= next=... 값이 href 속성에 그대로 삽입됨<a href="{{ next }}">Next >></a>
→ next=javascript:alert(1)로 설정 시
해당 링크를 누르면 alert(1) 실행!<a href="javascript:alert(1)"></a> - onclick 없이 링크를 클릭해 JS를 실행하는 방법
→ <a href="javascript:..."> - IETF draft 보기
= javascript: URI 공식 문서 / javascript:를 URL로 쓰는 것이 표준적으로 가능한 일임
- 레벨 제목이 힌트 → "프로토콜을 깨는 것"이 핵심
- 페이로드
?next=javascript:alert(1) - 최종 URL
※ URL 위조 후 GO 버튼 클릭 후 Next 버튼을 눌러야 위조된 URL이 반영됨https://xss-game.appspot.com/level5/frame/signup?next=javascript:alert()
Level 6: Follow the 🐇

문제 풀이
- 공격 유형: DOM-based / Data *스킴 XSS
*스킴: URL의 가장 앞 부분, 어떤 방식으로 자원에 접근할지를 나타내는 부분
ex. 스킴://호스트/경로?쿼리 - 취약점: *location.hash를 scriptEl.rec = url에 그대로 삼입함. http(s): 포함 시 차단 정책은 존재
*location.hash: URL에서 # 뒤 문자열
ex. https://example.com/page#hello일 경우, location.hash는 #hello, location.hash.substring(1)은 hello
→ data: 스킴으로 우회 가능// index.html <script> function includeGadget(url) { var scriptEl = document.createElement('script'); if (url.match(/^https?:\/\//)) { // 차단 조건: http:// 또는 https://로 시작하면 로드 안 됨 setInnerText(document.getElementById("log"), "Sorry, cannot load a URL containing \"http\"."); return; } scriptEl.src = url; // 전달받은 URL을 scriptEl.src에 설정 scriptEl.onload = function() { ... }; scriptEl.onerror = function() { ... }; document.head.appendChild(scriptEl); // <script src="...">를 <head>에 추가 = 조작한 값이 스크립트로 실행됨 } // Take the value after # and use it as the gadget filename. function getGadgetName() { return window.location.hash.substr(1) || "/static/gadget.js"; // hash에서 '#' 제거 = # 뒤 값이 외부 스크립트 경로! } includeGadget(getGadgetName()); // 함수가 호출 시 조작한 URL #(hash) 값을 통해 악성 자바스크립트를 로드하고록 만들어야 함 // Extra code so that we can communicate with the parent page window.addEventListener("message", function(event){ if (event.source == parent) { includeGadget(getGadgetName()); } }, false); </script> - 페이로드
#data:text/javascript,alert(1) - 공격 흐름
- 사용자가 URL에 #data:text/javascript,alert(1)을 넣고 접속
- getGadgetName() → data:text/javascript,alert(1) 리턴
- includeGadget() → <script src=...> 생성
- <script src="data:text/javascript,alert(1)"> 실행
- alert(1) 팝업 성공 → ✅ XSS
정리
| level | 공격 유형 | 취약점 | 페이로드 |
| 1 | Reflected XSS | query 삽입 텍스트 무필터링 | <script>alert(...)</script> |
| 2 | Stored XSS | <script> 제한, onerror 가능 | <img src="x" onerror="alert()"> |
| 3 | DOM-based XSS | location.hash → innerHTML | #1' onerror='alert(1)// |
| 4 | JS 컨텍스트 XSS | onload="startTimer('{{timer}}');" | 3');alert('test |
| 5 | URL 스킴/DOM XSS | next 파라미터 삽입 | ?next=javascript:alert(1) |
| 6 | DOM-based + 스킴 | location.hash → script src | #data:text/plain,alert(1) |
'STUDY > webhacking writeup' 카테고리의 다른 글
| [Dreamhack] simple_sqli (0) | 2025.06.27 |
|---|