Christopher Ahearn

What's on your wire: Zero-Width Spaces and their hidden danger

Blog Post created by Christopher Ahearn Employee on Jan 28, 2019

There are many reasons I enjoy working with the RSA Netwitness Platform, but it’s when our customers turn their attention to threat hunting that really makes things exciting. In one case, there was a need where they could take new threat intelligence or research and apply it to their RSA Netwitness stack. This wasn’t directed at threat intelligence via feeds, but more around how attackers could deliver malicious content. Since there is never a shortage of threat research, one customer asked about detection of zero-width spaces.


In a recent article in The Hacker News, research was presented showing that zero-width spaces that were embedded within URL’s would be able to bypass URL scanners in Office 365. The question now is how to go about detecting this in Netwitness?


We begin with a search of our network using the following query:


Query contains "​","‌","‍","","0"


This gave us several DNS sessions and a single SMTP session.


If we pivot into the SMTP session, we can get slightly better view of the meta for that session.


That last hostname in ‘’ does look interesting, but not sure. We need to examine the session more closely.


We rendered the session as an email and it bore the signs of a classic phishing email.


However, only when we examine the raw contents of it, does the malicious indicator present itself.


The bytes highlighted in red (E2 80 8C) represent the zero-width non-joiner character (&#8204). This appears to be the attackers use of zero-width spaces as a bypass attempt in a phishing email. Next we look at the meta data for the session.


Above, we can see our suspicious hostname, but how did it get there. Turns out that the ‘phishing_lua’ parser will examine URL’s within SMTP traffic and extract the hostnames it finds into the ‘’ meta key. Fortunately for us, it included the zero-width space as part of the meta data…we just can’t see it. Or can we?


I copied the meta value for the hostname and then pasted it into my text editor. Sadly, I did not notice any strange characters. However, I did paste it into my good friend ‘vi’.


This proved that the zero-width spaces were in the meta data, which allowed our query to work successfully. The malicious site actually leads to a credential stealing webpage.  It appears that the website was a compromised wordpress site.


Next, I wanted to get a bigger data set. I took some of the meta data, such as the email address of the sender, and used it to find additional messages. Turned out, this helped identify an active phishing campaign.


Next up is to put together some kind of detection going forward. My first thought was to use an application rule, but was not successful. I think it was the way the Unicode was being interpreted or how it was inputted. I need to do more research on that. Since the app rule syntax was not working properly, I decided to build a Lua parser instead. This parser would perform the meta callback function of the “” meta key, just like an app rule would. Next, the parser would loop through a predefined list of zero-width space bytes against the returned meta value. If a match was made, it would write meta into the ‘ioc’ meta key. 



-- Step 1 Name the parser
local lua_zws_check = nw.createParser("lua_zws_check", "Check meta for zero-width spaces")



Check meta for zero-width spaces


2019-01-24 - Initial development








-- Step 3 Define where your meta will be written
-- These are the meta keys that we will write meta into

nwlanguagekey.create("ioc", nwtypes.Text),



local zws = ({


["\226\128\139"] = true, -- ​ &NegativeMediumSpac zero width space
["\226\128\140"] = true, -- ‌ ‌ zero width non-joiner
["\226\128\141"] = true, -- ‍ ‍ zero width joiner
["\239\187\191"] = true, --  zero width no-break space
["\239\188\144"] = true, -- 0 fullwidth digit zero



-- This is our function. What we want to do when we match a token...or in this case, the
-- filename meta callback.
function lua_zws_check:hostMeta(index, meta)

if meta then

for i,j in pairs(zws) do

local check = string.find(meta, i)
if check then

--nw.logInfo("*** BAD HOSTNAME CHECK: " .. meta .. " ***")

nw.createMeta(self.keys["ioc"], "hostname_zero-width_space")






-- Step 2 Define your tokens

[nwlanguagekey.create("")] = lua_zws_check.hostMeta, -- this is the meta callback key




After deploying the parser, I re-imported the new pcap file into my virtual packet decoder. The results came back quickly. I now had reliable detection for these zero-width space hostnames.


Since meta is displayed in the order in which it was written, we can get a sense as to the hostname that triggered this indicator.


Now that we have validated that the parser is working correctly in the lab environment, it was time to test some other capabilities of the Netwitness platform.


As we stated in the beginning, the query (as well as the parser) was flagging on DNS name resolutions that involved Unicode characters. Therefore, we wanted to create an alert when we saw the ‘zero-width’ meta when it was in SMTP traffic. We then created an ESA rule in the lab environment.


To begin this alert, I went to the Configure / ESA Rules section in Netwitness and created a new rule using the Rule Builder wizard.



We gave the rule a name, which will be important in the next phase. Next, we created the condition by giving the condition a name and then populating the fields.

The first line is looking for the meta key and the meta value. The second is looking at the service type. Once it looks good, we hit save. We then hit save again and close out the rule.


NOTE: In the first line, you see the “Array?” box checked. Some meta keys are defined as arrays meaning they could contain multiple values in a session. The meta key ‘ioc’ is one such meta key. You may encounter a situation where a meta key should be set as an Array but is not. If that is the case, it is a simple change on the ESA configuration.


Next, we want to deploy the rule to our ESA appliance. To do, we clicked the ESA appliance in our deployments table.

Next, we add the rule we want to deploy. Then, we deploy it.


We then imported the PCAP again to see if our ESA rule fired successfully, which it did.


The last piece before production is to create an Incident rule, based on the ESA alerts. We move to Configure / Incident Rules and create a new rule.

I created the Incident rule in the lab and used the parameters shown below.


I then enabled the rule and saved it.


Now, when the incidents are examined in the Respond module, we can see our incidents being created.


To summarize this activity, we started from some new(ish) research and wanted to find a way to detect this in Netwitness. We found traffic that we were interested in and then built a Lua parser to improve our detection going forward. Next, I wanted to alert on this traffic only when it was in SMTP traffic and, because I wanted to work on some automation, created an Incident rule to put a bow on this. We now have actionable alerting after small bit of research on our end.  My intent is to get the content of the parser added to one already in Live.  Until that time, it will be here to serve as a reference.


What are your use cases? What are some things you are trying to find on the network that Netwitness can help with? Let us know.


Good luck and happy hunting.