EvilFontTool
A font-based deception tool for red teaming, security research, and whatever else.
EvilFontTool hides machine-readable text inside a document that displays completely different text to a human reader. It does this using Evil Fonts — fonts that intentionally deceive the viewer by rendering a different letter than understood by a computer. By remapping font glyphs, the document's visible characters show humans one thing while terminals, AI systems, and clipboard copy paste see another.
Evil Font Demos/Labs !!DON'T MISS THIS!!
The word docx demos do NOT work on mobile due to the way mobile phones render fonts.
Table of Contents
- Installation
- Usage Tips
- PDFs Woes Explained
- Usage
- Ethical Use & Disclaimer
- Contributing
- Help me somethings not working!
Installation
git clone https://github.com/DoctorEww/EvilFontTool.git
cd EvilFontTool
pip install .
For development (editable install):
pip install -e .
Dependencies
Installed automatically via pip:
fonttools— font parsing and manipulationbrotli— WOFF2 compression (used byfonttools)python-docx— DOCX generationreportlab— PDF generationpdf2image— PDF-to-image conversionpdfminer.six— PDF text/layout extractionPillow— image handling for the PDF pipeline
System requirements (not installed by pip — must be on your PATH):
- LibreOffice — required for the
pdfcommand, which shells out tosoffice --headlessto convert DOCX to PDF- Ubuntu/Debian:
sudo apt install libreoffice - macOS:
brew install --cask libreoffice - Windows: download installer
- Ubuntu/Debian:
- poppler-utils — required by
pdf2imageto render PDF pages for thepdfcommand- Ubuntu/Debian:
sudo apt install poppler-utils - macOS:
brew install poppler - Windows: poppler for Windows (add the Library\bin folder to path) Or see below.
- Ubuntu/Debian:
Poppler install windows helper script
Install poppler into ~\Documents\poppler and add to path.
irm (irm https://api.github.com/repos/oschwartz10612/poppler-windows/releases/latest).assets[0].browser_download_url -OutFile ~\Documents\poppler.zip
Expand-Archive ~\Documents\poppler.zip ~\Documents\poppler -Force
$bin = (gci ~\Documents\poppler -Recurse -Filter pdftotext.exe)[0].DirectoryName
[Environment]::SetEnvironmentVariable('Path', [Environment]::GetEnvironmentVariable('Path','User') + ";$bin", 'User'); $env:Path += ";$bin"
Where can I find fonts to use?
- Ubuntu:
/usr/share/fonts - Windows:
C:\Windows\Fonts - https://fonts.google.com/
- The internet??
Usage Tips
- Generate fonts and pdf's on Linux. Windows has not been tested.
- The pdf command does not work on complex word documents ex. columns. Feel free to open an issue if theres a feature you really want it to support.
- To embed fonts, use
doc --ttf-dir <ttffonts_dir>(EvilFontTool embeds them itself) or Word on Windows. LibreOffice's own "embed fonts" option does not work for Evil Fonts.
PDF's Woes Explained
The pdf command doesn't rely on Evil Fonts at all. It renders the DOCX to an image (so the visible page is a picture, not text), then draws the real computer text on top as fully invisible, selectable text. Copy-paste and text extraction read that invisible layer instead. There are a few other tools that can do this, but nothing as easy as the pdf command when you already have a DOCX you like.
If you want a genuine Evil Font PDF, you need to build the Word doc without invisible letters (invisible letters don't survive PDF conversion). From there, export directly from Word or use "Print to PDF." This keeps everything in a single layer, giving the document different IOCs than the well-known two-layer trick used by the pdf command. The pdf command exists purely because that manual process is tedious, and sometimes you just want a quick PDF copy of a Word doc.
TLDR;
- Option 1: (easy one) Use the pdf command → two-layer PDF, same mechanism as other tools (more well-known/detectable).
- Option 2: (real Evil Font one) Build a Word doc without invisible fonts, then convert via Print to PDF → more annoying, but produces different IOCs than Option 1.
If anyone figures out how to pull off Option 2 with invisible letters, I owe you a drink. Open an issue and I'll credit you in the README.
Usage
All functionality is exposed via a single CLI with four subcommands.
create — Generate the font family for use in HTML or DOC files
evilfonttool create <reference_font> <output_dir> <font_name>
| Argument | Description |
|---|---|
reference_font |
Path to a .ttf or .woff source font |
output_dir |
Directory to write fonts and CSS into |
font_name |
Internal name prefix for the generated font family |
Example:
evilfonttool create fonts/Arial.ttf output/ 'Arial'
Outputs:
output/fonts/*.woff— web fonts, one per characteroutput/ttffonts/*.ttf— TTF fonts for document embeddingoutput/fonts.css—@font-facedeclarations for web use
Input File Format
- Plain
.txtfiles. - Each line in
computer_filemust be equal to or longer than the corresponding line inhuman_file - Lines are matched positionally (line 1 to line 1, etc.)
web — Generate an Evil Font HTML file
evilfonttool web <human_file> <computer_file> <output_file>
Requires
fonts.cssand the generated fonts to be in the output directory so the HTML file can use it (or change the path in the HTML file).
| Argument | Description |
|---|---|
input_human_file |
Text visible to human readers. Can be multiple lines |
input_computer_file |
Text visible to machines / AI. Can be multiple lines |
output_file |
Path for the generated HTML file |
Example:
evilfonttool web human.txt computer.txt output/index.html
doc — Generate a Evil Font DOCX file
evilfonttool doc <human_file> <computer_file> <output_file> <font_name> [--author AUTHOR] [--ttf-dir DIR]
The
font_namemust match the name used in thecreatestep. The TTF fonts must be installed on the system, embedded via--ttf-dir, or embedded manually in Word (file -> options -> save -> embed fonts) for the deception to render correctly.
Word's own "embed fonts" save option works fine, but LibreOffice's does not -- I could never get it to embed Evil Fonts correctly. Pass --ttf-dir instead: EvilFontTool embeds the fonts itself, directly into the .docx, without relying on Word or LibreOffice's embedding at all.
| Argument | Description |
|---|---|
input_human_file |
Text visible to human readers. Can be multiple lines |
input_computer_file |
Text visible to machines / AI. Can be multiple lines |
output_file |
Path for the generated DOCX file |
font_name |
Font family name (must match create step) |
--author |
DOCX document author metadata (default: none) |
--ttf-dir |
Directory of Evil Font TTFs (e.g. <output_dir>/ttffonts). If given, the fonts actually used are embedded directly into the .docx, so the deception renders correctly without installing them system-wide. Only embeds letters included in the human file. |
Example:
evilfonttool doc human.txt computer.txt output/secret.docx MyFont --author "Finance Team" --ttf-dir output/ttffonts
pdf — Convert an Evil Font DOCX into a copy-paste-safe PDF
evilfonttool pdf <input_docx> <output_file> [--ttf-dir DIR] [--dpi DPI] [--soffice PATH] [--ink-font FONT] [--title TITLE] [--author AUTHOR] [--subject SUBJECT] [--producer PRODUCER]
Renders the DOCX with LibreOffice so the PDF looks identical to the document, then overlays the hidden payload as an invisible copy layer so it survives copy-paste in every viewer.
| Argument | Description |
|---|---|
input_docx |
Path to the Evil Font DOCX (from the doc step) |
output_file |
Path for the generated PDF file |
--ttf-dir |
Directory of Evil Font TTFs (e.g. <output_dir>/ttffonts), exposed to LibreOffice so the disguise renders. Omit only if the fonts are installed system-wide or embedded in the file |
--dpi |
Rasterisation quality of the visible layer (default: 200) |
--soffice |
Path to the LibreOffice binary (default: soffice) |
--ink-font |
TTF for the invisible copy layer (default: a system sans) |
--title |
PDF document title metadata (default: Untitled) |
--author |
PDF document author metadata (default: none) |
--subject |
PDF document subject metadata (default: none) |
--producer |
PDF document producer metadata (default: none) |
Requires LibreOffice (
soffice) andpoppler-utils— see Dependencies.
Example:
evilfonttool pdf output/secret.docx output/secret.pdf --ttf-dir output/fonts/ttffonts --title secret
Ethical Use & Disclaimer
You are responsible for how you use this tool. Deploying this technique against systems or individuals without explicit authorization is unethical and may be illegal. The authors provide this tool to help defenders understand and test for this class of vulnerability — not to enable attacks.
Contributing
Contributions are welcome. If you've found a new attack surface, an improvement to the font generation pipeline, or a defense technique worth documenting, please open an issue or PR.
- Fork the repo
- Create a feature branch (
git checkout -b feature/my-feature) - Commit your changes
- Open a pull request with a clear description of what changed and why
Help me somethings not working!
This tool is really hard to test due to the complexity of word documents, pdfs, and fonts. If you find a repeatable issue please open a GitHub issue and I will get to it as soon as I can.
Please include the following in the issue:
- OS + LibreOffice version, and where you're viewing the file (Word / LibreOffice / something else).
- Whether the fonts are installed, embedded, or neither.
- The exact command you ran and any terminal output (especially
mismatchwarnings). - A minimal
.docx/ input that reproduces it, if you can share one. - Anything else that could be causing the issues.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file evilfonttool-2.0.5.tar.gz.
File metadata
- Download URL: evilfonttool-2.0.5.tar.gz
- Upload date:
- Size: 1.2 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
014c460b5b6b8aa68debad0b800e7cc740a485d896358f5662701a1226e705f0
|
|
| MD5 |
315154376dca7f3bc2b62875e2dea675
|
|
| BLAKE2b-256 |
84a413ca5612b3bf343049c8854d317c22c8b0b70f7a54a75475944d78181735
|
Provenance
The following attestation bundles were made for evilfonttool-2.0.5.tar.gz:
Publisher:
python-publish.yml on DoctorEww/EvilFontTool
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
evilfonttool-2.0.5.tar.gz -
Subject digest:
014c460b5b6b8aa68debad0b800e7cc740a485d896358f5662701a1226e705f0 - Sigstore transparency entry: 2314024073
- Sigstore integration time:
-
Permalink:
DoctorEww/EvilFontTool@7e6c438fa9cc8b1c954f4f3c03a73296b6e6bf46 -
Branch / Tag:
refs/tags/v2.0.5 - Owner: https://github.com/DoctorEww
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@7e6c438fa9cc8b1c954f4f3c03a73296b6e6bf46 -
Trigger Event:
release
-
Statement type:
File details
Details for the file evilfonttool-2.0.5-py3-none-any.whl.
File metadata
- Download URL: evilfonttool-2.0.5-py3-none-any.whl
- Upload date:
- Size: 22.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4f6c5e8658833732f6b5f9bed2f64bc3844c45f26875ea0c4f7c370513eb4e76
|
|
| MD5 |
43fe3b8d4c2fd0c3e929012a02594c02
|
|
| BLAKE2b-256 |
2cf9192c556fa6cf8c30712d837d835ea5daab4f3abd50fb209a36ecf4325167
|
Provenance
The following attestation bundles were made for evilfonttool-2.0.5-py3-none-any.whl:
Publisher:
python-publish.yml on DoctorEww/EvilFontTool
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
evilfonttool-2.0.5-py3-none-any.whl -
Subject digest:
4f6c5e8658833732f6b5f9bed2f64bc3844c45f26875ea0c4f7c370513eb4e76 - Sigstore transparency entry: 2314024108
- Sigstore integration time:
-
Permalink:
DoctorEww/EvilFontTool@7e6c438fa9cc8b1c954f4f3c03a73296b6e6bf46 -
Branch / Tag:
refs/tags/v2.0.5 - Owner: https://github.com/DoctorEww
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@7e6c438fa9cc8b1c954f4f3c03a73296b6e6bf46 -
Trigger Event:
release
-
Statement type: