public static byte[] convertToJSON( IExtensionHelpers helpers, IHttpRequestResponse requestResponse) { byte[] request = requestResponse.getRequest(); if (Objects.equals(helpers.analyzeRequest(request).getMethod(), "GET")) { request = helpers.toggleRequestMethod(request); } IRequestInfo requestInfo = helpers.analyzeRequest(request); int bodyOffset = requestInfo.getBodyOffset(); byte content_type = requestInfo.getContentType(); String body = new String(request, bodyOffset, request.length - bodyOffset); String json = ""; Boolean success = true; try { if (content_type == 3) { JSONObject xmlJSONObject = XML.toJSONObject(body); json = xmlJSONObject.toString(2); } else if (content_type == 0 || content_type == 1) { Map<String, String> params = splitQuery(body); Gson gson = new Gson(); json = gson.toJson(params); } else { json = body; } } catch (Exception e) { success = false; } if (!success) { return request; } else { List<String> headers; headers = helpers.analyzeRequest(request).getHeaders(); Iterator<String> iter = headers.iterator(); while (iter.hasNext()) { if (iter.next().contains("Content-Type")) iter.remove(); } headers.add("Content-Type: application/json;charset=UTF-8"); return helpers.buildHttpMessage(headers, json.getBytes()); } }
public static byte[] convertToXML(IExtensionHelpers helpers, IHttpRequestResponse requestResponse) throws Exception { byte[] request = requestResponse.getRequest(); if (Objects.equals(helpers.analyzeRequest(request).getMethod(), "GET")) { request = helpers.toggleRequestMethod(request); } IRequestInfo requestInfo = helpers.analyzeRequest(request); int bodyOffset = requestInfo.getBodyOffset(); byte content_type = requestInfo.getContentType(); String body = new String(request, bodyOffset, request.length - bodyOffset, "UTF-8"); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); Document doc = new Document() { public String getNodeName() { return null; } public String getNodeValue() throws DOMException { return null; } public void setNodeValue(String nodeValue) throws DOMException {} public short getNodeType() { return 0; } public Node getParentNode() { return null; } public NodeList getChildNodes() { return null; } public Node getFirstChild() { return null; } public Node getLastChild() { return null; } public Node getPreviousSibling() { return null; } public Node getNextSibling() { return null; } public NamedNodeMap getAttributes() { return null; } public Document getOwnerDocument() { return null; } public Node insertBefore(Node newChild, Node refChild) throws DOMException { return null; } public Node replaceChild(Node newChild, Node oldChild) throws DOMException { return null; } public Node removeChild(Node oldChild) throws DOMException { return null; } public Node appendChild(Node newChild) throws DOMException { return null; } public boolean hasChildNodes() { return false; } public Node cloneNode(boolean deep) { return null; } public void normalize() {} public boolean isSupported(String feature, String version) { return false; } public String getNamespaceURI() { return null; } public String getPrefix() { return null; } public void setPrefix(String prefix) throws DOMException {} public String getLocalName() { return null; } public boolean hasAttributes() { return false; } public String getBaseURI() { return null; } public short compareDocumentPosition(Node other) throws DOMException { return 0; } public String getTextContent() throws DOMException { return null; } public void setTextContent(String textContent) throws DOMException {} public boolean isSameNode(Node other) { return false; } public String lookupPrefix(String namespaceURI) { return null; } public boolean isDefaultNamespace(String namespaceURI) { return false; } public String lookupNamespaceURI(String prefix) { return null; } public boolean isEqualNode(Node arg) { return false; } public Object getFeature(String feature, String version) { return null; } public Object setUserData(String key, Object data, UserDataHandler handler) { return null; } public Object getUserData(String key) { return null; } public DocumentType getDoctype() { return null; } public DOMImplementation getImplementation() { return null; } public Element getDocumentElement() { return null; } public Element createElement(String tagName) throws DOMException { return null; } public DocumentFragment createDocumentFragment() { return null; } public Text createTextNode(String data) { return null; } public Comment createComment(String data) { return null; } public CDATASection createCDATASection(String data) throws DOMException { return null; } public ProcessingInstruction createProcessingInstruction(String target, String data) throws DOMException { return null; } public Attr createAttribute(String name) throws DOMException { return null; } public EntityReference createEntityReference(String name) throws DOMException { return null; } public NodeList getElementsByTagName(String tagname) { return null; } public Node importNode(Node importedNode, boolean deep) throws DOMException { return null; } public Element createElementNS(String namespaceURI, String qualifiedName) throws DOMException { return null; } public Attr createAttributeNS(String namespaceURI, String qualifiedName) throws DOMException { return null; } public NodeList getElementsByTagNameNS(String namespaceURI, String localName) { return null; } public Element getElementById(String elementId) { return null; } public String getInputEncoding() { return null; } public String getXmlEncoding() { return null; } public boolean getXmlStandalone() { return false; } public void setXmlStandalone(boolean xmlStandalone) throws DOMException {} public String getXmlVersion() { return null; } public void setXmlVersion(String xmlVersion) throws DOMException {} public boolean getStrictErrorChecking() { return false; } public void setStrictErrorChecking(boolean strictErrorChecking) {} public String getDocumentURI() { return null; } public void setDocumentURI(String documentURI) {} public Node adoptNode(Node source) throws DOMException { return null; } public DOMConfiguration getDomConfig() { return null; } public void normalizeDocument() {} public Node renameNode(Node n, String namespaceURI, String qualifiedName) throws DOMException { return null; } }; StringBuilder xml = new StringBuilder(); xml.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"); xml.append("<root>"); if (content_type == 0 || content_type == 1) { Map<String, String> params = splitQuery(body); Gson gson = new Gson(); body = gson.toJson(params); } Boolean success = true; try { JSONObject json = new JSONObject(body); xml.append(XML.toString(json)); xml.append("</root>"); DocumentBuilder builder = factory.newDocumentBuilder(); ByteArrayInputStream input = new ByteArrayInputStream(xml.toString().getBytes("UTF-8")); doc = builder.parse(input); } catch (Exception e) { success = false; } if (!success) { return null; } else { List<String> headers; headers = helpers.analyzeRequest(request).getHeaders(); Iterator<String> iter = headers.iterator(); while (iter.hasNext()) { if (iter.next().contains("Content-Type")) iter.remove(); } headers.add("Content-Type: application/xml;charset=UTF-8"); return helpers.buildHttpMessage(headers, prettyPrint(doc).getBytes()); } }
public static void getIssues( String software, String release, IBurpExtenderCallbacks callbacks, IHttpRequestResponse baseRequestResponse) { IExtensionHelpers helpers = callbacks.getHelpers(); /** Apache Tomcat */ if (software.equalsIgnoreCase("Apache Tomcat")) { /** End of Life - Apache Tomcat */ if (Integer.parseInt(release.substring(0, 1)) <= 5) { callbacks.addScanIssue( new CustomScanIssue( baseRequestResponse.getHttpService(), helpers.analyzeRequest(baseRequestResponse).getUrl(), baseRequestResponse, "End of Life Software - Apache Tomcat " + release, "J2EEScan identified an unsupported release of Apache Tomcat <b>" + release + "</b>.<br />" + "No more security updates for this version will be released by Apache <br /><br />" + "<b>References</b><br />" + "http://tomcat.apache.org/tomcat-55-eol.html<br />", "Update the Apache Servlet Container with the last stable release", Risk.High, Confidence.Certain)); } } /** Jetty */ if (software.equalsIgnoreCase("Jetty")) { /** End of Life - Jetty */ if (Integer.parseInt(release.substring(0, 1)) < 9) { callbacks.addScanIssue( new CustomScanIssue( baseRequestResponse.getHttpService(), helpers.analyzeRequest(baseRequestResponse).getUrl(), baseRequestResponse, "End of Life Software - Jetty " + release, "J2EEScan identified an unsupported release of Jetty <b>" + release + "</b>.<br />" + "No more security updates for this version will be released by the vendor <br /><br />" + "<b>References</b><br />" + "https://webtide.com/jetty-7-and-jetty-8-end-of-life/<br />", "Update the Jetty Container with the last stable release", Risk.High, Confidence.Certain)); } } /** Oracle Application Server */ if (software.equalsIgnoreCase("Oracle Application Server")) { /** End of Life - Oracle Application Server */ if (release.startsWith("9.") || release.startsWith("10.1.2")) { callbacks.addScanIssue( new CustomScanIssue( baseRequestResponse.getHttpService(), helpers.analyzeRequest(baseRequestResponse).getUrl(), baseRequestResponse, "End of Life Software - Oracle Application Server " + release, "J2EEScan identified an unsupported release of Oracle Application Server <b>" + release + "</b>.<br />" + "No more security updates for this version will be released by the vendor <br /><br />" + "<b>References</b><br />" + "http://www.oracle.com/us/support/library/lifetime-support-middleware-069163.pdf<br />", "Update the Oracle Application Server with the last stable release", Risk.High, Confidence.Tentative)); } } }
public static void analyzeWEBXML( byte[] webxmlFile, IBurpExtenderCallbacks cb, IHttpRequestResponse baseRequestResponse) { IExtensionHelpers helpers = cb.getHelpers(); PrintWriter stderr = new PrintWriter(cb.getStderr(), true); Pattern pattern = Pattern.compile("(<web-app.*?</web-app>)", Pattern.DOTALL | Pattern.MULTILINE); String webxml = helpers.bytesToString(webxmlFile); Matcher matcher = pattern.matcher(webxml); if (matcher.find()) { try { String webxmlContent = matcher.group(1); DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder dBuilder; dBuilder = dbFactory.newDocumentBuilder(); InputSource is = new InputSource(new StringReader(webxmlContent)); Document doc = dBuilder.parse(is); /** * HTTP VERB Tampering * * <p>http://docs.oracle.com/cd/E14571_01/web.1111/e13712/web_xml.htm#WBAPP502 * https://weblogs.java.net/blog/swchan2/archive/2013/04/19/deny-uncovered-http-methods-servlet-31 * http-method should not be defined, to restrict access to a resources using HTTP verbs In * Servlet 3.1 spec, the attribute "deny-uncovered-http-methods" could be used to deny * uncovered HTTP verbs */ try { NodeList httpMethods = doc.getElementsByTagName("http-method"); if ((httpMethods != null) && (httpMethods.getLength() >= 1)) { cb.addScanIssue( new CustomScanIssue( baseRequestResponse.getHttpService(), helpers.analyzeRequest(baseRequestResponse).getUrl(), baseRequestResponse, "Compliance Checks - web.xml - HTTP Verb Tampering", "J2EEScan identified a potential HTTP Verb Tampering vulnerability inspecting" + " the remote web.xml resource.<br /><br />" + "One or more resources defined into the web.xml uses some definitions to restrict " + "access based on the HTTP Verb used with requests; based on this context, in some scenarios " + "it's possible to bypass these resctrictions providing a different HTTP verb to access to the remote resource." + "<br /> This allows the attacker to access data that should otherwise be protected." + "<br /><br />An example of vulnerable configuration that could lead to Authentication Bypass vulnerabilities:<br /><br />" + "<div style=\"font: courier;\"><pre>" + "<security-constraint>\n" + " <display-name>\n" + " Protect GET only, leave all other methods unprotected\n" + " </display-name>\n" + " <web-resource-collection>\n" + " <url-pattern>/company/*</url-pattern>\n" + " <http-method>GET</http-method>\n" + " </web-resource-collection>\n" + " <auth-constraint>\n" + " <role-name>sales</role-name>\n" + " </auth-constraint>\n" + "</security-constraint>" + "</pre></div> " + "<br />" + "<br /><b>References:</b><br />" + "https://www.owasp.org/index.php/Testing_for_HTTP_Verb_Tampering_(OTG-INPVAL-003)<br />" + "http://www.aspectsecurity.com/research-presentations/bypassing-vbaac-with-http-verb-tampering<br />" + "http://capec.mitre.org/data/definitions/274.html<br />" + "http://jeremiahgrossman.blogspot.it/2008/06/what-you-need-to-know-about-http-verb.html", "Remove <i>http-method</i> elements to avoid possible HTTP Verb Tampering attacks", Risk.Medium, Confidence.Tentative)); } } catch (Exception ex) { ex.printStackTrace(stderr); } /** * URL Parameters for Session Tracking * http://docs.oracle.com/javaee/6/api/javax/servlet/SessionTrackingMode.html */ try { NodeList sessionTracking = doc.getElementsByTagName("tracking-mode"); if ((sessionTracking != null) && (sessionTracking.getLength() >= 1)) { String value = sessionTracking.item(0).getTextContent(); if (value.equalsIgnoreCase("URL")) { cb.addScanIssue( new CustomScanIssue( baseRequestResponse.getHttpService(), helpers.analyzeRequest(baseRequestResponse).getUrl(), baseRequestResponse, "Compliance Checks- web.xml - URL Parameters for Session Tracking", "J2EEScan identified a potential Information Disclosure vulnerabilitiy inspecting" + " the remote web.xml resource.<br /><br />" + "The remote applications seems to put the JSESSIONID into the URL using the directive <i>tracking-mode</i> with the URL value;<br />" + "the tracking-mode element in the Servlet 3.0 specification allows to define whether the JSESSIONID should be stored in a cookie or in a URL parameter. <br />" + "If the session id is stored in a URL parameter it could lead to and Information Disclosure vulnerability, because the URLs could be inadvertently " + "saved in browser history, proxy server logs, referrer logs etc. <br />" + "<br /><b>References:</b><br />" + "http://software-security.sans.org/blog/2010/08/11/security-misconfigurations-java-webxml-files<br />", "Change the <i>tracking-mode</i> value to avoid possible Information Disclosure vulnerabilities", Risk.Low, Confidence.Tentative)); } } } catch (Exception ex) { ex.printStackTrace(stderr); } /** * Incomplete Error Handling * * <p>https://blog.whitehatsec.com/error-handling-in-java-web-xml/ * http://software-security.sans.org/blog/2010/08/11/security-misconfigurations-java-webxml-files * http://www.jtmelton.com/2010/06/02/the-owasp-top-ten-and-esapi-part-7-information-leakage-and-improper-error-handling/ */ try { NodeList exceptionType = doc.getElementsByTagName("exception-type"); Boolean incompleteErrorHandling = true; int excTypeLen = exceptionType.getLength(); for (int i = 0; i < excTypeLen; i++) { Node s = exceptionType.item(i); String value = s.getTextContent(); if (value.equalsIgnoreCase("java.lang.Throwable")) { incompleteErrorHandling = false; break; } } if (incompleteErrorHandling) { cb.addScanIssue( new CustomScanIssue( baseRequestResponse.getHttpService(), helpers.analyzeRequest(baseRequestResponse).getUrl(), baseRequestResponse, "Compliance Checks - web.xml - Incomplete Error Handling (Throwable)", "J2EEScan identified a potential Information Disclosure vulnerabilitiy inspecting" + " the remote web.xml resource.<br /><br />" + "The remote application seems to not correctly handle application errors; the web.xml does not " + "provide an error page for <i>java.lang.Throwable</i> exceptions.<br /><br />" + "<b>References:</b><br />" + "https://blog.whitehatsec.com/error-handling-in-java-web-xml/<br />" + "http://software-security.sans.org/blog/2010/08/11/security-misconfigurations-java-webxml-files<br />", "Modify the error handling catching the <i>java.lang.Throwable</i> exception to avoid possible Information Disclosure vulnerabilities<br /><br />" + "<div style=\"font: courier;\"><pre>" + "<error-page>\n" + " <error-code>404</error-code>\n" + " <location>/error.jsp</location>\n" + "</error-page>\n" + "<error-page>\n" + " <error-code>500</error-code>\n" + " <location>/error.jsp</location>\n" + "</error-page>\n" + "<error-page>\n" + " <exception-type>java.lang.Throwable</exception-type>\n" + " <location>/error.jsp</location>\n" + "</error-page>\n" + "</pre></div>", Risk.Low, Confidence.Tentative)); } } catch (Exception ex) { ex.printStackTrace(stderr); } try { NodeList exceptionType = doc.getElementsByTagName("error-code"); Boolean incompleteErrorHandling500 = true; int excTypeLen = exceptionType.getLength(); for (int i = 0; i < excTypeLen; i++) { Node s = exceptionType.item(i); String value = s.getTextContent(); if (value.equalsIgnoreCase("500")) { incompleteErrorHandling500 = false; break; } } if (incompleteErrorHandling500) { cb.addScanIssue( new CustomScanIssue( baseRequestResponse.getHttpService(), helpers.analyzeRequest(baseRequestResponse).getUrl(), baseRequestResponse, "Compliance Checks - web.xml - Incomplete Error Handling (HTTP Code 500)", "J2EEScan identified a potential Information Disclosure vulnerabilitiy inspecting" + " the remote web.xml resource.<br /><br />" + "The remote application seems to not correctly handle application errors; the web.xml does not " + "provide an error page for <i>500</i> HTTP code.<br /><br />" + "<b>References:</b><br />" + "https://blog.whitehatsec.com/error-handling-in-java-web-xml/<br />" + "http://software-security.sans.org/blog/2010/08/11/security-misconfigurations-java-webxml-files<br />", "Modify the error handling catching the <i>500</i> error code to avoid possible Information Disclosure vulnerabilities<br /><br />" + "<div style=\"font: courier;\"><pre>" + "<error-page>\n" + " <error-code>404</error-code>\n" + " <location>/error.jsp</location>\n" + "</error-page>\n" + "<error-page>\n" + " <error-code>500</error-code>\n" + " <location>/error.jsp</location>\n" + "</error-page>\n" + "<error-page>\n" + " <exception-type>java.lang.Throwable</exception-type>\n" + " <location>/error.jsp</location>\n" + "</error-page>\n" + "</pre></div>", Risk.Low, Confidence.Tentative)); } } catch (Exception ex) { ex.printStackTrace(stderr); } try { NodeList exceptionType = doc.getElementsByTagName("error-code"); Boolean incompleteErrorHandling404 = true; int excTypeLen = exceptionType.getLength(); for (int i = 0; i < excTypeLen; i++) { Node s = exceptionType.item(i); String value = s.getTextContent(); if (value.equalsIgnoreCase("404")) { incompleteErrorHandling404 = false; break; } } if (incompleteErrorHandling404) { cb.addScanIssue( new CustomScanIssue( baseRequestResponse.getHttpService(), helpers.analyzeRequest(baseRequestResponse).getUrl(), baseRequestResponse, "Compliance Checks - web.xml - Incomplete Error Handling (HTTP Code 404)", "J2EEScan identified a potential Information Disclosure vulnerabilitiy inspecting" + " the remote web.xml resource.<br /><br />" + "The remote application seems to not correctly handle application errors; the web.xml does not " + "provide an error page for <i>404</i> HTTP code.<br /><br />" + "<b>References:</b><br />" + "https://blog.whitehatsec.com/error-handling-in-java-web-xml/<br />" + "http://software-security.sans.org/blog/2010/08/11/security-misconfigurations-java-webxml-files<br />", "Modify the error handling catching the <i>404</i> error code to avoid possible Information Disclosure vulnerabilities<br /><br />" + "<div style=\"font: courier;\"><pre>" + "<error-page>\n" + " <error-code>404</error-code>\n" + " <location>/error.jsp</location>\n" + "</error-page>\n" + "<error-page>\n" + " <error-code>500</error-code>\n" + " <location>/error.jsp</location>\n" + "</error-page>\n" + "<error-page>\n" + " <exception-type>java.lang.Throwable</exception-type>\n" + " <location>/error.jsp</location>\n" + "</error-page>\n" + "</pre></div>", Risk.Low, Confidence.Tentative)); } } catch (Exception ex) { ex.printStackTrace(stderr); } /** InvokerServlet */ try { NodeList exceptionType = doc.getElementsByTagName("servlet-class"); int excTypeLen = exceptionType.getLength(); for (int i = 0; i < excTypeLen; i++) { Node s = exceptionType.item(i); String value = s.getTextContent(); if (value.contains("InvokerServlet")) { cb.addScanIssue( new CustomScanIssue( baseRequestResponse.getHttpService(), helpers.analyzeRequest(baseRequestResponse).getUrl(), baseRequestResponse, "Compliance Checks - web.xml - Invoker Servlet", "J2EEScan identified the <i>InvokerServlet</> enabled inspecting" + " the remote web.xml resource.<br /><br />" + "It allows any class in your classpath to be accessed, as long as the class is a valid servlet. <br />" + "This functionality could potentially introduces a security risk; different servlets could be directly accessed from remote bypassing any Authorization Layers<br /><br />" + "<b>References:</b><br />" + "http://www.coderanch.com/how-to/java/InvokerServlet<br />" + "https://tomcat.apache.org/tomcat-4.1-doc/catalina/funcspecs/fs-invoker.html<br />", "Disable or restrict access to the remote InvokerServlet", Risk.Medium, Confidence.Tentative)); break; } } } catch (Exception ex) { ex.printStackTrace(stderr); } } catch (ParserConfigurationException | SAXException | IOException ex) { ex.printStackTrace(stderr); } } }
/** * Analyze and categorize each of the parameters in scope. * * @param helpers The standard burp ExtensionHelpers object. * @param messages The set of request messages to be processed. */ private void firstPass(IExtensionHelpers helpers, IHttpRequestResponse[] messages) { publish("Examining parameters..."); for (int i = 0; i < messages.length; i++) { publish(100 * i / messages.length); messages[i].getHttpService(); // Analyze response for cookies if (messages[i].getResponse() != null) { IResponseInfo responseInfo = helpers.analyzeResponse(messages[i].getResponse()); List<String> headers = responseInfo.getHeaders(); for (String header : headers) { if (startsWithIgnoreCase(header, "set-cookie:")) { processCookieHeader(header); } } } IRequestInfo requestInfo = helpers.analyzeRequest(messages[i]); if (callbacks.isInScope(requestInfo.getUrl())) { byte[] responseBytes = messages[i].getResponse(); String responseString = ""; if (responseBytes != null) { responseString = helpers.bytesToString(responseBytes); inScopeMessagesWithResponses.add(messages[i]); } List<IParameter> params = requestInfo.getParameters(); for (IParameter param : params) { if ((!ignoreEmpty || param.getValue().length() > 0) && !ignoreList.contains(param.getName())) { int type = param.getType(); Map<String, CorrelatedParam> paramMap; switch (type) { case IParameter.PARAM_URL: paramMap = urlParameters; break; case IParameter.PARAM_BODY: paramMap = bodyParameters; break; case IParameter.PARAM_COOKIE: paramMap = cookieParameters; break; case IParameter.PARAM_JSON: paramMap = jsonParameters; break; default: paramMap = null; // nothing } if (paramMap != null) { if (messages[i] == null) { callbacks.printOutput("Warning... adding null message!"); } if (paramMap.containsKey(param.getName())) { paramMap .get(param.getName()) .put(param, messages[i], requestInfo, responseString, helpers); } else { paramMap.put( param.getName(), new CorrelatedParam(param, messages[i], requestInfo, responseString, helpers)); } } } } } } }