oss-sec mailing list archives
ktexteditor / Kate local privilege escalation
From: Matthias Gerstner <mgerstner () suse de>
Date: Tue, 24 Apr 2018 13:11:09 +0200
Hello list, following is a report about a local privilege escalation I found in ktexteditor. I just informed upstream about it and will obtain a CVE soon. ktexteditor (https://api.kde.org/frameworks/ktexteditor/html/) provides a text editor component for KDE applications. It is, for example, used in the "kate" text editor program. One of ktexteditor's features is support to write files owned by root or other users after entering the root password via polkit authentication. The authentication part is handled via the "kauth" framework. The actual work of saving files on behalf of the authenticated user is performed by a small program named "kauth_ktexteditor_helper". The related code is found in the upstream repository in the following source files: src/buffer/katesecuretextbuffer_p.h src/buffer/katesecuretextbuffer.cpp The logic for saving the file goes roughly as follows: - the caller provides source and target file paths, a sha512 digest of the source file content and (optionally) the target file owner and group IDs. - the helper tries to open a temporary file in the directory containing the target file, reads chunk from the source file and writes them to the target file, recalculating the sha512 digest on the way. - in the end, if the digest matches, the temporary file will be rename()'d for replacing the target file path with the new file content. For temporary file handling the qt5 core library facilities are employed and the following source code lines are the important ones: ``` // We will first generate temporary filename and then use it relatively to prevent an attacker // to trick us to write contents to a different file by changing underlying directory. QTemporaryFile tempFile(targetFileName); if (!tempFile.open()) { return false; } tempFile.close(); QString tempFileName = QFileInfo(tempFile).fileName(); tempFile.setFileName(tempFileName); if (!readFile.open(QIODevice::ReadOnly) || !tempFile.open()) { return false; } ``` This code results in the following system call sequence: ``` openat(AT_FDCWD, "/etc", O_RDWR|O_CLOEXEC|O_TMPFILE, 0600) = 11 lseek(11, 0, SEEK_SET) = 0 linkat(AT_FDCWD, "/proc/self/fd/11", AT_FDCWD, "/etc/fstab.hdRIFU", AT_SYMLINK_FOLLOW) = 0 close(11) = 0 [...] openat(AT_FDCWD, "fstab.hdRIFU", O_RDWR|O_CREAT|O_CLOEXEC, 0666) = 12 ``` So while the code author(s) have been seemingly aware that temporary file handling needs to be done carefully they somehow still broke it in the end which we can see from the system calls. The initially unnamed temporary file is linked, the associated file descriptor closed and the now named temporary file is reopened with the `O_CREAT` flag. On file systems that don't support O_TMPFILE the vulnerability is also existing, the code will fall back to named files right away. As it turns out this situation can be exploited in scenarios like the following: A user running "kate" wants to edit and save a file in a directory that is owned by another unprivileged user. Such directories exist for various software e.g. in /var/lib or in /etc. The user enters root credentials to perform the privileged save operation. The other unprivileged user can now perform a symlink attack on the temporary file being opened by the "kauth_ktexteditor_helper" and achieve various effects: - creation of new files in arbitrary file system locations. - corruption of arbitrary existing files (because the helper will write the source file content into the symlinked file). - taking ownership of arbitrary files (because the helper will perform an fchown() call on the symlinked file), thereby facilitating local root privilege escalation. The attached proof of concept code succeeds in gaining ownership of /etc/shadow in the described situation. Exploiting the race condition does not work very reliably, because the window of opportunity is very small. Some more advanced exploit code might improve the chances. The API of the QTemporaryFile class has some non-obvious semantics. The close() method does not really close the underlying file descriptor. The setFileName() function, however, does. I am not clear what the original intentions of the code above being this way might have been. As far as I can tell the attached patch fixes the race condition detailed in this report without breaking anything. The vulnerable code is already found in upstream commit f7a9573d973e6ef0cd6f2c419290c0c7e46381b7 and therefore ktexteditor starting from version 5.34.0 can be considered vulnerable. Cheers Matthias -- Matthias Gerstner <matthias.gerstner () suse de> Dipl.-Wirtsch.-Inf. (FH), Security Engineer https://www.suse.com/security Telefon: +49 911 740 53 290 GPG Key ID: 0x14C405C971923553 SUSE Linux GmbH GF: Felix Imendörffer, Jane Smithard, Graham Norton HRB 21284 (AG Nuernberg)
Attachment:
ktexteditor_tmpfile_fix.patch
Description:
Attachment:
kattack.cpp
Description:
Attachment:
signature.asc
Description: Digital signature
Current thread:
- ktexteditor / Kate local privilege escalation Matthias Gerstner (Apr 24)
- Re: ktexteditor / Kate local privilege escalation (CVE-2018-10361) Matthias Gerstner (Apr 25)