Skip to content

Month: March 2023

AWS Lambda (Python) – Preventing Command Injection

When writing a AWS lambda function, we need to be careful about secure coding issues.

If your lambda function takes an input and use it to run a command, you need to avoid using os.system(...). You should use subprocess.run(...) but without the shell. The commands provided to subprocess should be in arguments.

Explaining the injection

I will use an example of AWS Lambda function that takes an input from event and uses it in a subprocess to run a command. Notice the command is used in a f-string.

import subprocess
import uuid

def lambda_handler(event, context):
    rand_value = uuid.uuid4().hex[:6]
    file_name = f'{event['file_name']}_{rand_value}'
    print('Creating:', file_name)

    result = subprocess.run(f'cd /tmp && touch {file_name} && ls -la', shell=True, stdout=subprocess.PIPE)
    print(result.stdout.decode('utf-8'))

We can inject a malicious filename to steal the values in env

ubuntu@ubuntu-virtual-machine:~$ python3 command_injection.py
Enter file name: hello && env | base64 #
Creating: hello && env | base64 #_526feb
R0pTX0RFQlVHX1RPUElDUz1KUyBFUlJPUjtKUyBMT0cKTEVTU09QRU49fCAvdXNyL2Jpbi9sZXNzcGlwZSAlcwpVU0VSPXVidW50dQpTU0hfQUdFTlRfUElEPTE0MDIKWERHX1NFU1NJT05fVFlQRT14MTEKU0hMVkw9MQpIT01FPS9ob21lL3VidW50dQpPTERQV0Q9L2hvbWUvdWJ1bnR1CkRFU0tUT1BfU0VTU0lPTj11YnVudHUKR05PTUVfU0hFTExfU0VTU0lPTl9NT0RFPXVidW50dQpHVEtfTU9EVUxFUz1nYWlsOmF0ay1icmlkZ2UKTUFOQUdFUlBJRD0xMDI5CkRCVVNfU0VTU0lPTl9CVVNfQUREUkVTUz11bml4OnBhdGg9L[...]

Defence

We should refactor the vulnerable code by adding two defences:

Input validation

Dependings on the context, try to whitelist the allowed characters.

If you do not know in advanced then it is crucial that you follow the next defence of using arguments command for subprocess.

Usage of arguments in subprocess

Instead of providing the commands in concatenated string or string format, provide an argument format ['touch', file_name]. This means you need to break down the command in different `subprocess` calls. You cannot run the multiple commands in one single subprocess.

One common routine is that you need to run a sequence of command at a specific same directory. With the use of cwd argument in subprocess.run(...), you can specify where the command should be executed.

import subprocess
import uuid

def lambda_handler(event, context):
    rand_value = uuid.uuid4().hex[:6]
    file_name = f'{event['file_name']}_{rand_value}'
    print('Creating:', file_name)

    working_dir = '/tmp'
    subprocess.run(['touch', file_name], cwd=working_dir, stdout=subprocess.PIPE)
    result = subprocess.run(['ls', '-la'], cwd=working_dir, stdout=subprocess.PIPE)
    print(result.stdout.decode('utf-8'))