Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Alternative way to get "C" letter in jsfuck

Currently jsfuck use following code to get "C" character

console.log(
    Function("return escape")()(("")["italics"]())[2],
)
   
console.log(  // after expansion
    []["flat"]["constructor"]("return escape")()(([]+[])["italics"]())[!![]+!![]]
)

console.log(  // after final strings expansion we get pure jsfuck code
    [][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]][[]+([]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[!![]+!![]+!![]]+(!![]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[[]+(+!![])+(+[])]+([]+[][[]])[+!![]]+([]+![])[!![]+!![]+!![]]+([]+!![])[+![]]+([]+!![])[+!![]]+([]+!![])[!![]+!![]]+([]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[!![]+!![]+!![]]+([]+!![])[+![]]+(!![]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[[]+(+!![])+(+[])]+([]+!![])[+!![]]]([]+([]+!![])[+!![]]+([]+!![])[!![]+!![]+!![]]+([]+!![])[+![]]+([]+!![])[!![]+!![]]+([]+!![])[+!![]]+([]+[][[]])[+!![]]+(+[![]]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[+([]+(+!![])+(+!![]))]+([]+!![])[!![]+!![]+!![]]+([]+![])[!![]+!![]+!![]]+([]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[!![]+!![]+!![]]+([]+![])[+!![]]+(+([]+(!![]+!![])+(!![]+!![]+!![]+!![]+!![])))[[]+([]+!![])[+![]]+(!![]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[[]+(+!![])+(+[])]+([]+[])[[]+([]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[!![]+!![]+!![]]+(!![]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[[]+(+!![])+(+[])]+([]+[][[]])[+!![]]+([]+![])[!![]+!![]+!![]]+([]+!![])[+![]]+([]+!![])[+!![]]+([]+!![])[!![]+!![]]+([]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[!![]+!![]+!![]]+([]+!![])[+![]]+(!![]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[[]+(+!![])+(+[])]+([]+!![])[+!![]]][[]+([]+[][[]])[+!![]]+([]+![])[+!![]]+((+[])[[]+([]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[!![]+!![]+!![]]+(!![]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[[]+(+!![])+(+[])]+([]+[][[]])[+!![]]+([]+![])[!![]+!![]+!![]]+([]+!![])[+![]]+([]+!![])[+!![]]+([]+!![])[!![]+!![]]+([]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[!![]+!![]+!![]]+([]+!![])[+![]]+(!![]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[[]+(+!![])+(+[])]+([]+!![])[+!![]]]+[])[[]+(+!![])+(+!![])]+([]+!![])[!![]+!![]+!![]]]](+([]+(!![]+!![]+!![])+(+[])))+([]+!![])[!![]+!![]+!![]])()(([]+[])[[]+([]+[][[]])[!![]+!![]+!![]+!![]+!![]]+([]+!![])[+![]]+([]+![])[+!![]]+([]+![])[!![]+!![]]+([]+[][[]])[!![]+!![]+!![]+!![]+!![]]+([]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[!![]+!![]+!![]]+([]+![])[!![]+!![]+!![]]]())[!![]+!![]]
)

But this method use deprecated function "".italics (info here). I develop little tool and try to find some alternative based on btoa but I saddly discovered that this is not supported by node.js (online)

console.log(
  Function("return btoa")()("t.")[1]
)

console.log( // after expansion
  []["flat"]["constructor"]("return btoa")()("t.")[+!![]]
)

console.log( // after full expansion
  [][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]][[]+([]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[!![]+!![]+!![]]+(!![]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[[]+(+!![])+(+[])]+([]+[][[]])[+!![]]+([]+![])[!![]+!![]+!![]]+([]+!![])[+![]]+([]+!![])[+!![]]+([]+!![])[!![]+!![]]+([]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[!![]+!![]+!![]]+([]+!![])[+![]]+(!![]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[[]+(+!![])+(+[])]+([]+!![])[+!![]]]([]+([]+!![])[+!![]]+([]+!![])[!![]+!![]+!![]]+([]+!![])[+![]]+([]+!![])[!![]+!![]]+([]+!![])[+!![]]+([]+[][[]])[+!![]]+(+[![]]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[+([]+(+!![])+(+!![]))]+([][[]+([]+!![])[!![]+!![]+!![]]+([]+[][[]])[+!![]]+([]+!![])[+![]]+([]+!![])[+!![]]+([]+[][[]])[!![]+!![]+!![]+!![]+!![]]+([]+!![])[!![]+!![]+!![]]+([]+![])[!![]+!![]+!![]]]()+[])[!![]+!![]]+([]+!![])[+![]]+(!![]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[[]+(+!![])+(+[])]+([]+![])[+!![]])()([]+([]+!![])[+![]]+(+(+!+[]+[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+[!+[]+!+[]]+[+[]])+[])[+!+[]])[+!![]]
)

Is there a way (working on current versions of chrome, safari, firefox and node.js) to get character "C" using jsfuck but without using deprecated methods?

like image 768
Kamil Kiełczewski Avatar asked Aug 31 '20 15:08

Kamil Kiełczewski


1 Answers

The fact that escape is semi-deprecated kept bothering me, so I took another stab at it. Let's rebuild JSFuck from scratch.

Level 0

You can get the following values as primitives:

false           ![]
true            !![]
undefined       [][[]]
NaN             +[![]]
""              []+[]
0               +[]
1               +!+[]
2               +!+[]+!+[]
3               +!+[]+!+[]+!+[]
4               +!+[]+!+[]+!+[]+!+[]
5               +!+[]+!+[]+!+[]+!+[]+!+[]
6               +!+[]+!+[]+!+[]+!+[]+!+[]+!+[]
7               +!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]
8               +!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]
9               +!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]

Level 1

With the above values and the fact that value+[] converts to string, we can set up the following character substitution rules:

"0"             0+[]
"1"             1+[]
"2"             2+[]
"3"             3+[]
"4"             4+[]
"5"             5+[]
"6"             6+[]
"7"             7+[]
"8"             8+[]
"9"             9+[]
"a"             (false+[])[1]
"d"             (undefined+[])[2]
"e"             (true+[])[3]
"f"             (false+[])[0]
"i"             ([false]+undefined)[1+[0]]
"l"             (false+[])[2]
"n"             (undefined+[])[1]
"r"             (true+[])[1]
"s"             (false+[])[3]
"t"             (true+[])[0]
"u"             (undefined+[])[0]
"N"             (NaN+[])[0]

Level 2

With the above characters, we can construct these four strings:

"11e100"        +!+[]+[+!+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+[+!+[]]+[+[]]+[+[]]
"1e1000"        +!+[]+(!+[]+[])[!+[]+!+[]+!+[]]+[+!+[]]+[+[]]+[+[]]+[+[]]
"flat"          (![]+[])[+[]]+(![]+[])[+!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]
"entries"       (true+[])[3]+(undefined+[])[1]+(true+[])[0]+(true+[])[1]+([false]+undefined)[1+[0]]+(true+[])[3]+(false+[])[3]

With which we can get three more values:

1.1e+101                +("11e100")
Infinity                +("1e1000")
Array Iterator          []["entries"]()
Array.prototype.flat    []["flat"]

The last one is particularly useful, because when converted to a string, it yields this:

"function flat() {\n    [native code]\n}"

Or this:

"function flat() { [native code] }"

This is a bit wonky to work with, but the characters up to and including the { are always the same, as is the last character.

Array Iterator will convert to something more stable:

"[object Array Iterator]"

This gives us more characters to work with:

" "             ([false]+[]["flat"])[2+[0]]
"("             ([]+[]["flat"])[1+[3]]
")"             ([]+[]["flat"])[1+[4]]
"+"             (+("11e100")+[])[4]
"."             (+("11e100")+[])[1]
"["             ([]+[]["entries"]())[0]
"]"             ([]+[]["entries"]())[2+[2]]
"{"             ([true]+[]["flat"])[2+[0]]
"c"             ([]["flat"]+[])[3]
"j"             ([]+[]["entries"]())[3]
"o"             ([true]+[]["flat"])[1+[0]]
"y"             (true+[Infinity])[1+[1]]
"A"             ([NaN]+([]+[]["entries"]()))[1+[1]]
"I"             (Infinity+[])[0]

Level 3

Combining level 1 and 2 characters and values, we can now build three new strings:

".0000001"      (+("11e100")+[])[1]+[0]+[0]+[0]+[0]+[0]+[0]+[1]
"constructor"   ([]["flat"]+[])[3]+([true]+[]["flat"])[1+[0]]+(undefined+[])[1]+(false+[])[3]+(true+[])[0]+(true+[])[1]+(undefined+[])[0]+([]["flat"]+[])[3]+(true+[])[0]+([true]+[]["flat"])[1+[0]]+(true+[])[1]

And this gives us access to a bunch of further values:

1e-7            +(".0000001")
Boolean         (![])["constructor"]
Number          (+[])["constructor"]
String          ([]+[])["constructor"]
Function        []["flat"]["constructor"]

By converting to strings, we get yet more characters:

"-"             (+(".0000001")+[])[2]
"b"             ([]+(+[])["constructor"])[1+[2]]
"g"             (false+[0]+([]+[])["constructor"])[2+[0]]
"m"             ([]+(+[])["constructor"])[1+[1]]
"B"             ([NaN]+(![])["constructor"])[1+[2]]
"F"             ([NaN]+[]["flat"]["constructor"])[1+[2]]
"S"             ([NaN]+([]+[])["constructor"])[1+[2]]

Level 4

Given the uppercase S, we could now build the string "toString manually. However, if we first build the string "name", we can achieve an overall shorter code:

"name"         (undefined+[])[1]+(false+[])[1]+([]+(+[])["constructor"])[1+[1]]+(true+[])[3]
"toString"     (true+[])[0]+([true]+[]["flat"])[1+[0]]+([]+[])["constructor"]["name"]

And with that we can call Number.toString(), giving us all remaining lowercase letters:

"h"             (+(1+[0]+[1]))["toString"](2+[1])[1]
"k"             (+(2+[0]))["toString"](2+[1])
"p"             (+(2+[1]+[1]))["toString"](3+[1])[1]
"q"             (+(2+[1]+[2]))["toString"](3+[1])[1]
"v"             (+(3+[1]))["toString"](3+[2])
"w"             (+(3+[2]))["toString"](3+[3])
"x"             (+(1+[0]+[1]))["toString"](3+[4])[1]
"z"             (+(3+[5]))["toString"](3+[6])

At the same time, we can construct two more strings:

"slice"         (false+[])[3]+(false+[])[2]+([false]+undefined)[1+[0]]+([]["flat"]+[])[3]+(true+[])[3]
"-1"            (+(".0000001")+[])[2]+[+!+[]]

And that gives us one last character that we need for the next level:

"}"             ([true]+[]["flat"])["slice"]("-1")

Level 5

At this point, there's one primitive we obtained that we haven't used yet: using Function as an eval primitive:

[]["flat"]["constructor"](...)()

Since we have all lowercase letter now as well as space, +, ., [, ], { and }, we can build:

"try{String().normalize(false)}catch(f){return f}"

By means of:

(true+[])[0]+(true+[])[1]+(true+[Infinity])[1+[1]]+([true]+[]["flat"])[2+[0]]+([]+[])["constructor"]["name"]+([]+[]["flat"])[1+[3]]+([]+[]["flat"])[1+[4]]+(+("11e100")+[])[1]+(undefined+[])[1]+([true]+[]["flat"])[1+[0]]+(true+[])[1]+([]+(+[])["constructor"])[1+[1]]+(false+[])[1]+(false+[])[2]+([false]+undefined)[1+[0]]+(+(3+[5]))["toString"](3+[6])+(true+[])[3]+([]+[]["flat"])[1+[3]]+![]+([]+[]["flat"])[1+[4]]+([true]+[]["flat"])["slice"]("-1")+([]["flat"]+[])[3]+(false+[])[1]+(true+[])[0]+([]["flat"]+[])[3]+(+(1+[0]+[1]))["toString"](2+[1])[1]+([]+[]["flat"])[1+[3]]+(false+[])[0]+([]+[]["flat"])[1+[4]]+([true]+[]["flat"])[2+[0]]+(true+[])[1]+(true+[])[3]+(true+[])[0]+(undefined+[])[0]+(true+[])[1]+(undefined+[])[1]+([false]+[]["flat"])[2+[0]]+(false+[])[0]+([true]+[]["flat"])["slice"]("-1")

Calling String.prototype.normalize() with a value that isn't a valid Unicode Normalization Form will throw a RangeError, which we catch and return to the caller. We thus have:

RangeError      []["flat"]["constructor"]("try{String().normalize(false)}catch(f){return f}")()

Note that the above is an instance - we'd have to use ["constructor"] to get the function/constructor, but we can just convert it to string as-is, giving us two more uppercase letters:

"E"             ([false]+[]["flat"]["constructor"]("try{String().normalize(false)}catch(f){return f}")())[1+[0]]
"R"             ([]+[]["flat"]["constructor"]("try{String().normalize(false)}catch(f){return f}")())[0]

Level 6

With two more characters unlocked, we can now construct this string:

"return RegExp" (true+[])[1]+(true+[])[3]+(true+[])[0]+(undefined+[])[0]+(true+[])[1]+(undefined+[])[1]+([false]+[]["flat"])[2+[0]]+([]+[]["flat"]["constructor"]("try{String().normalize(false)}catch(f){return f}")())[0]+(true+[])[3]+(false+[0]+([]+[])["constructor"])[2+[0]]+([false]+[]["flat"]["constructor"]("try{String().normalize(false)}catch(f){return f}")())[1+[0]]+(+(1+[0]+[1]))["toString"](3+[4])[1]+(+(2+[1]+[1]))["toString"](3+[1])[1]

And that gives us a new value/function:

RegExp          []["flat"]["constructor"]("return RegExp")()

When invoked with no arguments and converting the resulting RegExp to string, we get:

"/(?:)/"        []+[]["flat"]["constructor"]("return RegExp")()()

So we have a bunch of new special characters:

"/"             ([]+[]["flat"]["constructor"]("return RegExp")()())[0]
":"             ([]+[]["flat"]["constructor"]("return RegExp")()())[3]
"?"             ([]+[]["flat"]["constructor"]("return RegExp")()())[2]

Level 7

Now we feed one of those characters back into the regex to get a new string:

"/\\//"         []+RegExp("/")

This gives us access to a single new character:

"\\"            ([]+RegExp("/"))[1]

Level 8

Let's build a new string:

"try{Function([]+[[]].concat([[]]))()}catch(f){return f}"

By:

(true+[])[0]+(true+[])[1]+(true+[Infinity])[1+[1]]+([true]+[]["flat"])[2+[0]]+[]["flat"]["constructor"]["name"]+([]+[]["flat"])[1+[3]]+([]+[]["entries"]())[0]+([]+[]["entries"]())[2+[2]]+(+("11e100")+[])[4]+([]+[]["entries"]())[0]+([]+[]["entries"]())[0]+([]+[]["entries"]())[2+[2]]+([]+[]["entries"]())[2+[2]]+(+("11e100")+[])[1]+([]["flat"]+[])[3]+([true]+[]["flat"])[1+[0]]+(undefined+[])[1]+([]["flat"]+[])[3]+(false+[])[1]+(true+[])[0]+([]+[]["flat"])[1+[3]]+([]+[]["entries"]())[0]+([]+[]["entries"]())[0]+([]+[]["entries"]())[2+[2]]+([]+[]["entries"]())[2+[2]]+([]+[]["flat"])[1+[4]]+([]+[]["flat"])[1+[4]]+([]+[]["flat"])[1+[3]]+([]+[]["flat"])[1+[4]]+([true]+[]["flat"])["slice"]("-1")+([]["flat"]+[])[3]+(false+[])[1]+(true+[])[0]+([]["flat"]+[])[3]+(+(1+[0]+[1]))["toString"](2+[1])[1]+([]+[]["flat"])[1+[3]]+(false+[])[0]+([]+[]["flat"])[1+[4]]+([true]+[]["flat"])[2+[0]]+(true+[])[1]+(true+[])[3]+(true+[])[0]+(undefined+[])[0]+(true+[])[1]+(undefined+[])[1]+([false]+[]["flat"])[2+[0]]+(false+[])[0]+([true]+[]["flat"])["slice"]("-1")

This is equivalent to:

"try{Function(',')()}catch(f){return f}"

Except for the fact that we can't write ',' (yet). Evaluating that will return a SyntaxError object, which, when converted to string, will yield:

"SyntaxError: Unexpected token ','"

We can then feed that string into RegExp("[\u0027]").exec(...)[0] to extract the single quote.
So we wanna run:

RegExp("[\u0027]").exec(Function("try{Function([]+[[]].concat([[]]))()}catch(f){return f}")())[0]

Applying a whole bunch of substitutions from above, we get one final character:

"'"             RegExp(([]+[]["entries"]())[0]+([]+RegExp("/"))[1]+(undefined+[])[0]+[+[]]+[+[]]+[+!+[]+!+[]]+[+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+([]+[]["entries"]())[2+[2]])[(true+[])[3]+(+(1+[0]+[1]))["toString"](3+[4])[1]+(true+[])[3]+([]["flat"]+[])[3]]([]["flat"]["constructor"]((true+[])[0]+(true+[])[1]+(true+[Infinity])[1+[1]]+([true]+[]["flat"])[2+[0]]+[]["flat"]["constructor"]["name"]+([]+[]["flat"])[1+[3]]+([]+[]["entries"]())[0]+([]+[]["entries"]())[2+[2]]+(+("11e100")+[])[4]+([]+[]["entries"]())[0]+([]+[]["entries"]())[0]+([]+[]["entries"]())[2+[2]]+([]+[]["entries"]())[2+[2]]+(+("11e100")+[])[1]+([]["flat"]+[])[3]+([true]+[]["flat"])[1+[0]]+(undefined+[])[1]+([]["flat"]+[])[3]+(false+[])[1]+(true+[])[0]+([]+[]["flat"])[1+[3]]+([]+[]["entries"]())[0]+([]+[]["entries"]())[0]+([]+[]["entries"]())[2+[2]]+([]+[]["entries"]())[2+[2]]+([]+[]["flat"])[1+[4]]+([]+[]["flat"])[1+[4]]+([]+[]["flat"])[1+[3]]+([]+[]["flat"])[1+[4]]+([true]+[]["flat"])["slice"]("-1")+([]["flat"]+[])[3]+(false+[])[1]+(true+[])[0]+([]["flat"]+[])[3]+(+(1+[0]+[1]))["toString"](2+[1])[1]+([]+[]["flat"])[1+[3]]+(false+[])[0]+([]+[]["flat"])[1+[4]]+([true]+[]["flat"])[2+[0]]+(true+[])[1]+(true+[])[3]+(true+[])[0]+(undefined+[])[0]+(true+[])[1]+(undefined+[])[1]+([false]+[]["flat"])[2+[0]]+(false+[])[0]+([true]+[]["flat"])["slice"]("-1"))())[0]

Level 9

At this point, we can return every character we want simply by doing:

Function("return '\uXXXX'")()

Demo

Let's take the character "C" from your question:

Function("return '\u0043'")()

Running this through all the substitutions above produces an absolute nightmare of 167'060 bytes. This exceeds the maximum post length on SO, but I pasted it into a gist, so feel free to try it. Though you might want to run it by means other than manually pasting it into your console...

like image 193
Siguza Avatar answered Oct 21 '22 17:10

Siguza