Bun
An alternative JavaScript runtime with unique libraries and quirks
# Related Pages
JavaScriptNodeJSDescription
Bun is an alternative runtime to NodeJS. It aims to be faster, and pack all tooling into one command: bun
. This re-implementation comes with some quirks, and the added features can have vulnerabilities too. This page will describe some of them.
Bun $ Shell
Bun is an alternative JavaScript runtime just like NodeJS, but has some more native packages. One such API is the $ Shell API that allows running shell commands safely with Tagged templates. User input as a string will be escaped properly to prevent injection of more commands.
Command Injection using Objects
Take a look at the following example:
Running this, we can try to access /?dir=$(id)
in hopes of command substitution, but we get an error instead:
The same will happen for any command injection attempt. There are however, edge cases where this is exploitable, and the example above is surprisingly one of them. The problem lies in an obscure functionality that disables the escaping of arguments: $.escape
(escape strings)
In that section of the documentation an interesting example is shown:
If you do not want your string to be escaped, wrap it in a
{ raw: 'str' }
object:
If the value in the tagged template is an object with a raw
key, the value is not escaped. If we are able to abuse any functionality to make our input into an object, we can include this raw:
key ourselves to bypass the filtering. In Express, this is possible by using a more complex query string that can create objects like ?dir[raw]=$(id)
becoming { raw: "$(id)" }
. This works!
Many other frameworks allow creating an object like this from a query string, or even directly from JSON input in a request body.
Globbing
One more interesting functionality in Bun is that globbing is partially implemented for filename expansion. This allows inputs like *
to match all files, and more specific patterns like *.txt
to match only files ending in .txt
. Take the following example:
The above code should directly echo back your input, but natively it allows wildcards to match any local filenames, even in different directories:
Note that depending on the filenames matched, this can even inject multiple arguments into a place where normally only one argument should be. The following Python script can be used to test this:
The above will generate multiple arguments in the place of ${string}
, and if you have control over the filenames matched, it may allow you to perform some more complex Argument Injection (Wildcards) attacks:
Bun <= v1.1.8 - Forgotten characters
In older versions of Bun, the first implementation of escaping shell characters lacked a few key characters that should have been escaped. Namely: `
and <
which can still cause trouble in the command line. A commit from version 1.1.8 to 1.1.9 adds these characters to the escape list.
Before this commit, we could abuse weird parsing of the shell to write files, and execute commands. Take the following vulnerable example:
In the above code snippet, any input inside msg=
will be passed to echo
and returned as a response. The shell API is used correctly and should not allow for command injection. It is however vulnerable to RCE in this older version!
First, you should know a weird parsing behaviour allowing you to write the output of a command to a file. By piping STDOUT (1
) from an input file with <
, the output of the echo
command is appended to the file. Note that spaces are not allowed, but the payload below works:
Tabs (%09
) are allowed, and can be used as shell argument separators.
Next, backticks (`
) are not escaped either. These allow for execution of arbitrary commands, just without arguments. By passing the just-written file as input to sh
, we will be able to execute arbitrary commands with arguments.
In the first step, we could have written a file with some commands to execute a reverse shell. Keep in mind that there are still some limitations around what content may be written to the file, as it is directly the output of the original command. You will be able to use only the following special characters in your input before the <
:
Last updated