Security/정보보안

XSS game

arsenic-dev 2025. 4. 1. 00:12

경희대학교 중앙동아리 쿠러그의 정보보안 강의를 기반으로 정리한 글입니다.

XSS game

XSS game은 XSS 공격을 통해 다음과 같이 alert()를 띄우는 게임이다.

Level 1: Hello, world of XSS

해당 창에 "hi"라고 입력하면,

다음과 같이 "hi"가 그대로 뜨는 것을 확인할 수 있다.

 

주어진 Target code에서 해당 페이지를 보여주는 부분을 찾아보면,

message = "Sorry, no results were found for <b>" + query + "</b>."

 

해당 코드를 찾을 수 있다.

이 코드를 보면 입력한 대로 query로 들어간다.

 

때문에, 아래와 같이 입력을 넣어주면 alert()를 띄울 수 있다.

<script>alert()</script>

 

Level 2: Persistence is key

Level 1을 풀었던 방식으로 입력하면,

원하는 대로 alert()가 뜨지 않고, 위와 같이 날짜 및 시간이 뜬다.

 

alert()가 뜨지 않는 이는 입력한 구문이 실행되지 않았기 때문이다.

<script></script> 태그 안의 내용은 페이지 로드 시에만 실행된다.

하지만, 댓글만 다는 것으로 페이지가 로드되지 않기에 해당 구문이 실행되지 않는 것이다.

 

때문에, 아래와 같이 입력을 넣어준 후,

생성된 button을 click해주면 alert()를 띄울 수 있다.

 

Level 3: That sinking Feeling...

Image 1, 2, 3를 각각 클릭할 때마다 URL의 마지막 숫자가 Image의 숫자에 맞게 변하는 것을 확인할 수 있다.

 

주어진 Target code에서 해당 페이지를 보여주는 부분을 찾아보면,

html += "<img src='/static/level3/cloud" + num + ".jpg' />";

 

해당 코드를 찾을 수 있다.

이 코드를 보면 num의 값을 조정해서 공격할 수 있음을 알 수 있다.

num 부분에 '> 이거로 기존 img 태그를 닫아버리고 <script>alert()</script><' 를 넣어주면, (문법 맞춰 오류 방지)

alert()를 띄울 수 있다.

 

Level 4: Context matters

입력을 넣어보면,

다음과 같이 뜨는 것을 확인할 수 있다.

 

주어진 Target code에서 해당 페이지를 보여주는 부분을 찾아보면,

<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>

 

해당 코드를 찾을 수 있다.

이 코드를 보면 입력한 대로 {{ timer }} 로 들어간다.

{{ timer }} 부분에 '); 이거로 기존 onload 함수를 닫아버리고 alert(); (' 를 넣어주면, (문법 맞춰 오류 방지)

alert()를 띄울 수 있다.

 

Level 5: Breaking protocol

위 페이지는 초기 페이지에서 'Sign up'을 눌렀을 때 나오는 페이지이다.

 

주어진 Target code에서 해당 페이지를 보여주는 signup.html 코드를 찾아보면,

<a href="{{ next }}">Next >></a>

 

해당 코드를 찾을 수 있다.

이 코드를 보면 next의 값이 html 코드의 {{ next }} 부분에 그대로 삽입되는 것을 확인할 수 있다.

뒤에 나오는 javascript 구문을 실행하게 하는 javascript:를 이용해 위와 같이 URL을 수정하고,

'Next >>'를 누르면 alert()를 띄울 수 있다.

 

Level 6: Follow the rabit

위 페이지에 해당하는 코드를 확인하면,

// 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);
}

 

해당 코드를 찾을 수 있다.

이 코드를 보면 URL의 #뒤에 적힌 url 경로를 통해 로드하는 것을 확인할 수 있다.

마지막 힌트를 이용해,

다음과 같이 url을 로드할 하였는데 실패했다.

 

로드를 실패하는 이유를 찾아보면,

// 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;
}

 

해당 코드를 찾을 수 있다.

이 코드를 보면 URL이 https로 되어있으면 안 된다는 것을 알 수 있다. (프로토콜 생략 시 현재 문서와 같은 프로토콜로 자동 지정)

때문에 https를 Https로 바꿔주면, (프로토콜은 대소문자 구분 안 함)

gadget가 로드된 것을 확인할 수 있다.

이때 callback하는 함수만 foo에서 alert로 바꿔주면 alert()를 띄울 수 있다.