As I was doom scrolling through LinkedIn, I came acrossa new post byAzimbek Abdullajonov, an avid Cyber Security researcher, discussing his novel creation of a new Invisible Javascript Payload. And by invisible, I mean you literally can't see the code it's running in a text editor. Let's dive into the world of Unicode Zero Width character prompt injections and see how they fit into modern day attacks.
But first, what is a zero width unicode character?
As the name suggests, they are characters that have no width. In other words, they don’t display a physical character symbol in an editor (like ‘£’ or ‘K’). They aren’t the same as whitespace such as tabs or spaces - whilst tabs and spaces don’t have a visible symbol you can see, they are physical gaps between other symbols, thus giving them a visible presence.
They have a broad range of legitimate and computationally important uses, such as telling any text viewer whether characters should be joined (like in arabic) and when they shouldn’t. We’ll be looking at how you can use zero-width characters to actually improve security later.
The zero width characters set includes:
Zero Width Space (ZWSP) → U+200B
Zero Width Non-Joiner (ZWNJ) → U+200C
Zero Width Joiner (ZWJ) → U+200D
Zero Width No-Break Space → U+FEFF
Tag Characters → U+E0000 - U+E007F
Invisible JS Payloads
Let’s come back to the Invisible JS payload attack by Azimbek. It works on a very simple premise, where it will take in a Javascript payload, and then converts every character to an 8-character sequence of invisible 1’s (Zero-Width Joiner) and 0’s (Zero-width space). Combined with a very small bootstrap script that then decodes these characters back into regular JS, you get a payload that visibly does very little. This is exemplified by the below screenshot. My cursor appears to have selected nothing, but as you can see, VS Code states that 36 characters are selected.
(It should be noted that this example has actually been made using the Zero-Wdith tag character, not the script created by Azimbek).
The implications of this are severe. It makes it practically impossible for static analysis to reliably determine the functionality of a potentially malicious payload Callout: Static analysis is the process of determining a malicious scripts methods, functionalities and behaviour by reading and understanding the code its made up of.
I won’t go into the implications of this attack vector here as Azimbek covers it pretty well in his linkedin post and the original repository, but its definitely worth a read.
The tag character
Originally intended for the invisible tagging of languages of text streams, tag characters are now mostly deprecated, but allow for the invisible storage of characters thanks to their one-to-one mapping with 7-bit ASCII. Because of this mapping, it’s possible to invisibly map all regular characters to an invisible version of itself. If you want to see this in action yourself, click this copy button: This will add an invisible sequence of tag characters - let me know if you can figure the hidden message. You can also do this yourself using the ASCII Smuggler.
I’ve built a microscript for converting strings programmatically available at (REPO URL). This script outputs to a file as the windows terminal will invisibly and silently convert these invisible characters to a visible read out of their unicode sequence (Linux/MacOS does not). Make sure to open the output in VS Code or Notepad++, as Windows’ notepad application silently strips invisible characters.
So, what can we do with this?
Quite a lot.
Homograph Attacks
The most classic is the Homograph attack. Homograph (or Homoglyph) attacks are the creation of fake domains using visually similar characters to register a domain. For example, ‘gogle.com’ to impersonate ‘google.com’, or replacing the Latin ‘a’ character with the cyrilic ‘а’ character (U+0430 : CYRILLIC SMALL LETTER A). This can result in dangerous phishing attacks to the untrained eye, and thus these attacks are banned by ICANN.
Its impossible to discuss security today without using the “L” word - Large Language Models. The widespread advent of LLMs has created a whole new world of exploits, script kiddies and attack vectors, and with, the zero-width character has had a rebirth. Without getting too “in-depth” about the inner workings of LLMs, all LLMs understand language as a series of tokens. These tokens may represent syllables, punctuation, full words, or anything inbetween. With this, many LLMs are blind to the difference between regular unicode characters and their invisible siblings. That means you end up with funny situations like the one below Where other instructions have been inserted with invisible tag characters to give other, potentially malicious instructions.
Courtesy of https://www.trendmicro.com/en_us/research/25/a/invisible-prompt-injection-secure-ai.html
As a result of this, it may be possible to jailbreak some LLMs, taking us back to 2023 when it was possible to have an LLM give you detailed instructions of how to build a bomb, create possibly harmful visual content, or manufacture narcotics. The main players in LLMs have come wise to this - with gemini’s “thinking” process giving me this reasonable output:
But with the vast proliferation of LLMs in every company, web app and tech bro using them, its almost inevitable that there are still some that haven’t caught up to the curve.
What else?
The possibilities continue: If you have a disgruntled employee who wants an insurance policy, they may be able to install a backdoor in your codebase. Not hard to spot when they’re fully rendered - but what is to stop them from inserting malicious code that is completely invisible? That leads us into input validation and security filters. The broadstrokes of these is attacks is imple: Any system that filters data based upon text values is potentially vulnerable. If your system doesn’t know how to handle these invisible unicode characters, you’re putting yourself at risk.
So what’s the uptick?
There are also some nice applications of Zerowidth characters, namely, Intellectual property protection. Its possible to watermark any type of text - code, literature, articles etc, with invisible characters. The overwhelming majority of people will be none-the-wiser, meaning there is a forensic “fingerprint” inside of the text, whether that be to prove the ownership and origins of code, or to detect the usage of copy-pasting.
How can I protect myself?
Like I said, its about making sure your systems can handle the unicode. Gracefully, some linters will flag Zero-width characters:
VS Code will display hints, but only with the correct extensions and languages installedAnd you can implement things like input sanitization, character whitelisting and, as above, any type of visual indication/highlighting for zero-width characters. Thanks for reading.