Aquaboutic | Focus Security Research | Vulnerability Exploit | POC


build a plug-in to automatically detect whether xss exists on the page (end)

Posted by muschett at 2020-02-29

Original author: black hole, special author of freebuf

This chapter is mainly to fill in the holes for the last three chapters (with links attached at the end), because during this period, according to the opinions of multiple users, many bugs were found, because recently another project was being written (see you at the end of the year), there was not much time, but always for the user's consideration. I took the time to write this chapter. Of course, it's not interesting to just fill in the hole. I'll add a few more features. Want to know what it is? Then watch it.

0 × 01: when there are multiple IMGs in the form, the first one will be obtained, and the following IMGs cannot be traversed

This is a bug in the "form form detection XSS" function. It was put forward by Wen Tong, a basic friend. I looked at the code, and there was this problem. Because in Chapter 3, when I set up the test environment, there was only one img image in the environment I tested, and I didn't consider the problem of multiple images in the form form form. What harm would this bug cause. That is, when one img of the form form ends in PHP, but the second img ends in PNG, which will cause the form to be discarded directly and no judgment and filtering will be performed. If one is at the end of PNG and the second is at the end of PHP, the program will continue to execute downward, taking up space and time.

Let's look at the code:

Because in jQuery, attr will automatically get the SRC attribute of the first DOM, even if there are multiple DOMS ahead.

Here I refactor the tureform variable.

The original code can be deleted, that is, the following code:

The next step is to restructure.

104 lines of code remain unchanged, or:

First, we create a new variable to store the suffix of the SRC attribute value in the IMG tag.

The next step is to store the current img tag in the IMG array. Here, the map function is used to loop each key value in the object.

Remember what I said in Chapter 3, when the objects of every, filter, foreach, map, and some functions are handled by object objects instead of array arrays, will the first and second functions in the incoming functions be exchanged? Here $(index). Find ("img") is an object, so when passing a processing function, it uses number, imgsrc. If it's an array, it's imgsrc, number order. Remember, the order can't be messed up.

Then press the SRC value into the imgarray array:

OK, let's take a look at the return value now.

OK, now it's pushed into the array. The next step is to determine whether the length of the imgarray is greater than 0. If it is greater than 0, the IMG image exists in the current form form. If it is not greater than 0, there is no img picture.

Now let's write the code that runs when there are img images.

Loop the imgarray first:

And then I'll intercept the SRC address, just keep the suffix

This code is the same as the previous code except for the variable name. Just change the formimg variable to imgarray [i].

Next is the suffix.

When the current form has img tags and the suffix is not PNG, JPG, JPEG or GIF, false is returned, that is to say, the current form is discarded. If the suffix matches one of PNG, JPG, JPEG, GIF. Then judge whether the length of all input tags except type = submit in the current form is greater than 0,

($(index). Find (": input: not (: submit)"). Length > 0) this code has much better characters and efficiency than before. The original code is:

obvious. This time, the code is much better than the original one. And it's much more accurate than the original code.

Because index is the current form DOM, we use the find function to search the DOM that meets the selector conditions under the current form. Find (": input: not (: submit)") means to retrieve all input tags in the current form, except for the input tags whose type is submit.

OK, now the bug has been completely fixed. Here is the complete code:

Other places don't need to be changed.

0 × 02: after the first filtering, only the name value of the first input can be obtained

This bug I encountered in the actual operation. Next, let's talk about how the bug is hateful and how it died.

This code appears at line 121 (unmodified version, 128 lines after last bug modification)

Let's see what this code looks like:

It's the same as the last bug. All because attr automatically gets the name attribute of the first dom.

Let's take a look at the harm caused by this code.

If the current page has a form form like this:

The above bug code will directly discard the form form without any operation or judgment. The accuracy will be greatly reduced. Even more serious than a 0 × 01 section bug.

Why is the form form discarded directly? Let's have a deeper understanding.

Because this form has no img, there are other input tags besides submit. Meets the first filter. So the form successfully enters the second filter code. I won't explain the filter code. I explained it before. Let's go directly to the return code:

Let's see. Actual output:

We can see that the return is undefined, that is, it is not obtained. The next step is to modify the code.

Tureform = $(tureform). Filter (function (item, index) {}) remains unchanged.

First, define a variable to store the value of the name attribute in the input tag:

Take the form tag above as an example. This code will return:

These two input tags. Then use the for loop inputname variable.

