Yet another way to invoke functions without parentheses
괄호 없이 JavaScript 함수를 호출하는 또 다른 방법
V8의 Error.prepareStackTrace와 프로토타입 변환을 조합해 괄호 없이 임의의 JavaScript 함수를 만들고 실행하는 방법을 설명합니다. 이 기법은 Error 객체와 배열의 문자열 변환, 단항 + 연산자의 암묵적 형 변환을 차례로 이용합니다.
- 주제
에디터 노트
이 페이로드의 의미는 괄호 없는 호출 자체보다 JavaScript의 여러 변환 규칙을 연결해 함수 생성과 실행을 한 표현식 안에 넣는 데 있습니다. 원문은 V8의 Error.prepareStackTrace와 Function 생성자 인수 형태를 이용하고, 배열의 toString을 바꿔 함수 본문을 공급한 뒤 단항 +로 valueOf를 호출합니다. 각 단계가 구체적으로 제시돼 있어 기법의 작동 원리는 따라갈 수 있습니다. 다만 브라우저 전반에서 쓸 수 있는 문법 기법으로 읽으면 범위를 벗어납니다. 댓글에서 지적했듯 Error.prepareStackTrace는 V8에 의존하며 Firefox에서는 작동하지 않습니다. 또 이 코드는 Object.prototype과 Array.prototype을 덮어쓰므로 주변 코드의 동작에도 영향을 줍니다. 댓글의 TC39 멤버가 언급한 Symbol 기반 호출 방식은 다른 사례를 보태지만, 이 페이로드의 이식성을 넓혀 주지는 않습니다. 실용적인 호출 문법보다는 런타임의 암묵 변환과 전역 프로토타입 변경이 어디까지 이어지는지 보여주는 실험으로 보는 편이 정확합니다.
AI 요약
JavaScript에서 함수 호출 괄호를 쓰지 않고 임의의 함수를 실행하는 방법을 소개합니다. 기법은 Error.prepareStackTrace를 Function 생성자로 바꾸는 데서 시작합니다. V8은 Error 객체의 stack 속성을 읽을 때 prepareStackTrace(error, structuredStackTrace)를 호출합니다. 두 인수의 형태가 Function 생성자와 맞아 함수 생성에 활용됩니다.
배열을 함수 본문으로 바꾸기
structuredStackTrace는 코드 위치 정보가 담긴 배열이라 그대로는 유효한 JavaScript 코드가 아닙니다. 글에서는 Array.prototype.toString을 바꿔 배열을 원하는 코드 문자열로 변환합니다. 예시에서는 Error.prototype.toString을 배열의 toString으로 지정하고 Object.prototype.message에 alert(1337)을 넣습니다. Error.prototype.toString이 name과 message를 조합해 반환하는 문자열에서 Error:는 JavaScript 레이블로 해석되고 alert(1337)이 실행됩니다.
암묵적 변환으로 함수 실행하기
생성한 함수를 실행하려면 x.valueOf에 x.stack을 넣고 마지막에 단항 + 연산자와 x를 둡니다. 단항 +는 객체를 숫자로 바꾸려 하며, 이 과정에서 valueOf를 호출합니다. 글은 toString을 이용한 변환 뒤 문자열을 연결하는 방법도 대안으로 언급합니다. 전체 페이로드는 다음과 같습니다.
Object.prototype.message='alert\\x281337\\x29';Array.prototype.toString=Error.prototype.toString;Error.prepareStackTrace = Function;const x = new Error;x.valueOf=x.stack;+x;
이 기법은 Error.prepareStackTrace가 구현된 V8에 의존합니다. Hacker News 댓글에서도 이 API가 선택 사항이며 Firefox에서는 동작하지 않는다는 지적이 나왔습니다. 댓글 작성자는 Symbol.isConcatSpreadable과 Symbol.iterator처럼 괄호 없이 함수를 호출하는 다른 방법도 있다고 덧붙였습니다.
Hacker News 반응
- @fg137 — 프로토타입 방식은 주류 언어 중에서도 독특합니다. 원리 자체는 꽤 단순하고 의도한 용도에도 잘 맞지만 혼란을 많이 일으키고 CVE의 원인이 되곤 합니다. 암묵적 변환과 valueOf를 함께 쓰면 실제 서비스 코드에서는 악몽입니다.
- @aktenlage — 마지막의 +x는 무슨 역할을 하나요?
- @undecidabot — 단항 더하기 연산자는 표현식을 숫자로 암묵 변환합니다. 원시값이 아닌 객체에서는 valueOf를 호출합니다.
- @CrendKing — 문서에 따르면 Error.prepareStackTrace는 선택 API이며 V8에서만 구현됐으므로 Firefox에서는 작동하지 않습니다.
- @airtnp — 이걸 난독화에 쓸 수 있을까요? 적용한 다음 LLM이 풀 수 있는지 시험해 봐야겠습니다.
- @sharktheone — TC39 멤버입니다. JavaScript에는 괄호 없이 함수를 호출하는 방법이 더 많습니다. 먼저 떠오르는 건 Symbol.isConcatSpreadable과 Symbol.iterator이며 여러 곳에서 작동합니다.
원문: 개인 블로그 / 번역·요약: Trawling