diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 407a0d2b..00000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..8d7f46df --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +# Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +.mvn/wrapper/maven-wrapper.jar + +# IDE +.idea/ +*.iml +.vscode/ +.settings/ +.project +.classpath + +# OS +.DS_Store +Thumbs.db diff --git a/pom.xml b/pom.xml index ddcc05b4..a7394298 100644 --- a/pom.xml +++ b/pom.xml @@ -24,12 +24,12 @@ org.apache.commons commons-text - 1.9 + 1.10.0 mysql mysql-connector-java - 5.1.42 + 8.0.33 com.mchange @@ -66,7 +66,7 @@ org.apache.logging.log4j log4j-core - 2.3 + 2.23.1 true test diff --git a/src/main/java/com/endor/AppServlet.java b/src/main/java/com/endor/AppServlet.java index 0e851f40..db83109f 100644 --- a/src/main/java/com/endor/AppServlet.java +++ b/src/main/java/com/endor/AppServlet.java @@ -8,12 +8,51 @@ import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.URL; +import java.net.InetAddress; +import java.net.UnknownHostException; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; @javax.servlet.annotation.WebServlet(name = "AppServlet", urlPatterns = "/AppServlet") public class AppServlet extends javax.servlet.http.HttpServlet { + + // Helper method to check if a host is an internal/private address + private boolean isInternalAddress(String host) { + if (host == null || host.isEmpty()) { + return false; + } + + // Check for obvious internal addresses + if (host.startsWith("localhost") || host.startsWith("127.") || + host.contains("metadata") || host.contains("169.254")) { + return true; + } + + // Check for private IP ranges + if (host.startsWith("192.168.") || host.startsWith("10.")) { + return true; + } + + // Check for 172.16.0.0 - 172.31.255.255 range + if (host.startsWith("172.")) { + String[] parts = host.split("\\."); + if (parts.length >= 2) { + try { + int secondOctet = Integer.parseInt(parts[1]); + if (secondOctet >= 16 && secondOctet <= 31) { + return true; + } + } catch (NumberFormatException e) { + // Invalid format, treat as potentially dangerous + return true; + } + } + } + + return false; + } + protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { doGet(request, response); } @@ -92,7 +131,30 @@ public void UseUrlOpenConnection(javax.servlet.http.HttpServletRequest request, response.getWriter().println("Inside Url.openStream"); String url = "https://www.oracle.com/"; if (ssrfURL != null && ssrfURL.length() > 0) { - url = ssrfURL; + // Security fix: Validate URL to prevent SSRF attacks + try { + URL parsedUrl = new URL(ssrfURL); + String protocol = parsedUrl.getProtocol(); + String host = parsedUrl.getHost(); + + // Only allow HTTPS protocol + if (!"https".equals(protocol)) { + response.getWriter().println("

Error: Only HTTPS URLs are allowed for security reasons.

"); + return; + } + + // Prevent access to internal/private networks using helper method + if (isInternalAddress(host)) { + response.getWriter().println("

Error: Access to internal/private network addresses is not allowed.

"); + return; + } + + // Use validated URL + url = ssrfURL; + } catch (Exception e) { + response.getWriter().println("

Error: Invalid URL format.

"); + return; + } } URL oracle = new URL(url); @@ -122,6 +184,18 @@ public void UseUrlOpenConnectionhttps(javax.servlet.http.HttpServletRequest requ String UrlToOpen = ssrfURL.replaceFirst("HTTPS://", ""); UrlToOpen = UrlToOpen.replaceFirst("https://", ""); + // Security fix: Validate hostname to prevent SSRF attacks using helper method + if (isInternalAddress(UrlToOpen)) { + response.getWriter().println("

Error: Access to internal/private network addresses is not allowed.

"); + return; + } + + // Validate that the hostname doesn't contain suspicious characters + if (!UrlToOpen.matches("[a-zA-Z0-9\\-\\.]+")) { + response.getWriter().println("