Let's take a look at the current output:

Next, if determines whether the name attribute value exists in the current input.

The getattribute function is a JavaScript native method. The effect is the same as the attr function in jQuery. If the name value is not found, undefined will be returned implicitly, and undefined represents false in if.

Because the filter used by the external function, we only need to return true.

The above if is for you to understand. It has been said before that return can also be used as if. In fact, you can use the following code:

The effect is the same. The complete code is as follows:

0 × 03: add white list

This is a suggestion from "ice sea" in freebuf comment area. This function should be added.

Because the previous code is in the window global. Cannot use return false; (return can only be used in function functions) to prevent code from executing downward.

And "white list" is to judge first. When the current domain name is in the "white list" list, it will not be executed downward. Return false; cannot be used in window global. Then just use something like this:

This kind of hack method that directly throws errors to make the browser no longer execute downward. I don't use this hack method when I think your article is facing novices.

First, write external JavaScript code and add anonymous functions:

As follows:

First, create a new variable urlwhitelist to store the URL whitelist address.

The default white list is, and

Here is the for loop array.

After OK, it is if to judge whether the current domain name exists in the white list:

Why use indexof instead of if (urlwhitelist [i] = = host).

Because in this way, the domain names in the white list, no matter the secondary domain name or the tertiary domain name, can exist in the white list, instead of each secondary domain name or tertiary domain name being added to the white list. Of course, if you don't want to use this method, you can use the code I said above:

Because Maxthon does not provide an API to store user input, there is no way to add HTML + input directly like chrome. However, I haven't had a deep understanding of the API of chrome plug-in development. So if you want to add a white list, you can download the plug-in locally. After decompression, open base.js. CTRL + F searches for the urlwhitelist keyword and adds a whitelist after the array. After adding, the packing is OK.

Maxthon method, open Mod = viewthread & TID = 611580

Download MX plug-in, where mxpacker can extract and package plug-ins.

For the chrome method, please check with Baidu.

Other minor modifications:

At the function feedback of "form? Xss()". Add the value value directly to the input tag:

Before that

This method is not so good, it is not intuitive. So I added the border color to make it red:

But it's still not very intuitive, so I added a piece of code to make the value change. The code is as follows:

Here is the feedback from the actual page:

How about it? Is it more intuitive than before.

Other questions and answers:

Q: Some img SRC addresses are scrabbled. Can the program distinguish exactly?

A: Yes, let's first assume that there is such a img tag code in the form form of the current web page:

In the refactoring code of 0 × 01 section, there is such a section: Code

If SRC is equal to testtesttestest, imgarray [i] is equal to the testtesttesttest string. Let's take a look at the further code:

The feedback is as follows:

Why is this so? When lastIndexOf cannot search for a string, it will return - 1, and "testtesttest". Length is the length of testtesttesttest string, subsyr is intercept. Causes the last character t of the testtesttesttest string to be returned. And in the code below. Is to determine whether it is PNG, JPG, jphe, GIF:

Obviously not, then the form form directly runs the code in else.

Full code:

Download address of Maxthon and chrome plug-ins:

Download address of Maxthon plug-in: View? Id = 2899


When I was modifying the code, I found that I still had a lot of shortcomings. The code was not modular enough, which led to one modification and affected the whole body. Originally, I wanted to add several functions of "Ajax XSS detection", "IP XSS detection", "user agent XSS detection" and "packet header XSS detection", but the time was really not enough. Because I am writing another project. If the "auto detect XSS plug-in" is 1, then the project I am writing is 10 +. Please wait.

As for "Ajax XSS detection", "IP XSS detection", "user agent XSS detection", "packet header XSS detection" and other functions, they may be written or not added next year. This chapter is the last chapter of the series "build a plug-in to automatically detect whether XSS exists on the page".

Link to a series of articles in "build a plug-in to automatically detect whether XSS exists on the page": 1 / 2 / 3.

Final welfare: next month, I will do a small book delivery activity (decryption of 0day loophole mining technology of home router, tools for forced detection of security loopholes by fuzzy testing, love for MCU, Android development practice, web dilemma, etc.), with a minimum of 80% new (postage paid by yourself ~), which will be launched in freebuf tavern, please pay more attention.

*This article is from the contribution of black hole, a special author of freebuf. It is exclusively released by freebuf hackers and geeks (freebuf. Com), and is not allowed to be reproduced without permission