Remote command execution - Dart

Remote command execution - Dart

Need

Prevention of unauthorized remote command execution

Context

  • Usage of Dart 2.0 for building high-performance, cross-platform applications
  • Usage of Shelf for managing and organizing data in a Node.js application

Description

Non compliant code

        import 'package:shelf/shelf.dart' as shelf;
import 'dart:io';

shelf.Response commandHandler(shelf.Request request) {
  var params = request.url.queryParameters;
  var command = params['command'];

  Process.run(command, []).then((ProcessResult results) {
    print(results.stdout);
  });

  return shelf.Response.ok('Command Executed');
}
        
        

The above code is a simple handler function for a backend application using the shelf framework in Dart. This function takes a request, extracts a command from the query parameters of the request, and then runs that command on the system using Process.run().

The vulnerability in this code is that it takes an external input (the command) and directly uses it to execute an operating system command without any form of sanitization or validation. This means that an attacker could potentially provide a malicious command as input, which could lead to remote command execution. This is a serious security vulnerability as it could allow an attacker to execute arbitrary commands on the system, leading to a range of potential attacks including data theft, data corruption, or even taking control of the system.

Steps

  • Validate and sanitize the input: Ensure that the 'command' parameter is strictly validated against a whitelist of allowed commands. Any input that does not strictly match the whitelist should be rejected.
  • Use parameterized APIs: Instead of directly injecting user input into the command, use APIs that allow parameterized execution. These APIs separate the command from its arguments, which prevents the arguments from being interpreted as part of the command.
  • Limit privileges: Run the application with the least privileges necessary. This can limit the damage if a command injection vulnerability is exploited.
  • Use an allowlist approach: Only specific commands necessary for the application should be allowed. All other commands should be blocked.
  • Implement error handling: Do not reveal any system level information in error messages or exceptions. This information can be used by an attacker to understand the system and create more targeted attacks.

Compliant code

        import 'package:shelf/shelf.dart' as shelf;
import 'dart:io';

const List allowedCommands = ['ls', 'pwd', 'whoami']; // Add your allowed commands here

shelf.Response commandHandler(shelf.Request request) {
  var params = request.url.queryParameters;
  var command = params['command'];

  if (!allowedCommands.contains(command)) {
    return shelf.Response.forbidden('Invalid command');
  }

  Process.run(command, []).then((ProcessResult results) {
    print(results.stdout);
  });

  return shelf.Response.ok('Command Executed');
}
        
        

The updated code includes a list of allowed commands ( allowedCommands). This list should contain all the commands that the application needs to execute. Any command not in this list is considered invalid and will be rejected.

When a request is received, the commandHandler function retrieves the 'command' parameter from the request's query parameters. It then checks if the command is in the allowedCommands list. If it is not, the function returns a 403 Forbidden response with the message 'Invalid command'.

If the command is in the allowedCommands list, the function executes the command using the Process.run function. The results of the command are then printed to the console.

This approach ensures that only valid, expected commands can be executed by the application. It prevents an attacker from executing arbitrary commands by manipulating the 'command' parameter. This is a simple and effective way to prevent command injection vulnerabilities.

References