Bash Privileged-Mode Vulnerabilities in Parallels Desktop and CDPATH Handling in MacOS
CVE-2023-27322 - Local Privilege Escalation Through Parallels Service
First a little bit of background about bash and privileged mode. Normally the Bash shell will drop privileges when it is started under a setuid/setgid situation. It will set the effective user/group identifiers to that of the real identifiers. Privileged mode will retain the (presumably) more privileged effective identifiers, but lock itself down a little to limit the degree of control the original user has over Bash’s execution. It won’t process the $ENV
or $BASH_ENV
files, shell functions won’t be inherited from the environments.
The interesting thing here is how privileges change across child processes. We start with the original Parallels Service, which is a setuid and setgid binary. It will execute an embeded script within a non-interactive Bash shell. To do so it makes a call to setuid(geteuid())
setting the real uid to the effective uid (root), then it calls fork()
and execv
to spawn a bash shell. Importantly here execv
wraps execve
passing in the current (attacker controlled) environment to the newly spawned process. This newly spawned process, despite the earlier setuid
binary will be detected as a setgid
execution, prompted Bash to automatically drop its privileges and ignore processing the environment variables. At this stage bash is properly dropping its privileged and not allowing the original user to influence execution.
However, the script continues, it spawns its own child process a watchdog
script. Whats interesting this time is that because Bash “dropped” the privileges, when this nested script executes, its process will be called with the effective and real user/group identifiers matching, so Bash won’t know to restrict itself. This nested call, will trust the environment values and so an attacker could provide malicious shell functions that would be inherited not by the first embedded script but by the further nested watchdog script.
The post also covers a couple more bugs, one with prl_update-helper
invoking the inittool
script, this one is a bit more direct. It is (presumably) not a setgid binary, and before invoking the script it uses a setuid(geteuid())
call, so it runs as root and does not restrict itself. Similarly inittool2
executable is invoked from the inittool
script. inittool2
will fork a child process to execute an embedded script which can be abused.