First of all, This was a private program, so I will refer to it with target.com.
Finding the endpoint:
After a few hours of playing around with the application, I found a subdomain that gives me a message “Taking a Short Break” which means it’s a good sign to start fuzzing directories.
I started fuzzing directories till I found some endpoints which return the error code 403 Forbidden page, except
/tools endpoint, it gives me a login page, and I thought it was for admins only, let’s see:
I tried to sign in with default credentials but it wasn’t successful.
I try searching for
admin , etc, also I try to search for Ajax and XML requestes.
And I found some endpoints that confirm that this login panel is for admins.
I checked all of these endpoints, and all gave me an empty response, except for this one
Analyze first file (xproupload.js):
Starting with xproupload.js, I found this mess.
This file was +550 lines of code, so I will focus only on the important objects and functions.
I analyzed this file until I reached a function that deals with a backend upload function, starting from line 465, object “o” was declared and prepared to be the request body of an initiated Ajax request in line 473, and contains the following elements (parameters):
- folder → The directory that I will upload to.
- thefile → the parameter that will have our file name and its content.
- currentFilter → just an empty string, also from later analyses, there is no need for it.
Also in lines 472 and 473, new parameters were added:
The upload request destination was declared in line 488, to _uploadHander, and this variable was mentioned in line 377 as you can see below
Also, you may notice that in line 379, there are some upload rules, one of them was the allowed file extensions.
Analyze second file (xprofile.js):
This file has many functions, so I’ll focus only on the important objects and functions as well.
The same upload rules were declared here as well, in addition to new handlers properties added, and they were called from different endpoints
- _fileHandler → filemgn.ashx
- _rsHandler → fileUtility.ashx
In line 13, there is the getAKrs function that I was looking for, it returns the results of an Ajax post request to _rsHandler (fileUtility.ashx) file, the only parameter needed to send the request is called “fx” and its value is “setFileRS”.
I tried to send this request with the burp suite, and the result was a random string per request, my guess is that this code may validate every request sent to the server, the same as a dynamic CSRF token, we will see.
Now all required parameters and their values for uploading a file are here, let’s upload a text file to test if I have permissions to upload a file or I should be logged as admin, I also changed the encoding of the request body to
multipart/form-data; so the server prepares to receive a file.
Nice, it worked! also, the response gives me the directory where my file was uploaded and it is under
/assets directory, let’s check it out!
Sounds good, now we need to bypass the application’s extension whitelist to upload a shell, keep in your mind this is an ASP.NET application.
I tried to bypass it with all known methods, like double extension, null byte, and so on, but it didn’t work, also tried with all possible configuration and executable extensions that work with ASP.NET and Microsoft server applications like exe, bat, config, etc, but no luck.
Back to xprofile.js file, exploring, maybe there are other functions that can leverage this upload.
The number of Ajax requests used in this file took my attention, I noticed that there are some of them sent to treat with some kind of a file manager, there are functions like getFiles, deleteFile, createFolder and more, one of these functions is called renameFile!, and renaming files is absolutely what I need!
By reviewing this function, it’s an Ajax request sent to the _fileHandler (filemgn.ashx) and it needs 4 parameters:
- fn → and it takes the “rename_file” string value.
- getRS → the returned value from getAKrs function.
- akap → as mentioned above, it is just a “true” value.
- param → takes the value of the “t” variable, which was declared as an array in line 131, and was assigned with three values in line 132.
I don’t know what is the param parameter used for? so I started fuzzing it.
A few minutes later, and since the parameter param was meant to be sent as an array of 3 elements, I finally found that:
- The 1st param was the directory that contained the file we want to rename.
- The 2nd was for the new name.
- The 3rd was for the current file name.
I tried with another text file, so I uploaded slider.txt and changed the name to slider_melo.txt
Nice!, I also tried to change the file extension to a NOT allowed one, like aspx, and it works as well, so let’s upload a malicious file!
I used cmd.aspx but changed it to cmd.txt and uploaded it, then changed its name to testshell.aspx, then I navigated to the file.
My baby uploaded successfully, and I can now run commands on the system!
25 Mar: Submitted.
28 Mar: Triaged.
29 Mar: Bounty Rewarded.
I got interested to know what’s behind this code, especially the filtration implemented on the file extension. Hence, as we have the privilege to write commands on the server, I started to read this part of the code inside the fileUtility.aspx file.
Note that you MUST ask for permission before exploiting further any bug you find, especially when it comes to accessing internal data, and of course, It was communicated and agreed upon by the program team in my case.
Back to what I found, the application converts the file extension to lowercase and then filters all executable files, and I realized it can not be exploitable without finding the rename function, but in the same file, I found something related to uploading a zip file that took my attention!
As you can see in line 126, it checks for two main things:
- If the last 4 characters on the filename are “.zip”
- The value of the extract request parameter.
If both are true, it will unzip the file in the same directory we uploaded!
Will extract it without any filtration?
- YES! Let’s go!
I used the same shell file, but this time I add it in a Zip archive and create a simple python script to upload it and add the extract parameter with a true value
Now let’s check it on the browser
Look at my second baby shell uploaded successfully!
I wanted to submit a new report, but they closed the server, anyway, I hope you enjoyed reading and I will be pleased if you have any feedback!