Error: Invalid hostname format.

"); + return; + } + try { System.out.printf("Opening SSL socket for host : %s\n", UrlToOpen); SSLSocketFactory factory = diff --git a/src/main/java/com/endor/BooksServlet.java b/src/main/java/com/endor/BooksServlet.java index 73168c41..21e86138 100644 --- a/src/main/java/com/endor/BooksServlet.java +++ b/src/main/java/com/endor/BooksServlet.java @@ -439,17 +439,16 @@ private boolean getCustomersUpdateColName(String first, String last, String pass Connection conn = connect(); if (conn == null) return false; - Statement stmt = null; - try { - stmt = conn.createStatement(); - } catch (SQLException e) { - e.printStackTrace(); - } + PreparedStatement stmt = null; try { - String[] cols = {"FIRST", "LAST"}; - String query = String.format("UPDATE CUSTOMERS SET FIRST = '%s' WHERE LAST = '%s' AND PASSWORD = '%s'", first, last, pass); + // Security fix: Use PreparedStatement instead of Statement to prevent SQL injection + String query = "UPDATE CUSTOMERS SET FIRST = ? WHERE LAST = ? AND PASSWORD = ?"; System.out.println("QUERY :" + query); - int ret = stmt.executeUpdate(query, cols); + stmt = conn.prepareStatement(query, new String[]{"FIRST", "LAST"}); + stmt.setString(1, first); + stmt.setString(2, last); + stmt.setString(3, pass); + int ret = stmt.executeUpdate(); // Clean up stmt.close(); } catch (SQLException e) { @@ -470,20 +469,24 @@ private boolean executeSQLWithColIndex(String methodName, String first, String l Connection conn = connect(); if (conn == null) return false; - Statement stmt = null; - try { - stmt = conn.createStatement(); - } catch (SQLException e) { - e.printStackTrace(); - } + PreparedStatement stmt = null; try { + // Security fix: Use PreparedStatement to prevent SQL injection int[] cols = {1, 2}; - String query = String.format("UPDATE CUSTOMERS SET FIRST = '%s' WHERE LAST = '%s' AND PASSWORD = '%s'", first, last, pass); + String query = "UPDATE CUSTOMERS SET FIRST = ? WHERE LAST = ? AND PASSWORD = ?"; System.out.println("QUERY :" + query); if (methodName.equalsIgnoreCase("execute")) { - boolean ret = stmt.execute(query, cols); + stmt = conn.prepareStatement(query, cols); + stmt.setString(1, first); + stmt.setString(2, last); + stmt.setString(3, pass); + boolean ret = stmt.execute(); } else if (methodName.equalsIgnoreCase("executeUpdate")) { - int ret = stmt.executeUpdate(query, cols); + stmt = conn.prepareStatement(query, cols); + stmt.setString(1, first); + stmt.setString(2, last); + stmt.setString(3, pass); + int ret = stmt.executeUpdate(); } else { System.out.println("Invalid SQL method!"); } @@ -493,7 +496,7 @@ private boolean executeSQLWithColIndex(String methodName, String first, String l return false; } finally { try { - stmt.close(); + if (stmt != null) stmt.close(); conn.close(); } catch (SQLException e) { System.err.println(e.getMessage()); @@ -506,20 +509,24 @@ private boolean executeSQLWithAutogenkeys(String methodName, String first, Strin Connection conn = connect(); if (conn == null) return false; - Statement stmt = null; - try { - stmt = conn.createStatement(); - } catch (SQLException e) { - e.printStackTrace(); - } + PreparedStatement stmt = null; try { + // Security fix: Use PreparedStatement to prevent SQL injection int autogenkeys = Statement.RETURN_GENERATED_KEYS; - String query = String.format("UPDATE CUSTOMERS SET FIRST = '%s' WHERE LAST = '%s' AND PASSWORD = '%s'", first, last, pass); + String query = "UPDATE CUSTOMERS SET FIRST = ? WHERE LAST = ? AND PASSWORD = ?"; System.out.println("QUERY :" + query); if (methodName.equalsIgnoreCase("execute")) { - boolean ret = stmt.execute(query, autogenkeys); + stmt = conn.prepareStatement(query, autogenkeys); + stmt.setString(1, first); + stmt.setString(2, last); + stmt.setString(3, pass); + boolean ret = stmt.execute(); } else if (methodName.equalsIgnoreCase("executeUpdate")) { - int ret = stmt.executeUpdate(query, autogenkeys); + stmt = conn.prepareStatement(query, autogenkeys); + stmt.setString(1, first); + stmt.setString(2, last); + stmt.setString(3, pass); + int ret = stmt.executeUpdate(); } else { System.out.println("Invalid SQL method!"); } @@ -529,7 +536,7 @@ private boolean executeSQLWithAutogenkeys(String methodName, String first, Strin return false; } finally { try { - stmt.close(); + if (stmt != null) stmt.close(); conn.close(); } catch (SQLException e) { System.err.println(e.getMessage()); @@ -582,11 +589,13 @@ public static String insertCustomers(String first, String last, String pass) { // Create database connection conn = DriverManager.getConnection(db, user, password); - // Create and execute statement - Statement stmt = conn.createStatement(); - String sql = "INSERT INTO CUSTOMER VALUES (\'" + first + "\',\'" + last + "\', \'" + pass + "')"; - System.out.println("Adding: " + sql); - stmt.executeQuery(sql); + // Security fix: Use PreparedStatement instead of concatenating values to prevent SQL injection + PreparedStatement stmt = conn.prepareStatement("INSERT INTO CUSTOMER VALUES (?, ?, ?)"); + stmt.setString(1, first); + stmt.setString(2, last); + stmt.setString(3, pass); + System.out.println("Adding customer: " + first + " " + last); + stmt.executeUpdate(); System.out.println("Inserted into Database"); // Clean up @@ -640,15 +649,16 @@ public boolean executeSQLHelper(String methodName, String name, String pass) { try { StringBuffer sbuf = new StringBuffer(); - String query = new String(); - - query = "select FIRST, LAST from CUSTOMERS WHERE LAST=\'" + name + "\' AND PASSWORD= \'" + pass + "\'"; + // Security fix: Use PreparedStatement with parameters to prevent SQL injection + String query = "select FIRST, LAST from CUSTOMERS WHERE LAST=? AND PASSWORD=?"; if (methodName.equalsIgnoreCase("executeQuerySQL")) { System.out.println("QUERY :" + query); - Statement stmt = conn.createStatement(); - ResultSet rs = stmt.executeQuery(query); + PreparedStatement stmt = conn.prepareStatement(query); + stmt.setString(1, name); + stmt.setString(2, pass); + ResultSet rs = stmt.executeQuery(); // Loop through the data and print all artist names while (rs.next()) { sbuf.append("Customer Name: " + rs.getString("FIRST") + " " + rs.getString("LAST")); @@ -662,6 +672,8 @@ public boolean executeSQLHelper(String methodName, String name, String pass) { } else if (methodName.equalsIgnoreCase("PreparedStatementEexecuteQuerySQL")) { System.out.println("PreparedStatementQUERY :" + query); PreparedStatement stmt = conn.prepareStatement(query); + stmt.setString(1, name); + stmt.setString(2, pass); ResultSet rs = stmt.executeQuery(); // Loop through the data and print all artist names while (rs.next()) { @@ -674,12 +686,16 @@ public boolean executeSQLHelper(String methodName, String name, String pass) { stmt.close(); rs.close(); } else if (methodName.equalsIgnoreCase("executeSQL")) { - Statement stmt = conn.createStatement(); - retVal = stmt.execute(query); + PreparedStatement stmt = conn.prepareStatement(query); + stmt.setString(1, name); + stmt.setString(2, pass); + retVal = stmt.execute(); stmt.close(); } else if (methodName.equalsIgnoreCase("executeUpdateSQL")) { - Statement stmt = conn.createStatement(); - retVal = stmt.executeUpdate(query) > 0; + PreparedStatement stmt = conn.prepareStatement(query); + stmt.setString(1, name); + stmt.setString(2, pass); + retVal = stmt.executeUpdate() > 0; stmt.close(); } } catch (SQLException e) { @@ -701,49 +717,50 @@ public boolean executeSQLHelper(String methodName, int n, String name, String pa if (conn == null) return false; - // Check for multiple values entry before constructing the query + // Security fix: Parse multiple values for IN clause using PreparedStatement String[] name_values = name.split(","); - String parse_name_values = name_values[0]; - for (int i = 1; i< name_values.length; i++ ) { - parse_name_values += "\',\'" ; - parse_name_values += name_values[i]; - } try { StringBuffer sbuf = new StringBuffer(); - String query; + PreparedStatement stmt = null; - // Check for filter operation + // Check for filter operation with LIKE if(n==2) { - query = "select FIRST, LAST from CUSTOMERS WHERE LAST like \'" + name + "%\' AND PASSWORD= \'" + pass + "\'"; + String query = "select FIRST, LAST from CUSTOMERS WHERE LAST like ? AND PASSWORD= ?"; + System.out.println("QUERY :" + query); + stmt = conn.prepareStatement(query); + stmt.setString(1, name + "%"); + stmt.setString(2, pass); } - // Check for normal query operation + // Check for normal query operation or IN clause else { - if(parse_name_values.contains(",")) { - query = "select FIRST, LAST from CUSTOMERS WHERE LAST IN (\'" + parse_name_values + "\')"; + if(name_values.length > 1) { + // Security fix: Build parameterized query for IN clause + StringBuilder queryBuilder = new StringBuilder("select FIRST, LAST from CUSTOMERS WHERE LAST IN ("); + for (int i = 0; i < name_values.length; i++) { + queryBuilder.append("?"); + if (i < name_values.length - 1) { + queryBuilder.append(","); + } + } + queryBuilder.append(")"); + String query = queryBuilder.toString(); + System.out.println("QUERY :" + query); + stmt = conn.prepareStatement(query); + for (int i = 0; i < name_values.length; i++) { + stmt.setString(i + 1, name_values[i]); + } } else { - query = "select FIRST, LAST from CUSTOMERS WHERE LAST=\'" + parse_name_values + "\' AND PASSWORD= \'" + pass + "\'"; + String query = "select FIRST, LAST from CUSTOMERS WHERE LAST=? AND PASSWORD=?"; + System.out.println("QUERY :" + query); + stmt = conn.prepareStatement(query); + stmt.setString(1, name_values[0]); + stmt.setString(2, pass); } } - System.out.println("QUERY :" + query); - // Check for preparedstatement - if (methodName.equalsIgnoreCase("executeQuerySQL")) { - Statement stmt = conn.createStatement(); - ResultSet rs = stmt.executeQuery(query); - // Loop through the data and print all artist names - while (rs.next()) { - sbuf.append("Customer Name: " + rs.getString("FIRST") + " " + rs.getString("LAST")); - System.out.println("Customer Name: " + rs.getString("FIRST") + " " + rs.getString("LAST")); - sbuf.append("
"); - retVal = sbuf.toString().length() > 2; - } - // Clean up - stmt.close(); - rs.close(); - } else if (methodName.equalsIgnoreCase("PreparedStatementEexecuteQuerySQL")) { - System.out.println("PreparedStatementQUERY :" + query); - PreparedStatement stmt = conn.prepareStatement(query); + // Execute the query based on method name + if (methodName.equalsIgnoreCase("executeQuerySQL") || methodName.equalsIgnoreCase("PreparedStatementEexecuteQuerySQL")) { ResultSet rs = stmt.executeQuery(); // Loop through the data and print all artist names while (rs.next()) { @@ -756,12 +773,10 @@ public boolean executeSQLHelper(String methodName, int n, String name, String pa stmt.close(); rs.close(); } else if (methodName.equalsIgnoreCase("executeSQL")) { - Statement stmt = conn.createStatement(); - retVal = stmt.execute(query); + retVal = stmt.execute(); stmt.close(); } else if (methodName.equalsIgnoreCase("executeUpdateSQL")) { - Statement stmt = conn.createStatement(); - retVal = stmt.executeUpdate(query) > 0; + retVal = stmt.executeUpdate() > 0; stmt.close(); } } catch (SQLException e) { @@ -779,38 +794,53 @@ public boolean executeSQLHelper(String methodName, int n, String name, String pa public boolean executeSQLHelper(String methodName, String ids) { boolean retVal = false; - String query = new String() ; Connection conn = connect(); if (conn == null) return false; - if(ids.contains(",")) { - query = "select ID, NAME from ACCOUNTS WHERE ID IN (" + ids + ")" ; - } else { - query = "select ID from ACCOUNTS WHERE ID = " + ids ; - } + // Security fix: Use PreparedStatement to prevent SQL injection + String[] id_values = ids.split(","); + PreparedStatement stmt = null; try { - if (methodName.equalsIgnoreCase("executeQuerySQL")) { - StringBuffer sbuf = new StringBuffer(); - Statement stmt = conn.createStatement(); - System.out.println("QUERY :" + query); - ResultSet rs = stmt.executeQuery(query); - // Loop through the data and print all artist names - while (rs.next()) { - sbuf.append("Customer id: " + rs.getString("ID")); - System.out.println("Customer id: " + rs.getString("ID")); - sbuf.append("
"); - retVal = sbuf.toString().length() > 2; - } - // Clean up - stmt.close(); - rs.close(); - } else if (methodName.equalsIgnoreCase("PreparedStatementEexecuteQuerySQL")) { - System.out.println("PreparedStatementQUERY :" + query); - PreparedStatement stmt = conn.prepareStatement(query); + if(id_values.length > 1) { + // Build parameterized query for IN clause + StringBuilder queryBuilder = new StringBuilder("select ID, NAME from ACCOUNTS WHERE ID IN ("); + for (int i = 0; i < id_values.length; i++) { + queryBuilder.append("?"); + if (i < id_values.length - 1) { + queryBuilder.append(","); + } + } + queryBuilder.append(")"); + String query = queryBuilder.toString(); + System.out.println("QUERY :" + query); + stmt = conn.prepareStatement(query); + for (int i = 0; i < id_values.length; i++) { + // Validate that each ID is numeric + try { + int id = Integer.parseInt(id_values[i].trim()); + stmt.setInt(i + 1, id); + } catch (NumberFormatException e) { + System.err.println("Invalid ID format: " + id_values[i]); + return false; + } + } + } else { + String query = "select ID from ACCOUNTS WHERE ID = ?"; + System.out.println("QUERY :" + query); + stmt = conn.prepareStatement(query); + try { + int id = Integer.parseInt(ids.trim()); + stmt.setInt(1, id); + } catch (NumberFormatException e) { + System.err.println("Invalid ID format: " + ids); + return false; + } + } + + if (methodName.equalsIgnoreCase("executeQuerySQL") || methodName.equalsIgnoreCase("PreparedStatementEexecuteQuerySQL")) { ResultSet rs = stmt.executeQuery(); - StringBuffer sbuf = new StringBuffer(); // Loop through the data and print all artist names while (rs.next()) { @@ -818,17 +848,15 @@ public boolean executeSQLHelper(String methodName, String ids) { System.out.println("Customer id: " + rs.getString("ID")); sbuf.append("
"); retVal = sbuf.toString().length() > 2; - } + } // Clean up stmt.close(); rs.close(); } else if (methodName.equalsIgnoreCase("executeSQL")) { - Statement stmt = conn.createStatement(); - retVal = stmt.execute(query); + retVal = stmt.execute(); stmt.close(); } else if (methodName.equalsIgnoreCase("executeUpdateSQL")) { - Statement stmt = conn.createStatement(); - retVal = stmt.executeUpdate(query) > 0; + retVal = stmt.executeUpdate() > 0; stmt.close(); } diff --git a/src/main/java/com/endor/ExecuteServlet.java b/src/main/java/com/endor/ExecuteServlet.java index e5ebf869..c60200b5 100644 --- a/src/main/java/com/endor/ExecuteServlet.java +++ b/src/main/java/com/endor/ExecuteServlet.java @@ -34,7 +34,43 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t String command = request.getParameter("command"); String env = request.getParameter("env"); - String[] envArr = env.split(";"); - Runtime.getRuntime().exec(command, envArr); + + // Security fix: Validate and sanitize inputs to prevent command injection + if (command != null && !command.isEmpty()) { + // Only allow alphanumeric characters, spaces, and safe characters + if (!command.matches("[a-zA-Z0-9\\s\\-_.]+")) { + out.println("

