This page looks best with JavaScript enabled

CTF: JS with restricted chars set

 ·  ☕ 6 min read  ·  ✍️  Firmin Martin

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

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.

Figure 1: Interface of the challenge.

Figure 1: Interface of the challenge.

The source code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
const { VM } = require('vm2');
const express = require("express")
const path = require("path")

const { FLAG } = require('./flag.json');

const app = express()
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

class Security {
  constructor() {
  }

  disable() {
    this.#lock = false;
    return ""
  }

  isLocked() {
    return this.#lock;
  }

  #lock = true
}

app.get("/", (req, res) => {
  let result = "ERROR!";
  let security = new Security();
  const exec = req.query.q
  if (exec === undefined || exec.length === 0) {
    result = "";
  }

  if (exec && exec.length < 55 && !/[^.`+\|a-z]/.test(exec)) {
    try {
      const vm = new VM({
        timeout: 100,
        sandbox: {sec: security}
      });
      const execResult = vm.run(exec);
      if (execResult === 1069 && !security.isLocked()) {
        result = FLAG
      }
    } catch (e) {
      console.error(e)
    }
  }
  res.render('index', { result });
})

app.get('/source', function (req, res) {
  res.sendFile(path.join(__dirname, 'app.js'));
});

module.exports = app;

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

  1. evaluates to 1069;
  2. calls sec.disable() in order to unlock the flag;
  3. 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 return 0 or typecasted to 0 (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:

1
tag`1 + 1 = ${1 + 1}.`

is equivalent to

1
tag(["1 + 1 = ", "."], 2)

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.

1
+``+`x`+`xxxx`.length+`xx`.length+`d`|sec.disable``

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.

Figure 2: The flag.

Figure 2: The flag.

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.
  • If you enjoy this kind of puzzle, you might want to check out codegolf.SE.

  1. In fact, multiplication alone would not be enough as 1069 is prime. ↩︎

  2. I suspect that the author, out of habit, intended to escape the pipe | with a backslash \, though it was absolutely not necessary. ↩︎

  3. 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 achieve undefined without [], though. That worth another post. ↩︎


Firmin Martin
WRITTEN BY
Firmin Martin

What's on this Page