GPGTools installerHelper - setuid Local Privilege Escalation
Tracking and CVE:
Affected version(s):
- < GPGTools 2014.12-b4
Fixed version:
- GPGTools 2015.06
- GPGTools 2015.08 - vulnerability disclosed (Release Notes)
Vulnerability:
GPGTools ships with a set-setuid installHelper binary:
$ ls -l /Library/Frameworks/Libmacgpg.framework/Versions/Current/Resources/
-rw-r--r-- 1 root wheel 1252 Oct 23 2013 Info.plist
-rw-r--r-- 1 root wheel 407 Oct 23 2013 Keyservers.plist
drwxr-xr-x 3 root wheel 102 Aug 15 13:06 de.lproj
drwxr-xr-x 3 root wheel 102 Aug 15 13:06 en.lproj
-rwsr-xr-x 1 root wheel 68576 Oct 23 2013 installerHelper
-rw-r--r-- 1 root wheel 550 Oct 23 2013 org.gpgtools.Libmacgpg.xpc.plist
drwxr-xr-x 3 root wheel 102 Aug 15 13:06 pinentry-mac.app
installHelper is used to install signed pkg-files as a normal user without root privileges:
$ /Library/Frameworks/Libmacgpg.framework/Versions/Current/Resources/installerHelper
Usage: installerHelper pkg-file [xml-file]
This tool checks the signature of an pkg file and installs it.
You can specify a xml-file to override the standard choices.
installHelper installs pkg-files by passing
the pkg-file path and the xml-file path into the installPackage
function:
BOOL installPackage(NSString *pkgPath, NSString *xmlPath) {
// Run the installer command.
NSString *commandString;
if (xmlPath) {
commandString = [NSString stringWithFormat:@"/usr/sbin/installer -applyChoiceChangesXML \"%@\" -pkg \"%@\" -target /", xmlPath, pkgPath];
}
else {
commandString = [NSString stringWithFormat:@"/usr/sbin/installer -pkg \"%@\" -target /", pkgPath];
}
const char *command = [commandString UTF8String];
uid_t uid = getuid();
int result = setuid(0);
if (result == 0) {
//Run only this command with root privileges.
result = system(command);
setuid(uid);
} else {
printf("This tool needs the setuid-bit to be set and the owner must be root!\nStart a normal installation using the GUI.\n");
commandString = [NSString stringWithFormat:@"/usr/bin/open -Wnb com.apple.installer \"%@\"", pkgPath];
command = [commandString UTF8String];
result = system(command);
}
return !!result;
}
installPackage
directly passed the two paths into the system
function and therefore is vulnerable to command injection.
Exploitation / Proof of Concept:
The PoC makes use of the command substitution (`command`
) applied by system
to injection user-defined commands.
Before installPackage
is called several requirements need to be fulfilled (see here):
- the pkg-file must exist
- the pkg-file must be signed with GPGTools’s key
- if passed the xml-file must exist
A signed pkg-file (Install.pkg
) can be obtained by downloading an older release of GPGTools.
Versions after 2014.12-b4 won’t work, because the signing key has been replaced to prevent downgrading of fixed installHelper versions to vulnerable ones.
After fulfilling the pkg-file requirements we want to inject commands via the xml-file path.
Therefore we create a file called `dummy`
and pass it as xml-file to installHelper.
installHelper will accept it as a valid path and the command dummy
will get executed with elevated rights.
So if we put an executable file named dummy
into $PATH
it will get executed.
For further details check out the full PoC: CVE-2014-4677.sh