Error: Invalid command. Only alphanumeric characters, spaces, hyphens, underscores and dots are allowed.

"); + return; + } + + String[] envArr = null; + if (env != null && !env.isEmpty()) { + envArr = env.split(";"); + // Validate each environment variable + for (String envVar : envArr) { + if (!envVar.matches("[a-zA-Z0-9_]+=.+")) { + out.println("

Error: Invalid environment variable format. Use KEY=value format.

"); + return; + } + } + } + + try { + // Use ProcessBuilder for safer command execution with validated inputs + ProcessBuilder pb = new ProcessBuilder(command.split("\\s+")); + if (envArr != null) { + for (String envVar : envArr) { + String[] keyValue = envVar.split("=", 2); + pb.environment().put(keyValue[0], keyValue[1]); + } + } + Process process = pb.start(); + out.println("

Warning: Command execution is inherently dangerous and should be avoided in production applications.

"); + out.println("

Command executed with validation

"); + } catch (Exception e) { + out.println("

Error executing command. Please contact system administrator.

"); + System.err.println("Command execution error: " + e.getMessage()); + } + } } } diff --git a/src/main/java/com/endor/OSCommandServlet.java b/src/main/java/com/endor/OSCommandServlet.java index 2339661e..0f141b4c 100644 --- a/src/main/java/com/endor/OSCommandServlet.java +++ b/src/main/java/com/endor/OSCommandServlet.java @@ -32,7 +32,27 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t out.println(form); String command = request.getParameter("command"); - String find = "find " + command; - Runtime.getRuntime().exec(find); + + // Security fix: Validate and sanitize input to prevent command injection + if (command != null && !command.isEmpty()) { + // Only allow alphanumeric characters, spaces, dots, slashes and hyphens for file paths + if (!command.matches("[a-zA-Z0-9\\s\\-_./]+")) { + out.println("

Error: Invalid file path. Only alphanumeric characters, spaces, dots, slashes, hyphens and underscores are allowed.

"); + return; + } + + // Use ProcessBuilder for safer command execution with explicit arguments + try { + ProcessBuilder pb = new ProcessBuilder("find", command); + pb.redirectErrorStream(true); + Process process = pb.start(); + out.println("

Warning: Command execution is inherently dangerous and should be avoided in production applications.

"); + out.println("

Command executed (with validation)

"); + } catch (Exception e) { + // Security fix: Don't expose detailed error messages to users + out.println("

Error executing command. Please contact system administrator.

"); + System.err.println("Command execution error: " + e.getMessage()); + } + } } }