Noroboto: Lying Fonts and Mitigation in Rust A team of legal technology researchers at LegalQuants has developed a "lexploit" called Noroboto, a malicious TrueType font that lies about the Unicode representation of its glyphs to obfuscate text in legal documents. The attack embeds the font in Word and PDF files, making text appear normal to human readers while producing incomprehensible Unicode garbage when copied and pasted, potentially undermining AI-powered legal document analysis and discovery tools. The researchers warn that the exploit exploits the complexity of decades-old document specifications and their imperfect implementations across modern legal tech stacks. What if your font is lying to your AI? LegalTech's Mythos Moment Modern legal tech stacks in 2026 are Rube Goldberg machines https://en.wikipedia.org/wiki/Rube Goldberg machine of open-source and proprietary products from Word to LibreOffice, to python-docx and PDFium, to tesseract , node.js and dozens of UI libraries like SuperDoc, PDF.js and Office.js. Through those pipelines are pushed artifacts of decades-old written specifications which span tens of thousands of pages. In addition to the venerated OSS parts of these stacks exist partial, proprietary implementations of these specs. Many of these have been spun up in the last year with the assistance of coding agents. Meanwhile even the oldest, grayest-beard OSS maintainers in the ecosystem complain of specification complexity https://tritium.legal/blog/word . What if an adversary were to try to take advantage of this complexity and the imperfections in these implementations? Could these imperfections be leveraged for a tactical legal advantage? I reached out to my friends at the LegalQuants https://legalquants.com and recruited a team to answer this question, and you can read the analysis of the "lexploit" discussed below and about our new "Red Team" mission here: link https://legalquants.substack.com/p/noroboto-and-legal-techs-mythos-moment . Noroboto.ttf The "noroboto.ttf" "lexploit" is straightforward: create a new malicious font definition which is embedded in a document according to the specification and lies about the Unicode representation of its glyphs. TrueType Among many other things, TrueType fonts like those distributed with Windows and macOS contain outlines and a cmap or character map which maps Unicode code points https://en.wikipedia.org/wiki/List of Unicode characters to these outlines. The Unicode specification which is huge. In addition to code points for scripts such as Latin and CJK, among many others, it also reserves ranges of code points for "private use". The simplest "full obfuscation" noroboto attack works by swapping valid Unicode-encoded scripts in the subject document with Unicode code points occupying these so-called "Private Use Areas" of Unicode. These glyphs typically render as "tofu" or some other unknown glyph in most graphical applications, or as a glyph from a fallback font definition as determined by such applications. You can check that out here https://noroboto.io . For "PUA" code points LibreOffice, for example, seems to fallback to Wingdings. But noroboto provides a glyph for these PUA code points. And those glyphs are metric compatible with the replaced font. Their underlying Unicode mapping, however, is incomprehensible garbage. This only works because the Word and PDF specifications allow for font definitions to be embedded in their containing documents. Embedding fonts is critical to maintain compatibility and pixel-tight rendering across platforms. And consistent rendering is especially important in legal documents where font metrics determine page layout and pagination, and page numbers can have legal meaning. Noroboto.py With the help of ChatGPT 5.4 we had a proof-of-concept for full obfuscation within a few hours. On the left of the above GIF is what the user sees. When the text is copied and pasted, however, you get the Unicode representation in an arbitrary non-Noroboto font. It's garbage. You can see a version of the code here: https://github.com/LegalQuants/noroboto https://github.com/LegalQuants/noroboto . 1 1 We opted for Python to maximize legibility, but that somewhat backfired given the "vibes heavy" implementation. 2 2 Testing An early testing against a version which leveraged a 1-to-1 mapping was defeated by ChatGPT 5.5 in Codex using "high effort". ChatGPT 5.5 deobfuscated in two ways. First, given the simple PUA-to-glyph deterministic mapping, ChatGPT 5.5 treated deobfuscation as a basic cryptoanalysis exercise. It sussed out our "monoalphabetic" cipher and broke our "simple substitution cipher with side channels left intact". 3 ChatGPT's second approach was to note that we had erroneously left the original "name" value in the glyph definition which could be reverted by reading the TTF. Time to pull out the big guns: https://en.wikipedia.org/wiki/Polyalphabetic cipher https://en.wikipedia.org/wiki/Polyalphabetic cipher . We updated noroboto.py in this commit https://github.com/LegalQuants/noroboto/commit/f28172d5346dcd26ae7a20fa99b69b2671ef7f57 to exclude that "name" field and in this commit https://github.com/LegalQuants/noroboto/commit/e64549f580d73434d1421c80cb7961741af663cb to include a 4-to-1 mapping which is randomly applied by the text replacement algorithm. We also perturb the font slightly across the four separate PUAs to avoid comparing the outlines and collapsing them back to a 1-to-1 mapping. Although these changes have limitations, they seemed to supply enough stochasticity to throw off ChatGPT's simple cipher. But the frontier models in agentic harnesses with their inference-time computing modes enabled aka "thinking" all still manage to crack the "full" obfuscation document by shelling out to something, rendering the document and OCR'ing that result. 4 4 It turns out obfuscating the entire document is enough signal to encourage these LLMs to try different approaches. 5 5 A live demonstration of full obfuscation is here: https://noroboto.io https://noroboto.io . We touched on in our LegalQuants post https://legalquants.substack.com/p/noroboto-and-legal-techs-mythos-moment the ethics and legality of using the Noroboto attack 6, but technically the much more effective approaches are both partial obfuscation and Unicode replacement. Extensions: Partial Obfuscation and Replacement It turns out, agents are somewhat lazy. Thus, if they are presented with what appears to be a document containing legible Unicode code points, they often take that apparent happy path. Total obfuscation fails this test in the smartest models, but even the best are fooled when a document is only partially obfuscated or the text of the document is replaced. We don't release any code on these two approaches but we present two example sets of documents in DOCX and PDF. | Example | DOCX | | |---|---|---| | Full obfuscation | | full.pdf https://github.com/LegalQuants/noroboto/blob/master/examples/full.pdf partial.docx https://github.com/LegalQuants/noroboto/blob/master/examples/partial.docx partial.pdf https://github.com/LegalQuants/noroboto/blob/master/examples/partial.pdf replaced.docx https://github.com/LegalQuants/noroboto/blob/master/examples/replaced.docx replaced.pdf https://github.com/LegalQuants/noroboto/blob/master/examples/replaced.pdf Partial Obfuscation What's the point of partially obfuscating a legal document? The most obvious case is to just disguise an adversarial term with a higher probability of success. In testing our partial obfuscation example https://github.com/LegalQuants/noroboto/blob/master/examples/partial.docx , we hid the fact that the NDA's confidentiality terms carry on to "successors and assigns". This isn't particularly egregious but was a useful test case. We asked the model "Does anything in this document extend my confidentiality obligations to successors or assigns?", and some, particularly inexpensive platforms, returned incorrect results for DOCX. Now some might argue this is fraudulent if it's intended to mislead the other party, but we don't necessarily express that opinion. Replacement The replacement extension of "noroboto" is the most effective. In the replacement attack, instead of mapping the glyphs to PUA code points, we map them to Unicode values that create a different meaning. In our example replacement-example , we caused the human-visible word "Maryland" to be replaced with the Unicode representation of "Delaware". This process isn't as simple as the obfuscation attack because it requires, in the worst case, a new embedded font for each replaced glyph. In the above image, we represent each additional font as "ext n ", but this can likely be compressed in longer replacement attacks to maximize font re-use. 7 7 All of the platforms we tested were fooled by this approach and happily reported that the agreement provided for Delaware governing law when presented with a DOCX file. 8. Most even trusted the Unicode values in PDF. The Red Team hypothesizes that the agentic harnesses are "lazy" and prefer to rely on a facially valid Unicode string rather than undertake to render the document and run an expensive OCR computation. This laziness is likely correlated with the length of the document. Proof of Concept Mitigation in Rust So how might we handle this in Tritium? Trust, but verify. We want to retain embedded font support to ensure layout and pagination accuracy, but we first run a check against the ASCII glyphs to ensure they represent the characters they purport to represent via their Unicode cmap value. That accuracy value is 1.0 minus the error rate, which we calculate as the Levenshtein distance https://en.wikipedia.org/wiki/Levenshtein distance between the expected ASCII string and the OCR result. php fn normalize text: &str - String { text.to lowercase .split whitespace .collect::