[XSS game] Level 4-6

<Level 4>


사용자가 제공한 데이터의 모든 bit는 정확하게 escape 되어야 한다 그것이 나타나게 될 페이지의 context에 맞게? 흠 무슨 소린지 잘 모르겠지만 일단 해보자.

타이머를 만들어주나보다. 눌러보자.

3초가 지나니 타이머가 시간이 다 됐다고 알려준다!

저기에 어떻게 입력을 해야할 것 같은데,, 코드를 좀 보자.

21번째 줄의 onload 부분에서 startTimer를 호출하고 우리가 입력한 timer 값을 받아서 사용하고 있다.

해당 값은 index.html를 보면 get으로 받아오는 것을 확인할 수 있다.

따라서 우리는 저 onload 부분을 잘 이용해야 한다.

만약 입력에 그냥 <img src=x onerror=alert(1)>를 입력하면 어떻게 될까?

url의 timer에는 다음과 같이 반영된다. 따라서 코드를 보고 parsing 하는 부분을 제대로 이해해야 할 것 같다.

이 문제 역시 이전 문제와 같이 img src를 전체 입력해줄 필요는 없다. timer 부분에 어떻게 입력되는지만 잘 파악하여 넣어주면 된다.

onload부분을 보면 startTimer(’{{ timer }}’);로 되어 있다. 그리고 뒤에서 />로 닫고 있으니, 뒷부분 바로 전에 alert(’1’);이 나오도록 해야한다. 그런데 alert(1);의 뒤 세 문자인 ‘);은 startTimer에서 알아서 제공해준다! 따라서 우리 입력의 끝부분은 alert(’1 이면 된다. 그렇다면 앞부분은 어떻게 해야할까? (’로 시작되므로 적당한 시간 ?을 넣고, ‘)로 따옴표와 괄호 닫고 ;을 추가해주면 된다. 그러면 타이머가 실행되고 alert까지 바로 실행된다!

따라서 payload는 1’);alert(’1 이 된다!

이렇게 보냈는데 계속 times up만 나오고 alert는 실행이 안되었다.. 그래서 hint를 보았더니,

HTML-decode에 대해 모르고 있었다.

;부터 받지 못하고 있었기에 ;를 인코딩하여 %3B로 보내주었다.( 1’)%3Balert(’1 )

그런데 또 안되었다! ㅠㅠ 그래서 그냥 alert말고 onerror=alert(’1로 바꿔서 보내줘보았다.

문제 해결~

결국 <img src=”/static/loading.gif” onload=”startTimer(‘1’);onerror=alert(‘1’);” />로 되어서 alert가 수행된 것이다!

<Level 5>


DOM에 새로운 것을 inject 하지 않고도 공격할 수 있다? 제목이 breaking protocol인 것으로 보아 프로토콜과 관련해서 뭔가를 하는 것 같기도 하고,, 일단 보자.

첫 페이지이다.

Sign up을 누르니 이메일을 입력하라고 한다.

이메일을 입력하니 위의 화면이 나오다가 다시 처음 화면으로 돌아간다.

도통 뭔지 모르겠다,, 코드를 보자.

signup을 보라는 힌트의 도움으로 봤는데 11번째 줄에 이상한 말이 적혀있다.

<!– We’re ignoring the email, but the poor user will never know! -→ email 입력하는 건 뻥카인 거 같다 ^^;; 마지막 힌트에서 어떠한 페이지를 줬다.

The ‘javascript’ resource identifier scheme

하이퍼링크에서 저렇게 ‘javascript:함수’로 보내면 함수를 실행해준다고 한다! 그렇다면 우리가 이용할 수 있는 취약한 부분은 어디일까? 위에 캡쳐해놓았는데, 바로 next 부분이다.

근데 next에 입력을 받지는 않는 거 같은데 어떻게 전달해야할까?

아까 이메일을 입력하고 나면 나오던 페이지였다. 해당 페이지를 보면, next가 window.location으로 보내지는 것을 확인할 수 있다.

signup 페이지로 넘어갔을 때 next에 전달되는 값이다. confirm으로 되어있음을 확인할 수 있다.

저 부분에 javascript:alert(1)을 입력하면 위의 글에서 말한 것처럼 next를 눌렀을 때 alert 함수를 실행하게 될 것이다!

url 부분에 입력해 주었다. 그리고 next를 누르면..

문제 해결~~

<Level 6>


드디어 마지막 문제,, 힘내자!

복잡한 웹 응용 프로그램은 때때로 URL 매개 변수의 값이나 location.hash의 일부를 기반으로 자바스크립트 라이브러리를 동적으로 로드하는 기능을 가지고 있다고 한다.

스크립트나 잠재적으로 위험한 데이터를 로드할 때 사용자 입력이 URL에 영향을 미치도록 허용하면 심각한 취약성이 발생하는 경우가 많다고 한다.

alert를 실행하는 외부 파일을 앱이 찾도록 해보자.

일단 화면이다. gadget을 로드할 수 없다고 한다!

코드를 보자.

<!doctype html>
<html>
  <head>
    <!-- Internal game scripts/styles, mostly boring stuff -->
    <script src="/static/game-frame.js"></script>
    <link rel="stylesheet" href="/static/game-frame-styles.css" />
 
    <script>
    function setInnerText(element, value) {
      if (element.innerText) {
        element.innerText = value;
      } else {
        element.textContent = value;
      }
    }
 
    function includeGadget(url) {
      var scriptEl = document.createElement('script');
 
      // This will totally prevent us from loading evil URLs!
      if (url.match(/^https?:\\/\\//)) {
        setInnerText(document.getElementById("log"),
          "Sorry, cannot load a URL containing \\"http\\".");
        return;
      }
 
      // Load this awesome gadget
      scriptEl.src = url;
 
      // Show log messages
      scriptEl.onload = function() { 
        setInnerText(document.getElementById("log"),  
          "Loaded gadget from " + url);
      }
      scriptEl.onerror = function() { 
        setInnerText(document.getElementById("log"),  
          "Couldn't load gadget from " + url);
      }
 
      document.head.appendChild(scriptEl);
    }
 
    // Take the value after # and use it as the gadget filename.
    function getGadgetName() { 
      return window.location.hash.substr(1) || "/static/gadget.js";
    }
 
    includeGadget(getGadgetName());
 
    // Extra code so that we can communicate with the parent page
    window.addEventListener("message", function(event){
      if (event.source == parent) {
        includeGadget(getGadgetName());
      }
    }, false);
 
    </script>
  </head>
 
  <body id="level6">
    <img src="/static/logos/level6.png">
    <img id="cube" src="/static/level6_cube.png">
    <div id="log">Loading gadget...</div>
  </body>
</html>

음.. 코드만 봐서는 모르겠어서 힌트를 좀 참고했다.

1. See how the value of the location fragment (after #) influences the URL of the loaded script.

getGadgetName을 보자. window.location.hash.substr(1)를 이용해서 받아오고 있다. 이것은 현재 URL의 해시값을 가져오는 것으로, 문자열의 첫번째 문자를 제외하고 받아오고 있다.

after #라고 힌트에서 알려준 것으로 보아, #을 제외한 해시값을 받아오는 것 같다. 그리고 이것을 gadget의 filename으로 이용한다고 한다.

2. Is the security check on the gadget URL really foolproof?

includeGadget 함수 부분을 보면, 주석으로 된 한 문장이 있다.

// This will totally prevent us from loading evil URLs!

악의적인 url로부터 완벽하게 막고있다고 한다. 그런데 정말 믿을 수 있냐고 힌트에서 다시 물어보는 것은, 문제가 있으니까 그러는 것이 아닐까..? 일단 의심한 채로 넘어가 보자.

3. Sometimes when I’m frustrated, I feel like screaming

솔직히 이건 잘 모르겠다. 소리 지르는 게 뭐 어쩌라고,,

4. If you can’t easily host your own evil JS file, see if google.com/jsapi?callback=foo will help you here.

저 사이트에 가봐야겠다.

억 이게 다 뭐람. 글이 정말 많다,, 그래도 이걸 어떻게 활용할 지 잘 생각해보자.

우리에겐 chatgpt가 있지 않은가? ㅎㅎ 라이브러리를 동적으로 로드할 수 있다고 한다. 위의 긴 글 역시 foo함수를 callback으로 호출한 것이다.

그렇다면 혹시 저 callback= 뒤에 alert을 넣는다면? alert 함수를 실행시켜 주지 않을까?

URL을 찾을 수 없다고 한다. 그도 그럴 것이, https://www. 이 붙은 채로 자동으로 검색되므로, 위의 저부분만 넣으면 url을 찾을 수 없다.

또 찾을 수 없다고 한다… 그러나 이전 문제를 잘 생각해보면 이 부분은 해결할 수 있었다.

항상 url이 https://xss-game.appspot.com/level?/frame 으로 시작했음을 잊지 말아야 한다. 따라서 이것을 앞에 붙여주고, 뒤에 붙인 url역시 검사를 해야 한다.

내가 입력한 url이 getGadgetName의 인자로 들어가서 “Load Gadget From” 부분에서 내 url을 불러내는 것을 확인할 수 있다. 근데 여기서 문제는, https로 시작하는 url을 무시하는 것이다!

하지만 괜찮다. 힌트 2번의 진가가 바로 여기서 발휘된다. https를 막음으로써 악의적은 공격은 다 막았다고 생각했지만, 사실 HTTPS를 막지 않았다! 따라서 https 대신 HTTPS를 입력하면 우리가 원하는 대로 url을 전송할 수 있다.

그리고 getGadgetName에서 맨 앞의 #을 제외하고 가져온다고 했으니, #HTTPS로 시작하도록 하면 된다.

결국 payload는 다음과 같다.

https://xss-game.appspot.com/level6/frame#HTTPS://www.gstatic.com/charts/loader.js?callback=alert

깔끔하게 해결!

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