CVE-2024-40800
An arbitrary argv[0] execution vulnerability exists in the ramrod binary of Apple macOS version 14.5 (23F79) x86_64. An attacker can inject an arbitrary argv[0] pathname while launching the ramrod, leading to SIP bypass. Previous versions may also be affected, however, they haven’t been tested.
The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.
Apple macOS 14.5 (23F79) x86_64
macOS - https://apple.com
8.2 - CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H
CWE-20 - Improper Input Validation
macOS is the operating system designed by Apple for their line of Mac computers, providing a graphical user interface and managing system resources and hardware.
The binary ramrod
seems to be a component of the macOS recovery mechanism. This binary is signed by Apple and possesses one interesting heritable entitlement:
$ codesign -dv --entitlements - ./ramrod | grep -A2 heritable
Executable=/Users/frbenven/Downloads/POC/ramrod
Identifier=com.apple.ramrod
Format=Mach-O thin (x86_64)
CodeDirectory v=20400 size=16457 flags=0x0(none) hashes=504+7 location=embedded
Platform identifier=15
Signature size=4442
Signed Time=7 May 2024 at 05:03:53
Info.plist=not bound
TeamIdentifier=not set
Sealed Resources=none
Internal requirements count=1 size=64
[Key] com.apple.rootless.install.heritable
[Value]
[Bool] true
This particular entitlement is noteworthy because of its implications: com.apple.rootless.install.heritable
. This entitlement effectively allows the child processes of ramrod
to bypass System Integrity Protection (SIP). From an attacker’s perspective, this is significant, but it would require the attacker to somehow force ramrod
into spawning a process controlled by the attacker.
Following a redacted version of the ramrod
’s main
function:
uint64_t main(int32_t argc, int64_t* argv)
{
[...]
rax_1 = plugin_related();
int32_t var_5d8 = 2;
int128_t likely_kern_bootargs;
if (rax_1 == 0)
{
zmm0 = {0};
__builtin_memset(&likely_kern_bootargs, 0, 0x90);
int64_t rcx_1;
rcx_1 = _stat$INODE64("/usr/libexec/ramrod/plugins/devi…", &likely_kern_bootargs, zmm0) == 0;
var_5d8 = ((int32_t)(rcx_1 * 3));
}
int32_t argv_2_is_server;
if (argc == 2)
{
argv_2_is_server = _strcmp(argv[1], "-server");
}
if (((argc == 2 && argv_2_is_server != 0) || (argc != 2 && argc != 1)))
{
_fprintf(*(uint64_t*)___stderrp, "usage: %s [-server]", *(uint64_t*)argv);
_exit(1);
/* no return */
}
int32_t child_pid;
int64_t var_620;
char* child_argv[0x3];
[1] if (argc == 1)
{
[2] child_argv[0] = *(uint64_t*)argv;
child_argv[1] = "-server";
child_argv[2] = 0;
[...]
__builtin_memset(&likely_kern_bootargs, 0, 0x100);
var_620 = 0x100;
int32_t ret_sysctlbyname = _sysctlbyname("kern.bootargs", &likely_kern_bootargs, &var_620, 0, 0, zmm0_1);
int64_t is_ramrod_debug;
if (ret_sysctlbyname != 0)
{
_perror("sysctlbyname(kern.bootargs) -> 0");
}
else
{
is_ramrod_debug = _strstr(&likely_kern_bootargs, "ramrod_debug=1");
}
void** child_env;
if ((ret_sysctlbyname != 0 || (ret_sysctlbyname == 0 && is_ramrod_debug == 0)))
{
child_env = nullptr;
}
if ((ret_sysctlbyname == 0 && is_ramrod_debug != 0))
{
_fwrite("enabling debug malloc in child p…", 0x27, 1, *(uint64_t*)___stderrp);
child_env = &env_debug_malloc;
}
int32_t ret_posix_spawn;
int512_t zmm0_2;
[3] ret_posix_spawn = _posix_spawn(&child_pid, *(uint64_t*)argv, 0, 0, &child_argv, child_env);
[...]
}
[...]
}
At [1]
the code checks if the process is launched with just one argument, which corresponds to the pathname used to invoke the program.
If the program was invoked without any further arguments, the code at [3]
is reached. The code at [3]
calls _posix_spawn
using argv[0]
as the pathname for the executable and child_argv
as arguments [2].
Essentially, when the program is called without any arguments, another process is spawned, using argv[0]
for the pathname and -server
as additional argument.
Since argv[0]
can be arbitrarily set when executing ramrod
, it is possible to control which program is spawned at [3]
, inheriting all of ramrod
’s heritable entitlements. This will allow an attacker to execute an arbitrary binary with the com.apple.rootless.install.heritable
entitlement, which allows for arbitrary code execution without SIP restrictions.
Note that we only tested the ramrod
binary on an intel-based Mac.
2024-06-12 - Vendor Disclosure
2024-07-29 - Vendor Patch Release
2024-07-30 - Public Release
Discovered by Claudio Bozzato and Francesco Benvenuto of Cisco Talos.