Typescript Command
Injection: Examples
and Prevention

stackhawk

StackHawk|January 25, 2022

In this article, we'll dive into TypeScript command injection, one of the most common exploits you can find on the web, and how to keep your work safe.

One of the great things about working with cutting-edge technology like Typescript is the feeling of experimentation. It's like when we were little and got our presents early. We can't wait to try it out, stretch it, and find out what it can do.

This, however, comes with some caveats. The community is young, and documentation is scarce. In addition, some rough edges can hinder our productivity and experience. And of course, there's security and vulnerability.

In this article, we'll dive into the topic of command injection, one of the common exploits you can find on the web and find ways to keep your work safe. First, we'll explore the basic concepts of injection. Next, you'll see some examples of command injection in JavaScript. Then we'll provide some mitigating measures.

Setting the Ground

First, let's prepare a simple boilerplate project. Then you can play around with it and keep it so you can check your work later.

Start by making sure you have NodeJS installed. You can do this by running the following command in macOS:

curl "https://nodejs.org/dist/latest/node-${VERSION:-$(wget -qO- https://nodejs.org/dist/latest/ | sed -nE 's|.*>node-(.*)\.pkg</a>.*|\1|p')}.pkg" > "$HOME/Downloads/node-latest.pkg" && sudo installer -store -pkg "$HOME/Downloads/node-latest.pkg" -target "/"

Alternatively, you can use Brew to install it with the following command:

brew install nodejs

If you run any other system, you can find more instructions here.

Now, creating a NodeJS project is extremely simple. Just create a folder called "nodejs_typescript_sample" (you can call this folder anything you like) and navigate to it. Once inside, create a file called "app.js" (this time we recommend you do call it that) and paste the following code:

const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

Once you've done that, you can go to the terminal and run the code using the following command:

node app.js

Visit http://localhost:3000, and you should see a message saying, "Hello World."

Simple, right?

Talking Command Injection

OK, so now let's talk command injection.

To briefly explain command injection, we need to talk about injection in general in web applications.

You might have heard people talk about SQL injection in security groups and on sites around the web. Simply speaking, injection is where an attacker attempts to hijack user input. By using specific characters or strings of characters, the attacker can bypass the application and manipulate or gain access to an application's database.

This attack and the vulnerability related to it became a buzzword for web security in the early 2000s as it became notorious for its seemingly simple and yet devastating potential.

Now, command injection, or code injection, is a special injection attack where the attacker injects JavaScript or Java code into the server to seize control of it. Subsequently, the browser or application runtime wrongly interprets this malicious code as valid since it can't distinguish between the code the developer intended and the attacker's code.

This situation is particularly hazardous considering that any arbitrary code from an attacker that the application executes can bypass any security measures, potentially even surrendering the server altogether.

Thankfully, code that takes strings as parameters and executes commands on the system is not standard on most development projects. For the most part, best practices discourage developers from using functions like 'exec' or 'directory.execute' unless necessary.

Nevertheless, it is essential to be aware that some of the libraries you use on your project might have these functions and put your platform at risk.

An Example of Command Injection

Now that you have a better understanding of what command injection is and what it's capable of doing to your operating system, let's look at a simple example.

Assuming that your victim application uses a function that uses the aforementioned 'child_process' module (say, the 'exec' Node.js function or the 'eval' JavaScript function), an attacker can reach your server command line by simply appending commands to the request. 

var express = require('express');
var router = express.Router();
 
/* GET home page. */
router.get('/', function(req, res, next) {
 // Get parameter. In this case, the image name.
 const image = req.query.image;
 // Execute the command
 exec(`git log --oneline ${image}`, (err, output) => {
   // Respond with HTTP 500 if there was an error
   if (err) {
     res.status(500).send(err);
 
     return;
   }
   // output the HTTP response
   res.send(output);
 });
});
 
module.exports = router;

The following is an excellent example of this:

curl http://localhost:3000/index?image=prof.png;nc%20-l%205656%20|/bin/bash

In this request, the attacker is trying to run the following command:

nc -l 5656 | /bin/bash

This command, called 'netcat,' can run arbitrary code on your server on behalf of the attacker and cripple it.

Fixing Command Injection Vulnerabilities

Alright, so what can we do about this? 

Well, the first line of defense against this kind of threat is also the most effective, and that is to do away with using functions that execute commands at a low level altogether.

Avoid eval(), exec(), setTimeout(), setInterval(), and any other function that allows dynamic code unless absolutely necessary. 

Additionally, avoid new Function() for reasons that should be obvious at this point.

var express = require('express');
var router = express.Router();
 
/* GET home page. */
router.get('/', function(req, res, next) {
 // Get parameter. In this case, the image name.
 const image = req.query.image;
 // Execute the command
 execFile(`git log --oneline ${image}`, (err, output) => {
   // Respond with HTTP 500 if there was an error
   if (err) {
     res.status(500).send(err);
 
     return;
   }
   // output the HTTP response
   res.send(output);
 });
});
 
module.exports = router;

Also, make sure to employ an input sanitization mechanism for any user input that your application allows. As a general rule, it's essential to restrict and regulate all avenues of user input that your application provides. For example, look for ways to change dynamic inputs into fixed selections. And make sure to safelist all inputs against known attacks.

Additionally, avoid using code serialization in JavaScript. Yes, it is a thing, and no, you shouldn't overlook it.

Finally, we recommend using security analysis tools like StackHawk and scanning your application for injection vulnerabilities regularly. StackHawk's state-of-the-art software tests your running applications, services, and APIs for security vulnerabilities that your team has introduced as well as exploitable open-source security bugs. 

With StackHawk, application security can keep up with the pace of today's engineering teams. Find vulnerabilities at the pull request and quickly push out fixes, all while yesterday's security tools are waiting for someone to kick off a manual scan.

Add Automated Security Testing to Your Pipeline

Moving On

The work of making robust and secure applications has never been easier and more accessible. Modern languages like Typescript and versatile platforms like NodeJS have provided unprecedented power to engineers looking to build the future. However, with all that versatility and power, it's crucial to maintain a high standard of security and resilience against the ever-evolving threats on the web.

Maintaining extensive expertise on these development stacks is becoming more and more of a necessity every year. Nowadays, teams have to be more trained and specialized than before. This is why it's vital to keep your standards high and use robust solutions like StackHawk.

Whether you are a developer trying to figure out your way out of a problem or a manager researching assets that can improve the value of your product, security must be at the forefront of your priorities.

This post was written by Juan Reyes. Juan is an engineer by profession and a dreamer by heart who crossed the seas to reach Japan following the promise of opportunity and challenge. While trying to find himself and build a meaningful life in the east, Juan borrows wisdom from his experiences as an entrepreneur, artist, hustler, father figure, husband, and friend to start writing about passion, meaning, self-development, leadership, relationships, and mental health. His many years of struggle and self-discovery have inspired him and drive to embark on a journey for wisdom.


StackHawk  |  January 25, 2022