oss-sec mailing list archives

CVE-2022-24986: KCron: Insecure temporary file handling


From: Carlos López <clopez () suse de>
Date: Fri, 25 Feb 2022 12:32:37 +0100

Hello list,

Find below our report for CVE-2022-24986: Insecure temporary file handling in KDE KCron <= 21.12.2.

# 0. Overview

KCron [0] is a graphical frontend to the classical cron utility for the KDE desktop environment. In order to perform elevated tasks, such as modifying the system-wide crontab at `/etc/crontab`, a privileged helper program is used. Communication (via dbus) and authorization against the helper (via Polkit) is abstracted away with the KDE Kauth framework.

# 1. Analysis

The client (unprivileged) code is mainly contained in `src/crontablib/ctcron.cpp:CTCron::save()` and `src/crontablib/ctSystemCron:CTSystemCron::CTSystemCron()`. The client side has two different modes of operation:

- Saving content of the user-specific crontab. This is done via the `/usr/bin/crontab` setuid-root binary. - Saving content of the system-wide crontab. This is done via invocation of the privileged privileged helper program on D-Bus (`src/helper/kcronhelper.cpp`).

In both cases a temporary file is created with the new contents and it is passed on to the respective mechanism. These temporary files are created under /tmp using unpredictable file names of the format `systemsettings.XXXXXX` with mode `0644`, and are thus world-readable

The first mode of operation is affected by an information leak, and the second one by a potential privilege escalation.

## 1.1. Information leak due to improper file permissions

When temporary files are created, the mode passed to the open call is `0666`, which causes the file to be world-readable.

```
openat(AT_FDCWD, "/tmp/systemsettings.jhcCtT", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 15
```

Since created temporary files are world-readable, other users in the system might gain knowledge about other users' crontabs. The severity of this leak depends on the actual contents of the user specific crontab. The system wide crontab is usually installed as world-readable, so the leak does not apply there by default (but could still be an issue if `/etc/crontab` is hardened, and thus not meant to be read). Users could place passwords in their crontabs via environment variables, or the contents could be hints for further attack vectors.

## 1.2. Privilege escalation due to filename reuse

The name for the temporary file holding user changes is obtained once and reused each time the user saves crontab content. This means that, after the first time the temporary file is written, other local users in the system have knowledge of the used filename, and can stage attacks if the user should reuse the same instance of the tool.

```
CTCron::CTCron(...)
{
    ...
    QTemporaryFile tmp;
    tmp.open();
    d->tmpFileName = tmp.fileName(); // [A]
    ...
}

CTSaveStatus CTCron::save()
{
    bool saveStatus = saveToFile(d->tmpFileName); // [B]
    ...
    QFile::remove(d->tmpFileName); // [C]
    ...
}

bool CTCron::saveToFile(const QString &fileName)
{
    QFile file(fileName);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
        ...
    ...
}
```

The file name is obtained once in [A], when the CTCron object is instantiated, and reused in [B] every time the user saves their changes. The file is then removed explicitly in [C].

Temporary files are opened without passing the `O_EXCL` or `O_NOFOLLOW` flags, which can be exploited in two ways.

First, if `/proc/sys/fs/protected_symlinks` is disabled (see the manual page for proc(5)), the absence of `O_NOFOLLOW` means that the application will happily follow any symlink. Note that this protection is disabled by default for the vanilla kernel, but it is enabled by most distributions.

This can be exploited as follows:

- An attacker creates a world-writable directory under its own control and prepares a symlink attack into this directory:
  1. `mkdir -m 777 /tmp/evil-directory`
  2. `ln -s /tmp/evil-directory/evil-file /tmp/systemsettings.abcdef`
- When KCron re-creates the temporary file, the call will succeed, because the target file does not yet exist. - After the file has been created in `/tmp/evil-directory`, the attacker can remove the `/tmp/evil-directory/evil-file`, as they have write permissions in the directory. After that, a new file under the attacker's control is created in its stead that contains malicous data. This is the race condition that needs to be won. - Once either the KAuth helper or the crontab binary is invoked, the malicious data is placed as the new crontab content, allowing arbitrary code execution as the victim user (or as an arbitrary user in the case of the system crontab).

Second, if `/proc/sys/fs/protected_regular` is disabled, the absence of `O_EXCL` means that the call to `openat()` will not fail if the file already exists and is owned by a different user. In this case, the attacker does not even need to set up a symlink, as they can simply create a file with the appropiate name and world-writeable permissions. Once KCron is done writing to the temporary file, but before it is copied to its destination, the attacker can modify its contents, as the file is still under their control.

This all stems from a misuse of the `QTemporaryFile` object of the Qt framework. The abstractions this class provides make its use fairly non-obvious, as we already noted in the past [1].

# 2. Other minor issues

During our analysis we noticed other minor issues that do not pose an immediate security threat, but that we communicated to the maintainer nonetheless. These have since been addressed through upstream patches.

 - The helper's error handling is incomplete, only warnings will be logged, but neither is the operation aborted on errors nor are errors propagated back to the caller. No proper polkit authentication dialog with the active source/target arguments is displayed.  - The helper's functionality is not limited to saving a crontab file, as it accepts source and destination arguments (i.e. an arbitrary file move scheme). If the helper were improperly parametrized, it could be used for arbitrary local root exploits. In the past we already identified issues with overly generic D-Bus file system APIs which can lead to security bugs [2].  - There is an additional DoS vector due to file name reuse, as other users might place files at the expected temporary locations. This breaks the application, as it will not be able to create said temporary files. Of course, this is not an issue for a privileged user, which can remove the malicious files, and the option to update the crontab manually via CLI is still available, but it would defeat the purpose of the GUI program.

The referenced upstream patch to fix the issues [3] still contains a problem. With the patch the source (temporary) file under user control is `chmod()`ed by the helper program [4] to give it the right permissions for moving it to `/etc/crontab`. Our suggestion is to to overwrite the destination file instead. An upstream merge request is currently in the works to address this [5].

# 3. Timeline

2022-01-26 - Issues reported to upstream
2022-01-31 - Upstream starts working on fixes
2022-02-02 - Embargo date set to 2022-02-16
2022-02-04 - Patch proposal from maintainer
2022-02-15 - Patch pushed to upstream repository
2022-02-16 - KDE report goes public [6]

# 4. References

[0] https://invent.kde.org/system/kcron
[1] https://www.openwall.com/lists/oss-security/2018/04/24/1
[2] https://www.openwall.com/lists/oss-security/2019/07/09/3
[3] https://invent.kde.org/system/kcron/-/commit/ef4266e3d5ea741c4d4f442a2cb12a317d7502a1 [4] https://invent.kde.org/system/kcron/-/commit/ef4266e3d5ea741c4d4f442a2cb12a317d7502a1#87f74e7efe41bc6f7cbe1f834b88b2e31889c804_47_47
[5] https://invent.kde.org/system/kcron/-/merge_requests/14
[6] https://kde.org/info/security/advisory-20220216-1.txt

Best regards,

Carlos

--
Carlos López
Jr. Security Engineer
SUSE Software Solutions


Current thread: