|
Message-ID: <29fbd05d-f9aa-47aa-ade1-b61ab5d86135@gmail.com> Date: Mon, 9 Sep 2024 17:17:52 +0300 From: Dimitrios Glynos <dglynos.pub@...il.com> To: oss-security@...ts.openwall.com Subject: CVE-2024-6655 Library injection from CWD in GTK-2/GTK-3 Hello all, the GTK project is a free and open-source cross-platform widget toolkit for creating graphical user interfaces [1]. I found that applications based on GTK-3 or GTK-2 (aka GTK+3 / GTK+2) were vulnerable to library injection from the current working directory (CWD), whenever a GTK module was requested to be loaded but the module was missing from the standard paths. This issue is tracked as CVE-2024-6655 (assigned by RedHat). The GTK project issued a security fix [2] in version 3.24.43 of GTK-3. GTK-2 will not be receiving an official fix for this issue as it is no longer maintained. Software distributions that maintain GTK-3 packages (and software vendors that bundle their software with GTK-3) should make sure that they are using a version of GTK-3 with the fix applied. Backporting the fix to a GTK-2 codebase is also an option. Users are recommended to update GTK-2/GTK-3 libraries to the latest versions available and use extreme caution when starting vulnerable GTK applications from directories containing possibly untrusted content (e.g. ~/Downloads). While researching the exploitability of this issue, I found that both Debian and Ubuntu came with a GNOME configuration that could allow for remote (but victim-assisted) exploitation of the issue. For more information on this please see the "Exploitation" section of this advisory. Ubuntu published a fix for the GTK libraries on July 16 2024 covering all supported LTS versions of Ubuntu [3], while Debian made the GTK fix available in Debian 12.7 (stable) and Debian 11.11 (oldstable) on August 31st 2024 [4]. Technical Analysis ================== Any mention of the term "GTK" in this section shall refer to GTK-2/GTK-3. A GTK-based application will try to load a GTK module (a shared library) when: (a) the module is mentioned in specific environment variables (GTK2_MODULES/GTK_MODULES for GTK-2, and GTK3_MODULES/GTK_MODULES for GTK-3). (b) the module is passed as a command line parameter to the application (see --gtk-module parameter). (c) there is a runtime change in a GTK setting (or GDK screen setting) called "gtk-modules". The actual loading of a GTK module in all of the abovementioned scenarios occurs through load_module(). This function uses a utility function called find_module() to locate and load the module. An excerpt of find_module() is provided below from gtk+-3.24.42/gtk/gtkmodules.c: 210 static GModule * 211 find_module (const gchar *name) 212 { 213 GModule *module; 214 gchar *module_name; 215 216 module_name = _gtk_find_module (name, "modules"); 217 if (!module_name) 218 { 219 /* As last resort, try loading without an absolute path (using system 220 * library path) 221 */ 222 module_name = g_module_build_path (NULL, name); 223 } 224 225 module = g_module_open (module_name, G_MODULE_BIND_LOCAL | G_MODULE_BIND_LAZY); 226 227 if (_gtk_module_has_mixed_deps (module)) 228 { 229 g_warning ("GTK+ module %s cannot be loaded.\n" 230 "GTK+ 2.x symbols detected. Using GTK+ 2.x and GTK+ 3 in the same process is not supported.", module_name); 231 g_module_close (module); 232 module = NULL; In line 222 above, if the requested GTK module has not been found under the standard GTK module directories, then the module is searched for in the current working directory (notice the NULL argument to glib's g_module_build_path()). This opens the door to having malicious versions of missing modules be loaded from a directory with attacker-controlled content. Exploitation ============ This section presents two example exploitation scenarios that take advantage of the library injection issue. Both scenarios share the theme where a victim user was tricked into receiving malicious files in a certain directory, and then at a later point in time the victim user executes a vulnerable GTK application using that directory as the current working directory (CWD). The vulnerable GTK application will automatically load the malicious files and the remote attacker will thus gain code execution on the victim's host. Receiving the malicious files could be performed in various ways, including: - a "drive-by download" where the user is lured into visiting a malicious website, the website automatically downloads content to "~/Downloads" and "~/Downloads" later becomes the CWD. - a more generic reception of content from an untrusted source, where the malicious files are placed alongside innocuous files in an archive file or torrent file and where the extracted directory later becomes the CWD. I am aware of the following methods to force a specific directory to be the CWD of a GTK application: - the user is executing the GTK application from the command line (e.g. "cd Downloads; eog ."). - the user has double-clicked on the GTK application executable through GNOME's file manager (nautilus), an action which is very common for AppImage bundled software. - a random event where the user first executed nautilus through the terminal (e.g. "cd zip-files; nautilus ."). Any later (GUI) invocation of this GTK-based file manager in the desktop session will continue to use the same CWD and will further apply this CWD to utility programs used with the files (like performing decompression with GNOME's GTK-based file-roller). (If anyone has other ideas on how to force a specific directory to become the CWD of GTK applications or of a specific GTK application, I would be interested to know.) The rest of this section describes the two example exploitation scenarios. Case #1. Running GTK-3 applications on Debian and Debian-derived distributions ------------------------------------------------------------------------------ Debian and Debian-derived distributions (including Ubuntu) push an environment variable GTK_MODULES="gail:atk-bridge" through the "libatk-adaptor" package, a core GNOME package. The variable mentions two modules that are meant for GTK-2 applications and in fact, GTK-3 carries a blacklisting (but bypassable) mechanism so that these modules would not be taken into consideration for module loading purposes. The modules are there in their standard directories for GTK-2 applications to use, but would normally appear as non-existent to a GTK-3 application. As previously mentioned, GTK-3 has a blacklisting mechanism for the "gail" and "atk-bridge" components. In load_module() there is a call to module_is_blacklisted(). The blacklist is implemented by not invoking the gtk_module_init() function of the blacklisted modules. However, just before checking the blacklist, each module is already loaded into memory by find_module() in gtk+-3.24.42/gtk/gtkmodule.c:292. 263 static GSList * 264 load_module (GSList *module_list, 265 const gchar *name) 266 { 267 GtkModuleInitFunc modinit_func; 268 gpointer modinit_func_ptr; 269 GtkModuleInfo *info = NULL; 270 GModule *module = NULL; 271 GSList *l; 272 gboolean success = FALSE; 273 274 if (g_module_supported ()) 275 { 276 for (l = gtk_modules; l; l = l->next) ... 290 if (!success) 291 { 292 module = find_module (name); 293 294 if (module) 295 { 296 /* Do the check this late so we only warn about existing modules, 297 * not old modules that are still in the modules path. */ 298 if (module_is_blacklisted (name, TRUE)) 299 { 300 modinit_func = NULL; 301 success = TRUE; 302 } 303 else if (g_module_symbol (module, "gtk_module_init", &modinit_func_ptr)) ... As we saw previously, find_module() calls glib's g_module_open() to dlopen() the shared library requested. Although gtk_module_init() is not called on a blacklisted module, the fact that the module has been loaded into memory means that any (malicious) code found in library constructors will be executed. Therefore this mechanism does not work as a true blacklist for all types of modules in GTK-3, just for well behaving ones. Now that we have a way to escape the blacklist, we will need to overcome one more obstacle. In glibc's dlopen(3) implementation, simply providing the filename of a library (without any path elements) does not by default load the library from the current working directory (as other things need also be tweaked like LD_LIBRARY_PATH etc.). glibc would have been happy to do that if we had provided "./libfoo.so" (i.e. a relative path with a slash character). This means that find_module()'s call to glib's g_module_open() will always fail for a "libfoo.so" library. However, if we take a look at the implementation of g_module_open() in glib-2.80.3/gmodule/gmodule.c we have: 704 GModule * 705 g_module_open (const gchar *file_name, 706 GModuleFlags flags) 707 { 708 return g_module_open_full (file_name, flags, NULL); 709 } 465 GModule* 466 g_module_open_full (const gchar *file_name, 467 GModuleFlags flags, 468 GError **error) 469 { 470 GModule *module; 471 gpointer handle = NULL; 472 gchar *name = NULL; ... 526 /* try completing file name with standard library suffix */ 527 if (!name) 528 { 529 char *basename, *dirname; 530 size_t prefix_idx = 0, suffix_idx = 0; 531 const char *prefixes[2] = {0}, *suffixes[2] = {0}; 532 533 basename = g_path_get_basename (file_name); 534 dirname = g_path_get_dirname (file_name); ... 562 if (!g_str_has_suffix (basename, ".so")) 563 suffixes[suffix_idx++] = ".so"; ... 566 for (guint i = 0; i < prefix_idx; i++) 567 { 568 for (guint j = 0; j < suffix_idx; j++) 569 { 570 name = g_strconcat (dirname, G_DIR_SEPARATOR_S, prefixes[i], 571 basename, suffixes[j], NULL); 572 if (g_file_test (name, G_FILE_TEST_IS_REGULAR)) 573 goto name_found; 574 g_free (name); 575 name = NULL; 576 } 577 } 578 name_found: 579 g_free (basename); 580 g_free (dirname); 581 } 582 /* try completing by appending libtool suffix */ 583 if (!name) 584 { 585 name = g_strconcat (file_name, ".la", NULL); 586 if (!g_file_test (name, G_FILE_TEST_IS_REGULAR)) 587 { 588 g_free (name); 589 name = NULL; 590 } 591 } ... The branch in line 583 makes the application search for a similarly named ".la" libtool file when the library file requested is not found on disk. Therefore, it is possible to craft this file (say "libfoo.so.la") to point to a completely different library (say "libx.so") at so-called libdir ".". The parsing mechanism for ".la" files will be happy to synthesize the "./libx.so" path for us and this will eventually be fed to dlopen(3) to load the "libx.so" module from the current working directory. As a proof of concept, have a look at the first two attachments. The first one ("libtool.txt") is a malicious libtool file (based on "libtiff"). You may rename this file to "libatk-bridge.so.la". The second file ("foo.c") is the source code of the malicious module. Its purpose is to print "HELLO!" to standard error. You may compile this file based on the instructions found in the file. Place both "libfoo.so" and "libatk-bridge.so.la" in a directory. Within that directory, try to start the GTK-3 based firefox on an unpatched Debian (or Debian-derived) system and you should see a "HELLO!" message printed to standard error. $ cd directory $ ls libatk-bridge.so.la libfoo.so $ firefox HELLO! Gtk-Message: 16:13:05.585: Not loading module "atk-bridge": The functionality is provided by GTK natively. Please try to not load it. Please note that we can substitute firefox with any other GTK-3 based application and the effects will remain the same. Case #2: An application bundled with GTK-3, looking for a non-existent module ----------------------------------------------------------------------------- Let's consider the case of an application that has been bundled with a vulnerable version of GTK-3 and is missing a GTK module. For our example we will use the latest (1.3.2) Inkscape AppImage build [5] which is missing the "canberra-gtk-module" GTK module. We will place a malicious "libcanberra-gtk-module.so.la" and a "libfoo.so" in the "Downloads" directory to simulate a drive-by download attack, and then we will trigger the application from the GNOME file manager's GUI. Start by renaming the first attachment ("libtool.txt") to "libcanberra-gtk-module.so.la" and place it in the "Downloads" directory. Then, use the third attachment ("foo2.c") to build a malicious GTK module that will spawn a GNOME calculator. You can build this based on the instructions provided in the source code and make sure to place "libfoo.so" in the "Downloads" directory. Use a browser to download the Inkscape AppImage in the "Downloads" directory. Then use the GNOME file manager to make the AppImage file executable. Double click on the AppImage file and you should see two GNOME calculators on your desktop (due to multiple requests for "canberra-gtk-module"). Please note that this is a GUI-only attack scenario involving no use of the terminal by the victim user. Issue Timeline ============== 2024-06-14 Issue reported to GTK team through GNOME Security [6] 2024-07-10 Red Hat assigns CVE-2024-6655 to the issue 2024-07-10 GTK project releases the fix in gtk+-3.24.43 2024-07-16 Ubuntu releases updated GTK-2/GTK-3 packages for all LTS versions 2024-08-31 Debian releases versions 12.7 and 11.11 with fixes for GTK-2/GTK-3 2024-09-09 Advisory post on OSS Security Thanks ====== Many thanks to Red Hat, the GTK developers, the Ubuntu Security Team, the Debian Security Team and the GTK package maintainers for the work they put into tracking the issue and releasing the fix. I remain available for any further information required on the issue. Kind regards, Dimitrios Glynos [1] https://www.gtk.org [2] https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/7361/diffs [3] https://ubuntu.com/security/notices/USN-6899-1 [4] https://security-tracker.debian.org/tracker/CVE-2024-6655 [5] https://inkscape.org/release/inkscape-1.3.2/gnulinux/appimage/dl/ [6] https://gitlab.gnome.org/GNOME/gtk/-/issues/6786 View attachment "libtool.txt" of type "text/plain" (901 bytes) View attachment "foo.c" of type "text/x-csrc" (193 bytes) View attachment "foo2.c" of type "text/x-csrc" (253 bytes) Download attachment "OpenPGP_0xE7DBE3A48B1E3ED2.asc" of type "application/pgp-keys" (2465 bytes) Download attachment "OpenPGP_signature.asc" of type "application/pgp-signature" (666 bytes)
Powered by blists - more mailing lists
Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.