Vulnerability DatabaseGHSA-hg64-r575-qhjj

GHSA-hg64-r575-qhjj: 
Python vulnerability analysis and mitigation

Summary

Five holes let an attacker run code or read files, esp. on Linux/BSD/Unix (unsupported OSes for the project, but widely used): another user on the same machine can plant code in music21's shared cache folder or a /tmp settings file; titles and lyrics written to LilyPond can run code; a LilyPond shell command breaks on crafted paths; Clercq-Temperley data is opened as a file path.

Details

  1. Shared cache folder (Linux etc.). Environment.getRootTempDir (environment.py) uses /tmp/music21, or /tmp/music21-userid-<uid>, if it exists and is readable and writable, without checking who owns it. Cached parses are pickles with predictable names (m21-<version>-py3.X-<md5 of source path>.p.gz), loaded whenever newer than the source file (PickleFilter, converter/init.py). A planted pickle runs code; a planted symlink makes the cache write overwrite the link's target; a planted download is returned by parse(url). Cache files are also world-readable (0644). Fix: per-user folder created with mode 0700; refuse it if owned by someone else or writable by others; write cache files to a temp name and rename into place.
  2. Settings file in /tmp. When HOME is unset or not writable, which is typical for web-server accounts, Environment.getSettingsPath (environment.py) reads /tmp/.music21rc. Any local user can create that file and set lilypondPath, musescoreDirectPNGPath, etc. to their own program. Fix: never fall back to /tmp; skip the settings file when HOME is unusable.
  3. LilyPond code injection. LyObject.quoteString (lily/lilyObjects.py) escapes " but not \, so a title ending a string with \" escapes it; lyrics (lily/translate.py, lyricToLy area) are not escaped at all. LilyPond runs Scheme code (#(system ...)) found outside strings, and music21 runs LilyPond itself for write('lily.pdf'|'lily.png'|'lily.svg'). LilyPond 2.24 removed its old safe mode (-dsafe), which used to block such code, so it cannot be used as a guard. Fix: escape \ then " in quoteString and use it for lyrics.
  4. Shell injection. LilypondConverter.runThroughLily (lily/translate.py) builds a command string with the output path unquoted and runs it with os.system. Fix: subprocess.run([...]) with an argument list.
  5. Clercq-Temperley data read as a file path. CTSong.parse (romanText/clercqTemperley.py) opens any string lacking both | and S: as a file, so parseData(text, format='cttxt') and parse('clercqTemperley: ' + text) read local files, and the error message reveals whether a file exists. parseData was intended to be the safe way of ensuring that files and URLs would never be read. Fix: parseData never opens files; parseFile reads the file and passes the text.

Proof of concept

  1. As user A on a shared Linux machine, before user B first uses music21:
import hashlib, os, pickle, sys, zlib
import music21
os.makedirs('/tmp/music21', exist_ok=True); os.chmod('/tmp/music21', 0o777)
class P:
    def __reduce__(self):
        return (os.system, ('id > /tmp/ran_as_B',))
target = os.path.join(music21.__path__[0], 'corpus', 'bach', 'bwv66.6.mxl')  # file B will parse
py = f'py{sys.version_info.major}.{sys.version_info.minor}'
name = f'm21-{music21.__version__}-{py}-{hashlib.md5(target.encode()).hexdigest()}.p.gz'
open('/tmp/music21/' + name, 'wb').write(zlib.compress(pickle.dumps(P())))

Then user B runs corpus.parse('bach/bwv66.6'), and A's command runs as B. 2. As user A: create /tmp/.music21rc containing <settings encoding="utf-8"><preference name="musescoreDirectPNGPath" value="/tmp/evil.sh" /></settings>. A service running as an account with no usable HOME then calls s.write('musicxml.png'), and /tmp/evil.sh runs. 3.

from music21 import converter
s = converter.parse('tinyNotation: 4/4 c4 d e f')
s.recurse().notes.first().lyric = 'la" #(system "touch /tmp/PWNED") "'
s.write('lily', fp='x.ly')   # x.ly contains:  "la" #(system "touch /tmp/PWNED") ""
s.write('lily.pdf')          # LilyPond runs the command

A title of \" #(system "touch /tmp/PWNED") % does the same. 4. s.write('lily.pdf', fp='/tmp/x;touch PWNED;#.pdf') runs touch PWNED through the shell. 5. converter.parseData('/home/victim/song.cttxt', format='cttxt') returns the file's chords. parseData('/etc/passwd', format='cttxt') fails with a parse error, while a missing file fails with "Cannot find file", so an attacker learns which files exist.

Impact

1-2: Linux and other UNIX systems where users share /tmp, such as multi-user servers and shared JupyterHub. Another local user can run code as anyone who uses music21 there. Windows and macOS, are not affected (each user gets a private temp folder). (Linux is not and has never been officially supported; nonetheless, many users run music21 on Linux/UNIX). 3-4: apps that render uploaded or user-edited scores through LilyPond (PDF/PNG/SVG). Remote code execution on the server. 5: apps that pass user text to parse() or parseData(). Reads local Clercq-Temperley files and reveals whether any file exists.


Source: NVD

Related Python vulnerabilities:

CVE ID

Severity

Score

Technologies

Component name

CISA KEV exploit

Has fix

Published date

CVE-2026-59177HIGH8.8
  • Python logoPython
  • esphome-device-builder
NoYesOct 28, 2026
GHSA-hg64-r575-qhjjHIGH8.1
  • Python logoPython
  • music21
NoYesOct 28, 2026
GHSA-6r57-3vhv-2wpfHIGH7.5
  • Python logoPython
  • clawmetry
NoYesOct 28, 2026
GHSA-pc58-q9c8-wh7jHIGH7.1
  • Python logoPython
  • omero-web
NoYesOct 28, 2026
GHSA-xj2r-hpg6-375cMEDIUM5.3
  • Python logoPython
  • ckan
NoYesOct 28, 2026

Free Vulnerability Assessment

Benchmark your Cloud Security Posture

Evaluate your cloud security practices across 9 security domains to benchmark your risk level and identify gaps in your defenses.

Request assessment

Additional Wiz resources

Get a personalized demo

Ready to see Wiz in action?

"Best User Experience I have ever seen, provides full visibility to cloud workloads."
David EstlickCISO
"Wiz provides a single pane of glass to see what is going on in our cloud environments."
Adam FletcherChief Security Officer
"We know that if Wiz identifies something as critical, it actually is."
Greg PoniatowskiHead of Threat and Vulnerability Management