0. 문제
function escape(text) {
var i = 0;
window.the_easy_but_expensive_way_out = function() { alert(i++) };
// "A JSON text can be safely passed into JavaScript's eval() function
// (which compiles and executes a string) if all the characters not
// enclosed in strings are in the set of characters that form JSON
// tokens."
if (!(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
text.replace(/"(\\.|[^"\\])*"/g, '')))) {
try {
var val = eval('(' + text + ')');
console.log('' + val);
} catch (_) {
console.log('Crashed: '+_);
}
} else {
console.log('Rejected.');
}
}
1. 풀이
해당 문제에서의 필터링은 text.replace(/"(\\.|[^"\\])*"/g, '')을 검사하기 때문에 "" 안에 있는 문자열은 검사에서 제외된다.
이것을 이용해 alert(1)을 일으키면 되는데
{}는 Object를 만드는 데 사용한다.
Object의 기본 함수로 valueOf 이라는 함수가 존재하는데 이 함수의 리턴값을 해당 Object가 연산을 할때 사용하게 된다.
따라서 valueOf 함수에 the_easy_but_expensive_way_out 함수를 넣어줘서 숫자와 연산을 진행하면 된다.
(Object의 기본 함수중 toString 함수에 the_easy_but_expensive_way_out 함수를 넣어주고 문자열과 연산해도 괜찮음.)
아래는 최종 payload이다.
0+{"valueOf":self["the_easy_but_expensive_way_out"]}+{"valueOf":self["the_easy_but_expensive_way_out"]}
self는 window의 함수를 가져오는데에 사용된다.
self["the_easy_but_expensive_way_out"]는 self.the_easy_but_expensive_way_out를 의미한다.
그렇게 {"valueOf":self["the_easy_but_expensive_way_out"]} 라는 이 오브젝트의 연산 값을 the_easy_but_expensive_way_out()의 리턴값으로 정해주는 것이다.
그러면 숫자와의 연산을 진행할때 alert(i++)이 실행되게 되고 첫 번째 연산때 alert(0)이 실행되고
두 번째 연산때 alert(1)이 연산되어서 문제를 풀 수 있다.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
글을 쓰다가 101글자짜리 payload를 찾아냈다.
굳이 0이랑 연산을 안하고 오브젝트끼리 연산을 진행하게 해도 상관없으므로 '0+'를 빼도 alert(1)이 정상 실행된다.
최종 payload:
{"valueOf":self["the_easy_but_expensive_way_out"]}-{"valueOf":self["the_easy_but_expensive_way_out"]}
여기서 self["alert"]를 넣으면 안되는 이유는 alert()에는 인자가 필요하기 때문에 안되는 것 같다.
라고 생각했지만
....
사용자 정의함수만 가능한건가..?
1. alert(n)은 인자를 받기 때문에 valueOf에 들어갈 수 없다.
2. alert(n)은 기본 내장 함수이기 때문에 valueOf에 들어갈 수 없다.
둘중에 하나 같은데.. 찾아보고 다시 포스팅 해야겠다.
[Reference]
https://stackoverflow.com/questions/35949554/invoking-a-function-without-parentheses
Invoking a function without parentheses
I was told today that it's possible to invoke a function without parentheses. The only ways I could think of was using functions like apply or call. f.apply(this); f.call(this); But these require
stackoverflow.com