Writing where no one can see

Chris McGuire
ACM at UCSD
Published in
3 min readApr 15, 2021

Challenge write-up for widthless from CSAW 2020 Qualifiers.

In this challenge, we revisit an old trick that, as the hint stated, is kind of funky. Diving into any web challenge can quickly turn into a unique experience and this challenge did not disappoint.

The challenge was a multistage challenge which presented a series of simple input webpages. On each page a password needed to be submitted to get access to the next webpage.

Discovering Zero Width Characters

After poking around the first webpage’s source, there is some HTML code that does not seem to have any effect on the appearance of the webpage. These character codes are a variety of zero width Unicode characters.

Screenshot of raw HTML from the webpage. It highlights over the zero width characters found in the code.
Looking at the raw html of the site

In the HTML, there was also another clue (not in the screenshot), “zwsp is fun!” After some web research, it became apparent this challenge would be about zero width character steganography. Essentially this means information is being hidden by encoding data with and within the zero width characters.

Null Byte had a really solid write up on this technique. In a short summary, there are Unicode characters mostly from languages other than English that do not appear in most normal output (there are cases where they appear).

Most surprisingly (for me), this technique goes back to the 1980s according to some of the commenters on a Hackaday post on the topic.

Finding the Solution

This challenge was a team effort of ideas to understanding the problem. Eventually, a GitHub repo by enodari popped up which looked like just the thing that was needed. Once the characters were copied over, we were able to remove the excess and extract the hidden message.

The tool we used has two different modes. MODE_ZWSP which uses Zero-Width Space (\u200b), Zero-Width Non-Joiner (\u200c), and Zero-Width Joiner (\u200d). MODE_FULL uses all of the MODE_ZWSP characters plus it includes Left-To-Right Mark (\u200e), Right-To-Left Mark (\u200f).

Screen capture of the python script being used to get the hidden message from the zero width characters from the HTML extraction.
Python to decode the hidden message
Screenshot of command line showing the results of the script being run.
Output from the script

The output from the hidden text is: YWxtMHN0XzJfM3o=

This looks a lot like Base64. Using a Base64 decoder, we can get the result of alm0st_2_3z. Now just use this code word in the sign up form!

Screen shot of a webpage with an input box and a sign up button. The password has been completed, which reveals a new URL structure below it. The URL structure is a random string with a subdirectory of the password.

Doing so gives us another URL to visit. Traveling to the URL, we will scrape the entire HTML page from view-source (ctrl-a, ctrl-c, ctrl-v).

Remove the excess and get only the raw zero width characters. Again we put this into our script and…

Screen capture of the python script being used to get the hidden message from the second set of zero width characters.
Python to decode the second hidden message

Voilà! It returns this:

Screenshot of command line showing the results of the second script being run.
Output from script number 2

This time the decoded message, 755f756e6831645f6d33, looks like hexadecimal. Using a hex to ASCII translator, such as the one from Rapid Tables, we get the next password: u_unh1d_m3.

Screen shot of a webpage with an input box and a sign up button. The password has been completed, which reveals a new URL structure below it. The URL structure is a random string with a subdirectory of password 1 and password 2
Entering the final password gives the final URL structure

Yet another URL to explore! However, this time, it is the end! Getting to this 3rd webpage allows us to get the flag.

Screen capture of the flag on the final webpage
Screen Cap of the flag on the final webpage

The flag being: flag{gu3ss_u_f0und_m3}. Overall this was a fun warm-up to web challenges where our team was able to learn about some different steganography techniques!

--

--