본문 바로가기

보안

JavaScript의 독특한 난독화 기법인 JSFuck에 대해 알아보자

반응형

JSFuck은 난해한 프로그래밍 스타일의 하나로, 오로지 ( ) [ ] + ! 라는 6가지의 문자만 써서 JavaScript의 코드를 구현한다. Martin Kleppe가 개발하였으며 자바스크립트를 해석하는 어떠한 웹 브라우저나 엔진에서라도 실행할 수 있다.

 

JSFuck - Write any JavaScript with 6 Characters: []()!+

 

jsfuck.com

그렇다면, 해당 문자를 이용해서 함수 실행을 어떻게 할지 단계별로 살펴보자.

1단계: 기본 Boolean값 만들기

  • JavaScript에서 빈 배열 []은 truthy이지만, 부정연산자 !를 쓰면 false가 된다.
    • []        // 빈 배열 (truthy)
    • ![]       // false (배열을 부정)
    • !![]      // true (이중 부정)

2단계: 숫자 생성

  • + 연산자는 값을 숫자로 강제 변환한다. 빈 배열은 0, false도 0이 된다.
    • +[]       // 0 (빈 배열을 숫자로 변환)
    • +![]      // 1 (false를 숫자로 변환하면 0, 이를 부정하면 1)
    • !+[]+!+[] // 2 (1 + 1)

3단계: 기본 문자열 생성

  • 배열과 다른 값을 +로 연산하면 문자열 연결이 된다.
    • ![] + []     // "false" (false + 빈배열 = 문자열 "false")
    • !![] + []    // "true" 
    • [][+[]]      // undefined (배열의 0번째 요소, 없으므로 undefined)
    • [][+[]] + [] // "undefined" (undefined를 문자열로 변환)

4단계: 개별 문자 추출

  • 문자열에서 [인덱스]로 문자를 뽑아낸다.
    • (![] + [])[+[]]      // "false"[0] = "f"
    • (![] + [])[+!+[]]    // "false"[1] = "a" 
    • (![] + [])[!+[]+!+[]] // "false"[2] = "l"
    • (!![] + [])[+[]]     // "true"[0] = "t"
    • (!![] + [])[+!+[]]   // "true"[1] = "r"
    • 여기서 +[]는 0, +!+[]는 1, !+[]+!+[]는 2입니다.

5단계: 더 많은 문자 얻기

  • 객체 문자열 활용
    • {} + []              // "[object Object]"
    • ({} + [])[+!+[]]     // "o" (object의 o)
    • ({} + [])[!+[]+!+[]] // "b" (object의 b)

6단계: 함수 호출

  • 브라우저에서 alert(1)을 호출하는 JSFuck 문자열:
    • [][(![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((!![]+[])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+!+[]]+(+[![]]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+(+(!+[]+!+[]+!+[]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[!+[]+!+[]])+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]])()((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[+!+[]+[+!+[]]]+[+!+[]]+([]+[]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[!+[]+!+[]]])
  • 어떤식으로 구현한 것일까?
    • 위 문자열은, [][at][constructor]("alert(1)")() 문자열을 JSFuck Encoding한 것이다.
  • 그렇다면 구문별로 그 의미를 분석해보자.
    • [][at]은 Array.prototype.at 함수이다. 그 함수가 어떤 함수인지는 중요치 않다.
    • 여기에 constructor를 더한다는 것은, Array.prototype.at의 constructor(생성자)를 의미하며, 모든 함수의 constructor는 Function이다. 그리고 Function constructor는 문자열로 함수를 생성한다.
    • 이를 통해 [][at][constructor]("alert(1)")() 의 변환 과정을 보면 아래와 같이 4단계로 설며할 수 있다.
1. 일반 문자열 [][at][constructor]("alert(1)")()
2. 실제 의미 Array.prototype.at.constructor("alert(1)")()
3. 핵심 동작 Function("alert(1)")()
4. 최종 실행 alert(1)
  • 결국, at이라는 함수는 단순히 Fucntion을 불러오기 위한 매개체일 뿐 다른함수가 사용되어도 동일하다.

 

* 핵심 원리 정리

  • 타입 강제변환: JavaScript의 자동 타입 변환 활용
  • 문자열 인덱싱: "문자열"[숫자]로 개별 문자 추출
  • 기본 문자열들: "false", "true", "undefined", "[object Object]", "NaN" 등
  • 조합: 기존 문자들을 +로 연결해서 새로운 문자열 생성
  • 동적 프로퍼티 접근: obj[문자열]로 메서드나 프로퍼티 접근
  • 이런 식으로 6개 문자([]()!+)만으로도 JavaScript의 모든 기능에 접근할 수 있습니다. 하지만 코드가 기하급수적으로 길어져서 실용성은 전혀 없다.

 

반응형