package utils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.parser.Parser;
import org.jsoup.select.Elements;
import org.w3c.tidy.Tidy;
/**
* Class is responsible for finding valid XSS and CSRF attacks in user submissions
*
* This file is part of the Security Shepherd Project.
*
*
The Security Shepherd project is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
*
The Security Shepherd project is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
*
You should have received a copy of the GNU General Public License along with the Security
* Shepherd project. If not, see ");
if (messageForAdmin.contains("
");
int tempStart = messageForAdmin.indexOf("
", tempStart + 5);
if (tempEnd == -1) {
log.debug("Invalid
Tag");
} else {
log.debug("Searching for SRC attribute");
String tempMessage = messageForAdmin.substring(tempStart, tempEnd);
log.debug("Working on: " + tempMessage);
if (tempMessage.contains(" src")) {
log.debug("Finding src after '='");
int srcStart = tempMessage.indexOf(" src") + 4;
tempMessage = tempMessage.substring(srcStart);
log.debug("After SRC: " + tempMessage);
int srcEqual = tempMessage.indexOf("=") + 1;
log.debug("srcEqual = " + srcEqual);
int counter = 0;
while (tempMessage.substring(srcEqual + counter).startsWith(" ")) {
// Find end of white space after equals sign, and then evaluate if the url is valid
counter++;
log.debug("counter = " + counter);
}
tempMessage = tempMessage.substring(srcEqual + counter);
log.debug("Working on: " + tempMessage);
String quoteType = null;
if (tempMessage.startsWith("\"")) {
quoteType = "\"";
} else if (tempMessage.startsWith("'")) {
quoteType = "'";
} else {
log.debug("No Quotes found around url");
int endOfUrl = tempMessage.indexOf(" ");
if (endOfUrl == -1) {
endOfUrl = tempMessage.length();
} else {
endOfUrl--;
}
log.debug(tempMessage);
tempMessage = tempMessage.substring(0, endOfUrl);
log.debug(tempMessage);
}
if (quoteType != null) {
log.debug("Quotes Found: " + quoteType);
tempMessage = tempMessage.substring(1, tempMessage.substring(2).indexOf(quoteType) + 2);
}
log.debug("URL found to be: " + tempMessage);
boolean validUrl = false;
log.debug("Validating URL for Solution");
try {
URL csrfUrl = new URL(tempMessage);
log.debug("URL Host: " + csrfUrl.getHost());
log.debug("URL Port: " + csrfUrl.getPort());
log.debug("URL Path: " + csrfUrl.getPath());
log.debug("URL Query: " + csrfUrl.getQuery());
validUrl =
csrfUrl.getPath().toLowerCase().equalsIgnoreCase("/root/grantComplete/csrflesson");
if (!validUrl) {
log.debug("1");
}
validUrl =
csrfUrl
.getQuery()
.toLowerCase()
.equalsIgnoreCase(("userId=" + falseId).toLowerCase())
&& validUrl;
if (!validUrl) {
log.debug("2");
}
} catch (MalformedURLException e) {
log.error("Invalid URL: " + e.toString());
}
if (!validUrl) {
log.debug("Invalid Url: " + tempMessage);
} else {
log.debug("Valid URL");
return true;
}
}
}
}
return false;
}
/**
* Searches for URL that contains CSRF attack string without user ID expected. Returns true if it
* is valid based on parameters submitted
*
* @param theUrl The Entire URL containing the attack
* @param csrfAttackPath The path the CSRF vulnerable function should be in
* @return boolean value depicting if the attack is valid or not
*/
public static boolean findCsrfAttackUrl(String theUrl, String csrfAttackPath) {
boolean validAttack = false;
try {
URL theAttack = new URL(theUrl);
log.debug("theAttack Host: " + theAttack.getHost());
log.debug("theAttack Port: " + theAttack.getPort());
log.debug("theAttack Path: " + theAttack.getPath());
log.debug("theAttack Query: " + theAttack.getQuery());
validAttack = theAttack.getPath().toLowerCase().equalsIgnoreCase(csrfAttackPath);
if (!validAttack) {
log.debug("Invalid Solution: Bad Path or Above");
}
} catch (MalformedURLException e) {
log.debug("Invalid URL Submitted: " + e.toString());
validAttack = false;
} catch (Exception e) {
log.error("FindCSRF Failed: " + e.toString());
validAttack = false;
}
return validAttack;
}
/**
* Searches for URL that contains CSRF attack string. Returns true if it is valid based on
* parameters submitted
*
* @param theUrl The Entire URL containing the attack
* @param csrfAttackPath The path the CSRF vulnerable function should be in
* @param userIdParameterName The user ID parameter name expected
* @param userIdParameterValue The user ID parameter value expected
* @return boolean value depicting if the attack is valid or not
*/
public static boolean findCsrfAttackUrl(
String theUrl,
String csrfAttackPath,
String userIdParameterName,
String userIdParameterValue) {
boolean validAttack = false;
try {
URL theAttack = new URL(theUrl);
log.debug("csrfAttackPath: " + csrfAttackPath);
log.debug("theAttack Host: " + theAttack.getHost());
log.debug("theAttack Port: " + theAttack.getPort());
log.debug("theAttack Path: " + theAttack.getPath());
log.debug("theAttack Query: " + theAttack.getQuery());
boolean validPath = theAttack.getPath().toLowerCase().endsWith(csrfAttackPath.toLowerCase());
if (!validPath) {
log.debug("Invalid Solution: Bad Path submitted. Expected:" + csrfAttackPath.toLowerCase());
} else {
boolean validQuery =
theAttack
.getQuery()
.toLowerCase()
.equalsIgnoreCase((userIdParameterName + "=" + userIdParameterValue).toLowerCase());
if (!validQuery) {
log.debug(
"Invalid Solution: Bad Query. Expected: "
+ (userIdParameterName + "=" + userIdParameterValue).toLowerCase());
} else {
validAttack = true;
}
}
} catch (MalformedURLException e) {
log.debug("Invalid URL Submitted: " + e.toString());
validAttack = false;
} catch (Exception e) {
log.error("FindCSRF Failed: " + e.toString());
validAttack = false;
}
return validAttack;
}
/**
* Forms XSS Input for XHTML before Searching with Shepherd XSS Detector
*
* @param xssString Untrusted User Input
* @return Boolean value depicting if XSS was detected
*/
public static boolean search(String xssString) {
boolean xssDetected = false;
log.debug("String to Search: " + xssString);
// Need to tidy submitted string, similar to how a browser would when it interprets it
Tidy tidy = new Tidy();
tidy.setXHTML(true);
tidy.setQuiet(true);
tidy.setShowWarnings(false);
InputStream inputStream = new ByteArrayInputStream(xssString.getBytes());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
tidy.parseDOM(inputStream, outputStream);
String tidyHtml = outputStream.toString().toLowerCase();
try {
outputStream.close();
inputStream.close();
} catch (Exception e) {
log.error("Could not Cloud Tidy Input/Output Streams: " + e.toString());
}
// log.debug("String Tidied To: " + tidyHtml);
// Now to Parse it and narrow down to the Body of the output
Document parsedHtml = Jsoup.parseBodyFragment(tidyHtml);
Element htmlBody = parsedHtml.body();
// Now We're in Search Territory. Three main Stages
// Stage One: Detect