curl · Report #3293801
Title: Remote Code Execution (RCE) via Arbitrary Library Loading in `--engine` option
How it was found
Summary:
The curl command-line tool is vulnerable to Arbitrary Code Execution on POSIX-like systems (Linux, macOS, etc.). The --engine option allows loading an OpenSSL crypto engine from a shared library (.so file). Crucially, this option accepts an absolute or relative path to the library file, allowing a user to load any shared library on the file system.
An attacker can craft a malicious shared library containing a __attribute__((constructor)) function. This function is executed by the dynamic loader the moment the library is loaded into the curl process's memory, achieving immediate code execution, even before OpenSSL attempts to initialize it as an engine.
This leads to direct RCE if an attacker can influence the arguments passed to a curl command, a common scenario in web application backends, CI/CD pipelines, and other automated scripts.
(Statement as per disclosure policy: This vulnerability was discovered and verified by me. An AI assistant was used to help structure and draft this report based on my findings and proof-of-concept.)
Affected version:
I reproduced this on the following version, but it likely affects all versions that support the --engine option on POSIX systems with GCC/Clang compiled binaries.
┌──(Dr4g0n㉿DESKTOP-2CIPGDF)-[~]
└─$ curl -V
curl 8.13.0 (x86_64-pc-linux-gnu) libcurl/8.13.0 OpenSSL/3.5.0 zlib/1.3.1 brotli/1.1.0 zstd/1.5.7 libidn2/2.3.8 libpsl/0.21.2 libssh2/1.11.1 nghttp2/1.64.0 nghttp3/1.8.0 librtmp/2.3 OpenLDAP/2.6.9
Release-Date: 2025-04-02, security patched: 8.13.0-5
Protocols: dict file ftp ftps gopher gophers http https imap imaps ipfs ipns ldap ldaps mqtt pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp ws wss
Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTP3 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM PSL SPNEGO SSL threadsafe TLS-SRP UnixSockets zstd
Steps To Reproduce:
These steps will demonstrate direct code execution on a WSL/Linux system.
-
Step 1: Create the malicious payload. Save the following C code as
evil_engine.c. This code will executeid > /tmp/RCE_VIA_ENGINEthe moment the library is loaded.#include <stdlib.h> // This constructor function is executed automatically by the dynamic loader // as soon as the library is loaded into the process address space. __attribute__((constructor)) static void rce_init(void) { system("id > /tmp/RCE_VIA_ENGINE"); } -
Step 2: Compile the payload into a shared library. Use
gccto compile the C code into a shared object (.so) file.gcc -fPIC -shared -o evil_engine.so evil_engine.c -
Step 3: Prepare for verification. Ensure the proof file does not exist before the attack.
rm -f /tmp/RCE_VIA_ENGINE -
Step 4: Execute
curlwith the malicious engine. Run anycurlcommand, but use the--engineoption to point to our malicious library. Note that we must provide an absolute path.curl --engine `pwd`/evil_engine.so https://example.comYou will see an error message like
curl: (53) SSL Engine '...' not found. This error is expected and irrelevant, as it occurs after our malicious code has already been executed by the constructor. -
Step 5: Verify Code Execution. Check the contents of the proof file.
cat /tmp/RCE_VIA_ENGINEThe command will output the result of the
idcommand, confirming that arbitrary code was executed successfully as the user who rancurl.
Supporting Material/References:
I have recorded a full video of the Proof of Concept: PoC.mp4
Impact
The security impact is direct and critical Remote Code Execution.
An attacker who can control or influence the arguments passed to a curl command can achieve RCE on the underlying system. This completely bypasses any application-level security.
Common attack scenarios include:
- Web Application Backends: A web service that allows users to provide options for a
curlcommand (e.g., in a "website checker" or "webhook tester" feature) would be vulnerable. An attacker could inject--engine /path/to/payload.soif they can also upload a file. - CI/CD Pipelines & Scripts: Automated scripts that build
curlcommands using variables from external, untrusted sources (like commit messages or API responses) could be tricked into loading a malicious engine. - Social Engineering: A developer or system administrator could be tricked into running a seemingly benign diagnostic command provided by an attacker, which includes the malicious
--engineflag.
The vulnerability stems from the --engine feature trusting a user-provided path without any validation or restriction to a secure, system-defined directory for crypto engines. This effectively turns the feature into a "load-and-run" primitive for arbitrary shared libraries.