A few days ago, I participated in, or more precisely, dropped by an online CTF contest held in Taiwan. Having no experience, most challenges were just nonsense to me. Except one, which was purely programmatic. This is an in-depth analysis of it.
The challenge
- Author: Leko
- Challenge: Calculator 0x1
The description of this challenge was
Javascript is the best programming language.
That’s kind of a hint that suggests you to be prepared to exploit diverse JS syntactic tricks. The challenge provided a simple interface to submit a query along with the source code which processes the query and returns the result.
The source code
|
|
Nothing fancy here. In short, it retrieves the query, and under certain
conditions, executes it in a sandbox (using vm2
). It returns the flag whenever
the result meets another specific conditions. Reading the documentation of vm2
helps to figure out that sec
is considered as a global object in the sandbox.
The task
In layman’s terms, all you have to do is finding a JS expression whose the
length is less than 55
that
- evaluates to
1069
; - calls
sec.disable()
in order to unlock the flag; - contains only characters in the character class
[.`+\|a-z]
.
Without the last restriction, the solution would be simply sec.disable(); 1069
.
However, due to the syntactic restriction:
- We don’t have parentheses to perform function call as usual.
- Neither numbers nor multiplication1 are allowed, meaning that to make up
1069
, we have to count on something else. - Semi-colon is not available, so we have to find an alternative for sequential statements.
At the same time, the character class tells us exactly to focus on the multifaceted semantics of its characters2:
+
not only serves as a binary addition operator, a string concatenation operator, but can also be used to perform integer type coercion when it is a unary operator.- Although we don’t have the double-quotation mark
"
to form string constant, template literal`...`
is a natural alternative. In fact, the backtick`
character can do much more than that, explanation below. - Having the dot
.
character implies that we can freely use object attributes and, by struggling a bit, object methods. - The pipe character
|
, as a bitwise OR operator, actually allows a limited form of sequential statements: to achieve<exp1>; <exp2>; ...; <ret-value>
, we can write instead<exp1> | <exp2> | ... | <ret-value>
as long as<exp1>
,<exp2>
, etc. respectively return0
or typecasted to0
(e.g.""
,false
, etc.). Note that, in this case,<ret-value>
will be typecasted to an integer.
Tagged template
Essentially a tagged template has the form of tag`...`
, where tag
is a tag
function and `...`
a template literate. A tagged template is evaluated by
passing string and placeholders of the template literate onto the tag
function.
Its first argument is a string array constituted by strings that are not
placeholder and the remaining variable arguments are placeholder expressions
${expression}
of the template literate.
Example:
|
|
is equivalent to
|
|
For more details, see MDN Web Docs: tagged template.
In merely fifty characters, we won’t be able to define any useful function
which has a tag function signature. The whole point of tagged template here is
using it with empty template literate. In this case, tagˋˋ
is equivalent to
tag()
. In other words, the backtick character is enough to perform function
invocation without argument. At this point, we have solve the half of the
problem: sec.disableˋˋ
disables the lock.
Make up 1069
The key to arrange 1069
within less than fifty characters is using hexadecimal numerals.
1069
written in hexadecimal is 0x42d
. But, we don’t have numbers, so we have to create them
one by one based upon string, then concatenate the result:
0
→+ˋˋ
4
→`xxxx`.length
2
→`xx`.length
Keep in mind that with JS type coercion, the binary operator +
returns a string
when one of its operands is a string. Moreover, the +
operator is
left-associative, so +ˋˋ+ˋxˋ+ˋxxxxˋ.length+ˋxxˋ.length+ˋdˋ
is evaluated to the
string "0x42d"
.
The solution
As sec.disableˋˋ
returns an empty string, we can use the pipe character |
to
build a sequence of statement as stated above.
Hence, by putting everything together, we obtain the solution.
|
|
The string "0x42d"
will be coerced into 1069
and we have invoked the function
sec.disable()
. The solution is 51 chars long, so it will pass the query length
condition.
Recapitulation
Character | Alternative of | Utility |
---|---|---|
` |
" and () |
string constant and function invocation without argument |
ǀ |
; |
sequential statements that returns an integer |
+ |
typecast to integer, binary addition operator and string concatenation operator | |
. |
access object attributes and methods | |
a-z |
along with ` and + , define hexadecimal number |
Further reading
- Our solution is 51 chars long based on a 30 chars set. There are other
encodings that use a far little chars set, but they generate code that are
severalfold longer:
- JSFuck (six chars,
[]()!+
3): 2417 chars. - Hieroglyphy (eight chars,
()[]{}!+
): 11090 chars. - aaencode (Japanese-styled emojis): 1666 chars.
- JSFuck (six chars,
- If you enjoy this kind of puzzle, you might want to check out codegolf.SE.
-
In fact, multiplication alone would not be enough as
1069
is prime. ↩︎ -
I suspect that the author, out of habit, intended to escape the pipe
|
with a backslash\
, though it was absolutely not necessary. ↩︎ -
Based on what we learned today, maybe we can reduce JSFuck’s chars set to five characters, namely,
[]!+`
? Or, maybe even better!+`
? At the time of writing, I have no clue how to achieveundefined
without[]
, though. That worth another post. ↩︎