Angular Command
Injection: Examples
and Prevention

stackhawk

StackHawk|October 15, 2021

We'll first go through what command injection is, and how it works in Angular. Finally, we'll show how you can prevent it.

If you look for the top web application frameworks on the internet, you'll find that most sources include Angular on their list. According to BuiltWith, there are over 1.5 million live websites using Angular. So it's pretty obvious why it would appear on the radar of attackers. Therefore, it becomes more important for you to take care of your Angular application's security. 

A malicious actor can cause harm to a web application by taking advantage of vulnerabilities, logical flaws, etc. Fixing vulnerabilities and logical flaws are both in control of the application's owner. So it should be one of the first things anybody should do to enhance security. But the question is with so many possible vulnerabilities, what should you work on first? The answer is simple: You first work on the most common vulnerabilities, move toward less common ones, and then to zero-day. 

Today, we'll focus on one of the common vulnerabilities in web applications—command injection. We'll first go through what command injection is, then how it works, and finally, how we can prevent it.

What Is Command Injection?

Command injection vulnerability is a security weakness in applications where a user can exploit the application to execute operating system (OS) commands on the server. This vulnerability can exist when the application uses user input or data from the client-side of the application in OS commands. 

What a command injection attack can result in depends on the permissions with which the application executes the command. In most scenarios, applications have enough permission to do harm to the system or the network. And even if they don't have a lot of permissions, privilege escalation is not in uncharted waters.

In general, command injection can lead to attackers stealing data, changing configurations, or even bringing the whole server down. In addition, attackers can use commands to know about the internal architecture and gain knowledge of the network. So even if they can't do much damage from/to the exploited system, they can find a weak link. That said, this vulnerability is not something you should turn a blind eye to. 

Now let's try and understand how command injections work. 

How Does Command Injection Work?

The main condition to fulfill for a command injection to work is that the application should use data from a client-side request in OS commands. In such applications, the attacker can inject crafted input to execute commands in addition to the command the application is designed to execute. 

We will go through some examples of how one can exploit command injection vulnerability in Angular applications. And for that, we first need to set up a vulnerable Angular application. The application will first take an IP address as input from the user. The entered IP address will be passed to the ping OS command, and the result of this command will be displayed again to the user. 

We start with installing Angular. Once done, we start building our vulnerable application. We need to build four main parts to do the following: 

  1. Take user input.

  2. Send input to the back end.

  3. Execute an OS command with user input.

  4. Display the result of the OS command.

So let's start with building the first part. 

Taking User Input

There are different ways to do this. I'll go with the template-driven form to collect user input. And for this, we need to import FormsModule and HttpModule in the app.module.ts file. First, you need to get into your project directory and under that /src/app/ directory. For example, the name of my project is osci, so the path is /home/kali/osci/osci/src/app. 

You can use your favorite text editor to add the following lines and mention the module names under imports: 

import { HttpClientModule } from '@angular/http';
import { FormsModule } from '@angular/forms';

Angular Command Injection: Examples and Prevention - Picture 1 image

Now, let's create a webpage to take user input. We will do this by adding HTML in the app.component.html file.

<form #myForm="ngForm" (ngSubmit)="onSubmit(myForm)">
<p>
<label for="ip">Enter IP address to ping</label>
<input type="text" name="ip_in" ngModel>
</p>
<p>
<button type="submit">Submit</button>
</p>
<p>{{ msg }}</p>
</form>

Now you can save this, run the ng serve command from the terminal, and visit localhost:4200 (or another port that you've explicitly mentioned) in your browser. You should see a webpage like this:

Angular Command Injection: Examples and Prevention - Picture 2 image

This is the front end of the application.

Sending Input to the Back End and Displaying the Result on the Webpage

Angular is a front-end framework. So, we need to send the input to a back end to execute the OS command. To do this, open the app.component.ts file and add the following lines:

import { Component } from '@angular/core';
import {HttpClient} from "@angular/common/http";


@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']


})


export class AppComponent {
msg: any = null; 
constructor(private http:HttpClient) {
}
private data:any = []

title = 'osci';
onSubmit(myForm) {

var headers = { 'content-type': 'application/json'} 
var body = JSON.stringify(myForm.value);
var res = this.http.post("http://localhost:5000/ping", body, {'headers': headers}).subscribe((res)=>{
this.data = res;
console.log(res);
this.msg = Object.values(res);
})

}
}

This TypeScript code will collect the user input from the HTML form and send it to the back end. In our case, we'll use a Python API server as the back end. So basically, this TypeScript code will send the request to the Python API, get the response, and display it on the webpage. The line this.msg = Object.values(res); will update an element in the webpage with the response value. The API request will be to http://localhost:5000/ping because we'll be hosting our API server locally on port 5000. Now let's build our API server. 

Execute OS Command With User Input

My choice for API server is Python Flask. If you don't have it installed already, you need to install Python3. If you're using a Linux machine like me, you can install it using the following command: 

sudo apt-get install python3

Next, we install Flask by running: 

pip install flask

Copy the following Python script for API in a .py extension file and save it. 

from flask import Flask, json, request, jsonify
import os
from flask_cors import CORS

