JSFuck
JSFuckはJavaScriptのサブセットとして考案された難解プログラミング言語。[・]・(・)・!・+の6文字のみコードを書く。
JavaScriptの様々な言語仕様を利用することで、非常に冗長なコードになってしまうものの上記6文字だけでJavaScriptの全機能を使用できる[1]。
名前はBrainfuckに由来するが、独自のコンパイラやインタプリタを必要とするBrainfuckとは異なり、JSFuckはあくまでもJavaScriptの言語仕様に基づいているためJavaScriptの処理系(WebブラウザやJavaScriptエンジン)で動作する。
歴史
[編集]2009年7月にHasegawa YosukeがJavaScriptを[]()!+,\"$.:;_{}~=の18文字に変換するWebアプリケーションを作った[2][3]。
2010年1月には、sla.ckers.orgというWebアプリケーションセキュリティサイトの「Obfuscation」フォーラムで非公式の競争が開催され、文字数を当時必要最小限だと思われていた8文字([]()!+,/)に抑える方法が考案された。その後、どうにか , と / を使わないようにできないか模索され[4]、同年3月にはJS-NoAlnumと呼ばれる現在の6文字で表現されるエンコーダーができた[5]。同年11月にハセガワはJSF*ckと呼ばれる6文字で表現されるエンコーダーを完成させた[6][7]。2012年には、Martin Kleppe が"jsfuck"と名前をつけたプロジェクトをGitHub上で公開した[8]。そしてJSFuck.comというサイトでエンコーダーの実装を公開している[9]。
JSFuckはマルウェアをウェブサイトにクロスサイトスクリプティング (XSS)等によって埋め込むことにも使われたことがある[10]。他の潜在的な使用方法としては、難読化がある。よく使われるJavaScriptライブラリであるjQueryも、6文字で完全に置き換えられたことがある[11]。
変換の手順
[編集]JSFuckは非常に冗長である。JavaScriptではalert("Hello World!")としてポップアップを開くであろうコードは21字である。しかし、同じことをJSFuckでしようとすると22948字にもなる。いくつかの文字はJSFuckで表現しようとすると1000文字を超える。この節はどうやって文字を生成することができるのか説明する。
数字
[編集]0は+[]によって作られる。ここで、[]は空の配列であり、+は単項プラスである。ここに型変換が働いて、[]は0とみなされる[12]。
1は+!![] か +!+[]で、論理値true (JSFuckでは!![]か!+[]として表現される)が数字の1に変換されたものである[13]。
2から9については、!をその回数だけ繰り返し、それを+で連結すれば良い。これを踏まえると2は!![]+!![]や!+[]+!+[]と書くことができる。
2桁以上の場合は、1桁ずつ数字として配列に詰めたあと、それを1要素連結すればよい。例として"10"は[1] + [0]と書き換えることができる。前述の0と1の作り方を適用すると、これは[+!+[]]+[+[]]となる。数値を返したい場合は、頭に+をつける(この例では10 = +([+!+[]]+[+[]]))
文字
[編集]JSFuckではいくつかの文字は論理値や"NaN"、"undefined"から添字付き(角括弧の中に数字が入ったもの)で取り出すことになる。他の文字を生成するためにさらなるトリックが存在する。"1e1000"を数字に型変換すると、Infinityとなり、yを簡単に取り出すことができるようになる[14]。下に記したのは最もシンプルにプリミティブな値を作るコードである。
(ただし、幾つかのコンパイラでは!+[]が警告されるため!![]に統一している。)
| 値 | JSFuck |
|---|---|
false |
![]
|
true |
!![]
|
NaN |
+[![]]
|
undefined |
[][[]]
|
Infinity |
+(+!![]+(!![]+[])[!![]+!![]+!![]]+[+!![]]+[+[]]+[+[]]+[+[]])
|
例:"a"の生成
[編集]"a": 文字列"false"から取ることにする。"false"の2文字目は"a"である。これは"false"[1]としてアクセスできる。"false"はfalse+[]から作ることができる。すなわち、falseと空の配列を足すことになる。(false+[])[1]: falseは![]から生成できるので(論理否定を空の配列に適用する)(![]+[])[1]: 1は数字であるので、これを+trueで置き換えると(![]+[])[+true]: そしてfalseは![]でtrueは!![]なので(![]+[])[+!![]]- これで"a"を返す。
JavaScriptで実際に試してみると、alert((![]+[])[+!![]])とalert("a")は同じ出力である[15]。
他の生成
[編集]FunctionのコンストラクタはJavaScriptとして有効なコードを実行することができるので、alert(1)はFunction("alert(1)")()と同じである。Functionのコンストラクタは、[]["at"] (Array.prototype.at) のように標準の関数のconstructorプロパティから取り出すことができる。このことを踏まえると、alert(1)は[]["at"]["constructor"]("alert(1)")()と書き換えることができる。
文字表
[編集]文字を作ることができる一番短いコードを下に記した。他の文字も生成できるがおそらくもっと長くなるだろう。
(ただし、幾つかのコンパイラでは!+[]が警告されるため!![]に統一している。)
| 文字 | JSFuck | 意味[16] |
|---|---|---|
| + | (+(+!![]+(!![]+[])[!![]+!![]+!![]]+[+!![]]+[+[]]+[+[]])+[])[!![]+!![]] |
1e+100 の3文字目
|
| . | (+(+!![]+[+!![]]+(!![]+[])[!![]+!![]+!![]]+[!![]+!![]]+[+[]])+[])[+!![]] |
1.1e+21 の2文字目
|
| 0 | +[] |
空配列を数値に変換した値 |
| 1 | +!![] |
論理値 true を数値に変換した値
|
| 2 | !![]+!![] |
目的の値になるまで論理値 true (=1)を加算した値
|
| 3 | !![]+!![]+!![]
| |
| 4 | !![]+!![]+!![]+!![]
| |
| 5 | !![]+!![]+!![]+!![]+!![]
| |
| 6 | !![]+!![]+!![]+!![]+!![]+!![]
| |
| 7 | !![]+!![]+!![]+!![]+!![]+!![]+!![]
| |
| 8 | !![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]
| |
| 9 | !![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]
| |
| a | (![]+[])[+!![]] |
false の2文字目
|
| c | ([]+[][(![]+[])[+!![]]+(!![]+[])[+[]]])[!![]+!![]+!![]] |
function at() { [native code] } の4文字目
|
| d | ([][[]]+[])[!![]+!![]] |
undefined の3文字目
|
| e | (!![]+[])[!![]+!![]+!![]] |
true の4文字目
|
| f | (![]+[])[+[]] |
false の1文字目
|
| i | ([![]]+[][[]])[+!![]+[+[]]] |
falseundefined の11文字目
|
| I (i) | (+(+!![]+(!![]+[])[!![]+!![]+!![]]+(+!![])+(+[])+(+[])+(+[]))+[])[+[]] |
Infinity の1文字目
|
| l (L) | (![]+[])[!![]+!![]] |
false の3文字目
|
| N | (+[![]]+[])[+[]] |
NaN の1文字目
|
| n | ([][[]]+[])[+!![]] |
undefined の2文字目
|
| o | (!![]+[][(![]+[])[+!![]]+(!![]+[])[+[]]])[+!![]+[+[]]] |
truefunction at() { [native code] } の11文字目
|
| r | (!![]+[])[+!![]] |
true の2文字目
|
| s | (![]+[])[!![]+!![]+!![]] |
false の4文字目
|
| t | (!![]+[])[+[]] |
true の1文字目
|
| u | ([][[]]+[])[+[]] |
undefined の1文字目
|
| y | (+[![]]+[+(+!![]+(!![]+[])[!![]+!![]+!![]]+[+!![]]+[+[]]+[+[]]+[+[]])])[+!![]+[+[]]] |
NaNInfinity の11文字目
|
| ( | ([]+[][(![]+[])[+!![]]+(!![]+[])[+[]]])[+!![]+[]+(+!![])] |
function at() { [native code] } の12文字目
|
| ) | ([]+[][(![]+[])[+!![]]+(!![]+[])[+[]]])[+!![]+[]+(!![]+!![])] |
function at() { [native code] } の13文字目
|
コード表
[編集]組み合わせることができる一番短いコードを下に記した。
これらを組み合わせ、コードを単純化することができるだろう。
| JSFuck | 出力 | 型 |
|---|---|---|
[]
|
[]
|
object |
+[]
|
0
|
number |
![]
|
false
|
boolean |
[[]]
|
[[]]
|
object |
!![]
|
true
|
boolean |
[]+[]
|
""
|
string |
[+[]]
|
[0]
|
object |
[![]]
|
[false]
|
object |
+!![]
|
1
|
number |
[[[]]]
|
[[[]]]
|
object |
[][[]]
|
undefined
|
undefined |
[]+![]
|
"false"
|
string |
[!![]]
|
[true]
|
object |
+[]+[]
|
"0"
|
string |
+[![]]
|
NaN
|
number |
[[]+[]]
|
[""]
|
object |
[[+[]]]
|
[[0]]
|
object |
[[![]]]
|
[[false]]
|
object |
[]+!![]
|
"true"
|
string |
[[[[]]]]
|
[[[[]]]]
|
object |
[+!![]]
|
[1]
|
object |
[[][[]]]
|
[undefined]
|
object |
[[]+![]]
|
["false"]
|
object |
[[!![]]]
|
[[true]]
|
object |
[+[]+[]]
|
["0"]
|
object |
[+[![]]]
|
[NaN]
|
object |
+!![]+[]
|
"1"
|
string |
セキュリティ
[編集]JSFuckのような難読化技術は、「通常の」JavaScriptに備わる"クラッキング防止システム"が存在しない[17]ため、eBayのオークションのページにJSFuckで書かれたスクリプトを埋め込むことができた[18]。
脚注
[編集]- ^ Jane Bailey/The Daily WTF: "Bidding on Security". http://thedailywtf.com/articles/bidding-on-security
- ^ Hasegawa, Yosuke (2009年7月10日). “jjencode - Encode any JavaScript program using only symbols”. utf-8.jp. 2009年7月15日時点のオリジナルよりアーカイブ。2017年10月25日閲覧。
- ^ Hasegawa, Yosuke (2009年7月). “UTF-8.jp [2009-07-28]”. utf-8.jp. 2009年7月28日時点のオリジナルよりアーカイブ。2017年10月25日閲覧。
- ^ “Yet Another Useless Contest (but fun!) Less chars needed to run arbitrary JS code”. sla.ckers.org (2010年1月14日). 2011年3月1日時点のオリジナルよりアーカイブ。2017年10月25日閲覧。
- ^ “js-noalnum_com.php”. discogscounter.getfreehosting.co.uk. 2010年3月1日時点のオリジナルよりアーカイブ。2017年10月25日閲覧。
- ^ Hasegawa, Yosuke (2010年11月). “JSF*ck - []()!+”. utf-8.jp. 2010年12月1日時点のオリジナルよりアーカイブ。2017年10月25日閲覧。
- ^ Hasegawa, Yosuke (2010年11月). “UTF-8.jp [2010-11-30]”. utf-8.jp. 2010年11月30日時点のオリジナルよりアーカイブ。2017年10月25日閲覧。
- ^ Kleppe, Martin (2012年7月16日). “Commits · aemkei/jsfuck”. github.com. 2017年10月25日閲覧。
- ^ Kleppe, Martin (2012年9月). “Site report for www.jsfuck.com”. toolbar.netcraft.com. 2017年10月25日閲覧。
- ^ https://arstechnica.com/security/2016/02/ebay-has-no-plans-to-fix-severe-bug-that-allows-malware-distribution/ Ars Technica: Ebay has no plans to fix severe bug that allows malware distribution
- ^ https://github.com/fasttime/jquery-screwed jQuery JavaScript library made of only six different characters: ! ( ) + [ ]
- ^ 単項和演算子 + を付けられた項は暗黙的に数値に型変換される。配列を数値に変換するとその文字列の表す数値が得られる(参照:“Arithmetic operators (Web technology for developers)”. 2019年10月12日閲覧。)が、[](空配列)の場合得られる値 +[] はゼロである。
- ^ [](空配列)は論理値 true を持つと定義されている(すなわちtruthyである(参照:“Truthy (Web technology for developers)”. 2019年10月12日閲覧。))ため、これに二重not演算した !![] はtrueであり、それを数値に型変換した +!![] は1になる。
- ^ http://patriciopalladino.com/blog/2012/08/09/non-alphanumeric-javascript.html "Brainfuck Beware: JavaScript is after you!"
- ^ Adapted from: https://esolangs.org/wiki/JSFuck
- ^ インデックスは0始まりであるため、何文字目か指定する添字は1引いた値になる。
- ^ Ré Medina, Matías A. (2012-09). Bypassing WAFs with non-alphanumeric XSS. Retrieved from http://blog.infobytesec.com/2012/09/bypassing-wafs-with-non-alphanumeric-xss.html.
- ^ “eBay has no plans to fix "severe" bug that allows malware distribution [Updated]”. Ars Technica (2016年2月4日). 2018年9月8日閲覧。
外部リンク
[編集]- JSFuck - Write any JavaScript with 6 Characters: []()!+ – JavaScriptをJSFuckに変換するWebアプリケーション
- JavaScript code of the aforementioned converter
- JScrewIt - JavaScriptをJSFuckに変換するツール