Skip to main content

5001 - Code Injection

TL;DR​

This category indicates that user-controlled input flows into a sink that allows code or shell command execution. This directly leads to Remote Code Execution which can be assumed to mean complete compromise of the server.

RCE via Code Injection (eval/exec)​

ISSUE​

The simplest kind of RCE involves user input flowing into a function such as eval or exec which are intended to interpret or run python code.

EXAMPLE​

def update_search_account_filtering(request: HttpRequest) -> HttpResponse:
...
if action in ["delete", "add", "update"]:
...
filter_by_username = eval(request.POST.get("filter_by_username", "True"))

There are few reasons to use these functions, and even fewer reasons to allow a user to control the content of these functions. Generally, we recommend not making calls to these functions with user input. If you only need to eval python datatypes you can use ast.literal_eval. Using it on arbitrary user input can still lead to DOS attack but can't be exploited for code execution (details).

RCE via Command Injection (os.system)​

ISSUE​

This kind of RCE involves user input flowing into a command executed in a system shell. If a user can control a portion of the command being executed in a shell, they can potentially add additional arbitrary commands to be executed.

EXAMPLE​

The following code is intended to run the spellcheck binary on a user provided text:

def spellcheck(request: HttpRequest):
command = f"/usr/bin/spellcheck -l {request.GET['text']}"
return subprocess.getoutput(command)

An attacker, however, can supply a path such as 'test' && rm -rf /, which would result in the following command being executed: /usr/bin/spellcheck -l 'test' && rm -rf /. Since this command is executed in a system shell the rm -rf / command will be executed after the spellcheck command.

In general, we recommend avoiding creation of a subprocess and prefer using the API provided by the language. However, if you need to create a subprocess, we recommend using an API such as subprocess.run, which allows you to separate arguments from the executable being invoked. DO NOT add the shell=True argument otherwise the code would still be vulnerable like the previous example

def spellcheck(request: HttpRequest):
command = ["/usr/bin/spellcheck", "-l", request.GET['text']]
subprocess.run(command)

NOTE: be conscious of the fact that arguments to an executable can still lead to code execution (e.g., the -exec argument of find).