api = Flask(__name__)
api.config['SECRET_KEY'] = 'the quick brown fox jumps over the lazy dog'
api.config['CORS_HEADERS'] = 'Content-Type'
cors = CORS(api)

@api.route('/ping', methods=['POST'])
def get_ping_result():
    data = json.loads(request.data)
    cmd = 'ping -c 2 ' + str(data['ip_in'])
    os.system(cmd + '>> a.txt')
    f = open("a.txt", "r")
    r = f.read()
    os.system('rm a.txt')
    print(r)
    return (jsonify(data=r))

if __name__ == '__main__':
    api.run()

First, we import the modules we need. Then we'll configure the API with CORS to avoid same-origin policy errors. The next part of the code gets the data from the request and runs the ping command by appending the IP to it. The result of the command is stored in a file and then sent back as a response to the API request. 

Angular Command Injection in Action

Now that we have everything we need, it's time to fire up our code and do some hands-on testing of command injection. First, we start our API server. To do this, you need to change your current directory in the terminal to the directory where you have the Python script and run the following command: 

python3 <name of the file>.py

That should start the API server on port 5000 by default.

Angular Command Injection: Examples and Prevention - Picture 3 image

And to start our Angular project, open another terminal and run the following command from your project folder: 

ng serve

If everything goes according to plan, you should see something like:

Angular Command Injection: Examples and Prevention - Picture 4 image

Now you can open the Angular application from your browser. Let's first see how the application behaves when we enter a legit input and then try to inject a command. So, when I enter the IP 8.8.8.8, the IP is passed to the API server, and the Python script executes the OS command ping -c 2 8.8.8.8 and sends back the response, which is displayed on the webpage.

Angular Command Injection: Examples and Prevention - Picture 5 image

Nothing critical so far. Now let's see what happens if I enter 8.8.8.8 ; pwd. 

Angular Command Injection: Examples and Prevention - Picture 6 image

So this input executes the ping -c 2 8.8.8.8, the semicolon tells that there's another command to execute and then the command pwd is executed. The output you're seeing is the output of the pwd command, which displays the path to the current working directory. Now imagine that instead of this command, an attacker gave a command to delete all the files. Well, it could bring the API server and the web application down. 

So this is how an attacker can craft malicious inputs to execute a command injection attack. 

Angular Command Injection: Prevention

Command injection weakness exists when you don't apply safety measures while using user input as a part of OS commands. But the good news is, you can fix it. Because you have complete control over your application, there are multiple ways you can prevent command injections. We'll go through some of the most common ones that apply in general to every Angular application. 

Do You Need OS Commands?

Command injection is not possible if you don't use OS commands for your application. So the first question you need to ask yourself is whether you actually need it. You might need a particular function that the OS command provides, but do you need to run an OS command to get that result? Command injections have been here for ages. So, back-end framework developers have provided multiple functions that you can use as a substitute for the OS command itself. If you can replace the OS command with a library function or an API call, then command injection isn't possible.

Filter Dangerous Characters

For command injections to work, the original command used by the application has to be executed, and then the next injected command is executed. As we saw in our example, we need a way to run two or more commands in a single run. And we use some special characters to do this. Some of the dangerous characters are: 

  • & (ampersand)

  • | (vertical slash)

  • ; (semicolon)

  • ` (backtick)

To prevent OS command injections, you can make a list of all such dangerous characters that shouldn't be a part of a legit command. Then, add a filter to remove these characters before executing the command. 

For instance, in our example, the input was 8.8.8.8 ; pwd. The command for this input would be ping -c 2 8.8.8.8 ; pwd, which was a successful injection. But we have a dangerous character here. If we filter it out, the command would become ping -c 2 8.8.8.8 pwd and this would throw an error, therefore failing the injection. 

Use Allowlist

If you are using only a limited number of OS commands, you can have a list of these commands stored. Then before executing the command, you can have a check whether the command being executed is a part of your allowlist. If it's not, then don't execute it. You can also use pattern matching to identify safe inputs to allow. For example, in our example, a legit input is when there's only an IP address. So when the user input is given, you can check if it matches the pattern of an IP address. If it doesn't, then don't execute it. 

Principle of Least Privileges

According to CISA, the "Principle of Least Privilege states that a subject should be given only those privileges needed for it to complete its task." If you give your application limited privileges, then the injected commands would fail due to lack of permissions, therefore preventing successful command injections. 

Add Automated Security Testing to Your Pipeline for Free

Conclusion

Angular is a front-end framework. Although command injections happen on the back end, Angular can pass these injections to the back end. We've discussed how command injections work in an Angular application and how they can be prevented. You can apply all the preventions at the back end, but that's going to be very expensive on resources. Luckily, you can implement multiple prevention methods using Angular. Above we discussed some general prevention measures. But you shouldn't stop there. For better security, you need to evaluate custom scenarios for your application and implement security measures for them.

This post was written by Omkar Hiremath. Omkar is a cybersecurity team lead who is enthusiastic about cybersecurity, ethical hacking, and Python. He is keenly interested in bug bounty hunting and vulnerability analysis.


StackHawk  |  October 15, 2021