List of usage examples for org.apache.commons.httpclient URI URI
public URI(String scheme, String userinfo, String host, int port, String path) throws URIException
From source file:org.zaproxy.zap.extension.ascanrulesBeta.BackupFileDisclosure.java
/** * attempts to find a backup file for the given file * * @param uri the URI of a file/*from w w w.java 2 s . co m*/ * @return */ private void findBackupFile(HttpMessage originalMessage) throws Exception { try { boolean gives404s = true; boolean parentgives404s = true; byte[] nonexistparentmsgdata = null; URI originalURI = originalMessage.getRequestHeader().getURI(); // request a file in the same directory to see how it handles "File not found". Using a // 404? Something else? String temppath = originalURI.getPath(); if (temppath == null) temppath = ""; int slashposition = temppath.lastIndexOf("/"); if (slashposition < 0) { // WTF? there was no slash in the path.. throw new Exception("The message has a path with a malformed path component"); } String filename = originalMessage.getRequestHeader().getURI().getName(); String randomfilename = RandomStringUtils.random(filename.length(), "abcdefghijklmoopqrstuvwxyz9123456789"); String randomfilepath = temppath.substring(0, slashposition) + "/" + randomfilename; if (log.isDebugEnabled()) log.debug("Trying non-existent file: " + randomfilepath); HttpMessage nonexistfilemsg = new HttpMessage( new URI(originalURI.getScheme(), originalURI.getAuthority(), randomfilepath, null, null)); try { nonexistfilemsg.setCookieParams(originalMessage.getCookieParams()); } catch (Exception e) { if (log.isDebugEnabled()) log.debug("Could not set the cookies from the base request:" + e); } sendAndReceive(nonexistfilemsg, false); byte[] nonexistfilemsgdata = nonexistfilemsg.getResponseBody().getBytes(); // does the server give a 404 for a non-existent file? if (nonexistfilemsg.getResponseHeader().getStatusCode() != HttpStatus.SC_NOT_FOUND) { gives404s = false; if (log.isDebugEnabled()) log.debug("The server does not return a 404 status for a non-existent path: " + nonexistfilemsg.getRequestHeader().getURI().getURI()); } else { gives404s = true; if (log.isDebugEnabled()) log.debug("The server gives a 404 status for a non-existent path: " + nonexistfilemsg.getRequestHeader().getURI().getURI()); } // now request a different (and non-existent) parent directory, // to see whether a non-existent parent folder causes a 404 String[] pathbreak = temppath.split("/"); HttpMessage nonexistparentmsg = null; if (pathbreak.length > 2) { // the file has a parent folder that is not the root folder (ie, there is // a parent folder to mess with) String[] temppathbreak = pathbreak; String parentfoldername = pathbreak[pathbreak.length - 2]; String randomparentfoldername = RandomStringUtils.random(parentfoldername.length(), "abcdefghijklmoopqrstuvwxyz9123456789"); // replace the parent folder name with the random one, and build it back into a // string temppathbreak[pathbreak.length - 2] = randomparentfoldername; String randomparentpath = StringUtils.join(temppathbreak, "/"); if (log.isDebugEnabled()) log.debug("Trying non-existent parent path: " + randomparentpath); nonexistparentmsg = new HttpMessage( new URI(originalURI.getScheme(), originalURI.getAuthority(), randomparentpath, null, null)); try { nonexistparentmsg.setCookieParams(originalMessage.getCookieParams()); } catch (Exception e) { if (log.isDebugEnabled()) log.debug("Could not set the cookies from the base request:" + e); } sendAndReceive(nonexistparentmsg, false); nonexistparentmsgdata = nonexistparentmsg.getResponseBody().getBytes(); // does the server give a 404 for a non-existent parent folder? if (nonexistparentmsg.getResponseHeader().getStatusCode() != HttpStatus.SC_NOT_FOUND) { parentgives404s = false; if (log.isDebugEnabled()) log.debug("The server does not return a 404 status for a non-existent parent path: " + nonexistparentmsg.getRequestHeader().getURI().getURI()); } else { parentgives404s = true; if (log.isDebugEnabled()) log.debug("The server gives a 404 status for a non-existent parent path: " + nonexistparentmsg.getRequestHeader().getURI().getURI()); } } String actualfilename = originalURI.getName(); String actualfileExtension = null; String path = originalURI.getPath(); if (path == null) path = ""; // record the position of the various injection points, always relative to the full path int positionExtensionInjection = 0; int positionFileSuffixInjection = 0; if (actualfilename.contains(".")) { positionExtensionInjection = path.lastIndexOf("."); positionFileSuffixInjection = positionExtensionInjection; actualfileExtension = actualfilename.substring(actualfilename.lastIndexOf(".")); } else { positionExtensionInjection = path.length(); positionFileSuffixInjection = path.length(); actualfileExtension = ""; } int positionFilePrefixInjection = path.lastIndexOf("/") + 1; int positionDirectorySuffixInjection = path.lastIndexOf("/"); int positionDirectoryPrefixInjection = 0; if (positionDirectorySuffixInjection >= 0) positionDirectoryPrefixInjection = path.substring(0, positionDirectorySuffixInjection) .lastIndexOf("/") + 1; // the set of files we will try, in the order of insertion Set<URI> candidateBackupFileURIs = new LinkedHashSet<URI>(); Set<URI> candidateBackupFileChangedFolderURIs = new LinkedHashSet<URI>(); // for a changed parent folder name, which we need to handle // separately log.debug("The path is " + path); // for each file extension to try (both appending, and replacing) int counted = 0; for (String fileExtensionToTry : fileExtensions) { // to append, inject the file extension at the end of the path String candidateBackupFilePath = path + fileExtensionToTry; log.debug("File Extension (append): '" + candidateBackupFilePath + "'"); candidateBackupFileURIs.add(new URI(originalURI.getScheme(), originalURI.getAuthority(), candidateBackupFilePath, null, null)); // to replace the extension, append the file extension at positionExtensionInjection candidateBackupFilePath = path.substring(0, positionExtensionInjection) + fileExtensionToTry; log.debug("File Extension (replace): '" + candidateBackupFilePath + "'"); candidateBackupFileURIs.add(new URI(originalURI.getScheme(), originalURI.getAuthority(), candidateBackupFilePath, null, null)); // to switch the extension (if there was one), append the file extension at // positionExtensionInjection if (!actualfileExtension.equals("") && doSwitchFileExtension) { candidateBackupFilePath = path.substring(0, positionExtensionInjection) + fileExtensionToTry + actualfileExtension; log.debug("File Extension (switch): '" + candidateBackupFilePath + "'"); candidateBackupFileURIs.add(new URI(originalURI.getScheme(), originalURI.getAuthority(), candidateBackupFilePath, null, null)); } counted++; if (counted > numExtensionsToTry) { break; // out of the loop. } } // for each file suffix to try counted = 0; for (String fileSuffixToTry : fileSuffixes) { // inject the file suffix at positionFileSuffixInjection String candidateBackupFilePath = path.substring(0, positionFileSuffixInjection) + fileSuffixToTry + (positionFileSuffixInjection >= path.length() ? "" : path.substring(positionFileSuffixInjection)); log.debug("File Suffix (insert): '" + candidateBackupFilePath + "'"); candidateBackupFileURIs.add(new URI(originalURI.getScheme(), originalURI.getAuthority(), candidateBackupFilePath, null, null)); counted++; if (counted > numSuffixesToTry) { break; // out of the loop. } } // for each file prefix to try counted = 0; for (String filePrefixToTry : filePrefixes) { // inject the file prefix at positionFilePrefixInjection String candidateBackupFilePath = path.substring(0, positionFilePrefixInjection) + filePrefixToTry + (positionFilePrefixInjection >= path.length() ? "" : path.substring(positionFilePrefixInjection)); log.debug("File Prefix (insert): '" + candidateBackupFilePath + "'"); candidateBackupFileURIs.add(new URI(originalURI.getScheme(), originalURI.getAuthority(), candidateBackupFilePath, null, null)); counted++; if (counted > numPrefixesToTry) { break; // out of the loop. } } // for each directory suffix/prefix to try (using the file prefixes/suffixes - or // whatever the plural of prefix/suffix is) counted = 0; if (pathbreak.length > 2) { // if there is a a parent folder to play with for (String fileSuffixToTry : fileSuffixes) { // inject the directory suffix at positionDirectorySuffixInjection String candidateBackupFilePath = path.substring(0, positionDirectorySuffixInjection) + fileSuffixToTry + (positionDirectorySuffixInjection >= path.length() ? "" : path.substring(positionDirectorySuffixInjection)); log.debug("Directory Suffix (insert): '" + candidateBackupFilePath + "'"); candidateBackupFileChangedFolderURIs.add(new URI(originalURI.getScheme(), originalURI.getAuthority(), candidateBackupFilePath, null, null)); counted++; if (counted > numSuffixesToTry) { break; // out of the loop. } } for (String filePrefixToTry : filePrefixes) { // inject the directory prefix at positionDirectorySuffixInjection String candidateBackupFilePath = path.substring(0, positionDirectoryPrefixInjection) + filePrefixToTry + (positionDirectoryPrefixInjection >= path.length() ? "" : path.substring(positionDirectoryPrefixInjection)); log.debug("Directory Suffix (insert): '" + candidateBackupFilePath + "'"); candidateBackupFileChangedFolderURIs.add(new URI(originalURI.getScheme(), originalURI.getAuthority(), candidateBackupFilePath, null, null)); counted++; if (counted > numSuffixesToTry) { break; // out of the loop. } } } // now we have a set of candidate URIs appropriate to the attack strength chosen by the // user // try each candidate URI in turn. for (URI candidateBackupFileURI : candidateBackupFileURIs) { byte[] disclosedData = {}; if (log.isDebugEnabled()) log.debug("Trying possible backup file path: " + candidateBackupFileURI.getURI()); HttpMessage requestmsg = new HttpMessage(candidateBackupFileURI); try { requestmsg.setCookieParams(originalMessage.getCookieParams()); } catch (Exception e) { if (log.isDebugEnabled()) log.debug("Could not set the cookies from the base request:" + e); } // Do not follow redirects. They're evil. Yep. sendAndReceive(requestmsg, false); disclosedData = requestmsg.getResponseBody().getBytes(); int requestStatusCode = requestmsg.getResponseHeader().getStatusCode(); // just to complicate things.. I have a test case which for the random file, does // NOT give a 404 (so gives404s == false) // but for a "Copy of" file, actually gives a 404 (for some unknown reason). We need // to handle this case. if (!isEmptyResponse(disclosedData) && ((gives404s && requestStatusCode != HttpStatus.SC_NOT_FOUND) || ((!gives404s) && nonexistfilemsg.getResponseHeader().getStatusCode() != requestStatusCode && (!Arrays.equals(disclosedData, nonexistfilemsgdata))))) { bingo(Alert.RISK_MEDIUM, Alert.CONFIDENCE_MEDIUM, Constant.messages.getString("ascanbeta.backupfiledisclosure.name"), Constant.messages.getString("ascanbeta.backupfiledisclosure.desc"), requestmsg.getRequestHeader().getURI().getURI(), // originalMessage.getRequestHeader().getURI().getURI(), null, // parameter being attacked: none. candidateBackupFileURI.getURI(), // attack originalMessage.getRequestHeader().getURI().getURI(), // new String (disclosedData), //extrainfo Constant.messages.getString("ascanbeta.backupfiledisclosure.soln"), Constant.messages.getString("ascanbeta.backupfiledisclosure.evidence", originalURI, candidateBackupFileURI.getURI()), requestmsg // originalMessage ); } if (isStop()) { if (log.isDebugEnabled()) log.debug("The scanner was stopped in response to a user request"); return; } } // now try the changed parent folders (if any) // the logic here needs to check using the parent 404 logic, and the output for a // non-existent parent folder. for (URI candidateBackupFileURI : candidateBackupFileChangedFolderURIs) { byte[] disclosedData = {}; if (log.isDebugEnabled()) log.debug("Trying possible backup file path (with changed parent folder): " + candidateBackupFileURI.getURI()); HttpMessage requestmsg = new HttpMessage(candidateBackupFileURI); try { requestmsg.setCookieParams(originalMessage.getCookieParams()); } catch (Exception e) { if (log.isDebugEnabled()) log.debug("Could not set the cookies from the base request:" + e); } // Do not follow redirects. They're evil. Yep. sendAndReceive(requestmsg, false); disclosedData = requestmsg.getResponseBody().getBytes(); int requestStatusCode = requestmsg.getResponseHeader().getStatusCode(); // If the response is empty it's probably not really a backup if (!isEmptyResponse(disclosedData) && ((parentgives404s && requestStatusCode != HttpStatus.SC_NOT_FOUND) || ((!parentgives404s) && nonexistparentmsg.getResponseHeader().getStatusCode() != requestStatusCode && (!Arrays.equals(disclosedData, nonexistparentmsgdata))))) { bingo(Alert.RISK_MEDIUM, Alert.CONFIDENCE_MEDIUM, Constant.messages.getString("ascanbeta.backupfiledisclosure.name"), Constant.messages.getString("ascanbeta.backupfiledisclosure.desc"), requestmsg.getRequestHeader().getURI().getURI(), // originalMessage.getRequestHeader().getURI().getURI(), null, // parameter being attacked: none. candidateBackupFileURI.getURI(), // attack originalMessage.getRequestHeader().getURI().getURI(), // new String (disclosedData), //extrainfo Constant.messages.getString("ascanbeta.backupfiledisclosure.soln"), Constant.messages.getString("ascanbeta.backupfiledisclosure.evidence", originalURI, candidateBackupFileURI.getURI()), requestmsg // originalMessage ); } if (isStop()) { if (log.isDebugEnabled()) log.debug("The scanner was stopped in response to a user request"); return; } } } catch (Exception e) { log.error("Some error occurred when looking for a backup file for '" + originalMessage.getRequestHeader().getURI(), e); return; } }
From source file:org.zaproxy.zap.extension.ascanrulesBeta.CrossDomainScanner.java
private void scanAdobeCrossdomainPolicyFile(URI originalURI) throws IOException, XPathExpressionException { // retrieve the Adobe cross domain policy file, and assess it HttpMessage crossdomainmessage = new HttpMessage(new URI(originalURI.getScheme(), originalURI.getAuthority(), "/" + ADOBE_CROSS_DOMAIN_POLICY_FILE, null, null)); sendAndReceive(crossdomainmessage, false); if (crossdomainmessage.getResponseBody().length() == 0) { return;/*from ww w .j a va2 s . c o m*/ } byte[] crossdomainmessagebytes = crossdomainmessage.getResponseBody().getBytes(); // parse the file. If it's not parseable, it might have been because of a 404 try { // work around the "no protocol" issue by wrapping the content in a ByteArrayInputStream Document adobeXmldoc = docBuilder .parse(new InputSource(new ByteArrayInputStream(crossdomainmessagebytes))); // check for cross domain read (data load) access XPathExpression exprAllowAccessFromDomain = xpath .compile("/cross-domain-policy/allow-access-from/@domain"); // gets the domain // attributes NodeList exprAllowAccessFromDomainNodes = (NodeList) exprAllowAccessFromDomain.evaluate(adobeXmldoc, XPathConstants.NODESET); for (int i = 0; i < exprAllowAccessFromDomainNodes.getLength(); i++) { String domain = exprAllowAccessFromDomainNodes.item(i).getNodeValue(); if (domain.equals("*")) { // oh dear me. bingo(getRisk(), Alert.CONFIDENCE_MEDIUM, Constant.messages.getString(MESSAGE_PREFIX_ADOBE_READ + "name"), Constant.messages.getString(MESSAGE_PREFIX_ADOBE + "desc"), crossdomainmessage.getRequestHeader().getURI().getURI(), // the url field "", // parameter being attacked: none. "", // attack Constant.messages.getString(MESSAGE_PREFIX_ADOBE_READ + "extrainfo", "/" + ADOBE_CROSS_DOMAIN_POLICY_FILE), // extrainfo Constant.messages.getString(MESSAGE_PREFIX_ADOBE_READ + "soln"), // solution "<allow-access-from domain=\"*\"", // evidence crossdomainmessage // the message on which to place the alert ); } } // check for cross domain send (upload) access XPathExpression exprRequestHeadersFromDomain = xpath .compile("/cross-domain-policy/allow-http-request-headers-from/@domain"); // gets // the // domain attributes NodeList exprRequestHeadersFromDomainNodes = (NodeList) exprRequestHeadersFromDomain .evaluate(adobeXmldoc, XPathConstants.NODESET); for (int i = 0; i < exprRequestHeadersFromDomainNodes.getLength(); i++) { String domain = exprRequestHeadersFromDomainNodes.item(i).getNodeValue(); if (domain.equals("*")) { // oh dear, dear me. bingo(getRisk(), Alert.CONFIDENCE_MEDIUM, Constant.messages.getString(MESSAGE_PREFIX_ADOBE_SEND + "name"), Constant.messages.getString(MESSAGE_PREFIX_ADOBE + "desc"), crossdomainmessage.getRequestHeader().getURI().getURI(), // the url field "", // parameter being attacked: none. "", // attack Constant.messages.getString(MESSAGE_PREFIX_ADOBE_SEND + "extrainfo", "/" + ADOBE_CROSS_DOMAIN_POLICY_FILE), // extrainfo Constant.messages.getString(MESSAGE_PREFIX_ADOBE_SEND + "soln"), // solution "<allow-http-request-headers-from domain=\"*\"", // evidence crossdomainmessage // the message on which to place the alert ); } } } catch (SAXException | IOException e) { // Could well be a 404 or equivalent log.debug("An error occurred trying to parse " + ADOBE_CROSS_DOMAIN_POLICY_FILE + " as XML: " + e); } }
From source file:org.zaproxy.zap.extension.ascanrulesBeta.CrossDomainScanner.java
private void scanSilverlightCrossdomainPolicyFile(URI originalURI) throws IOException, XPathExpressionException { // retrieve the Silverlight client access policy file, and assess it. HttpMessage clientaccesspolicymessage = new HttpMessage(new URI(originalURI.getScheme(), originalURI.getAuthority(), "/" + SILVERLIGHT_CROSS_DOMAIN_POLICY_FILE, null, null)); sendAndReceive(clientaccesspolicymessage, false); if (clientaccesspolicymessage.getResponseBody().length() == 0) { return;// ww w . j ava 2 s .c o m } byte[] clientaccesspolicymessagebytes = clientaccesspolicymessage.getResponseBody().getBytes(); // parse the file. If it's not parseable, it might have been because of a 404 try { // work around the "no protocol" issue by wrapping the content in a ByteArrayInputStream Document silverlightXmldoc = docBuilder .parse(new InputSource(new ByteArrayInputStream(clientaccesspolicymessagebytes))); XPathExpression exprAllowFromUri = xpath .compile("/access-policy/cross-domain-access/policy/allow-from/domain/@uri"); // gets the uri attributes // check the "allow-from" policies NodeList exprAllowFromUriNodes = (NodeList) exprAllowFromUri.evaluate(silverlightXmldoc, XPathConstants.NODESET); for (int i = 0; i < exprAllowFromUriNodes.getLength(); i++) { String uri = exprAllowFromUriNodes.item(i).getNodeValue(); if (uri.equals("*")) { // tut, tut, tut. if (log.isDebugEnabled()) log.debug("Bingo! " + SILVERLIGHT_CROSS_DOMAIN_POLICY_FILE + ", at /access-policy/cross-domain-access/policy/allow-from/domain/@uri"); bingo(getRisk(), Alert.CONFIDENCE_MEDIUM, Constant.messages.getString(MESSAGE_PREFIX_SILVERLIGHT + "name"), Constant.messages.getString(MESSAGE_PREFIX_SILVERLIGHT + "desc"), clientaccesspolicymessage.getRequestHeader().getURI().getURI(), // the url field "", // parameter being attacked: none. "", // attack Constant.messages.getString(MESSAGE_PREFIX_SILVERLIGHT + "extrainfo"), // extrainfo Constant.messages.getString(MESSAGE_PREFIX_SILVERLIGHT + "soln"), // solution "<domain uri=\"*\"", // evidence clientaccesspolicymessage // the message on which to place the alert ); } } } catch (SAXException | IOException e) { // Could well be a 404 or equivalent log.debug( "An error occurred trying to parse " + SILVERLIGHT_CROSS_DOMAIN_POLICY_FILE + " as XML: " + e); } }
From source file:org.zaproxy.zap.extension.ascanrulesBeta.SourceCodeDisclosureSVN.java
/** * finds the source code for the given file, using SVN metadata on the server (if this is * available)//from w w w. j av a 2 s. c o m * * @param uri the URI of a file, whose source code we want to find * @return Did we find the source code? */ private boolean findSourceCodeSVN(HttpMessage originalMessage) throws Exception { AlertThreshold alertThreshold = getAlertThreshold(); // SVN formats 1-10 (format 11 is not used) are supported by this logic. // TODO: The SQLite based (and centralised, except for pre-release formats which we don't // plan to support) ".svn/wc.db" style used from SVN format 12 through to 31 // (and possibly later formats) is not yet supported here. It's a work in progress. // It is fully supported in the Spider, however. URI uri = originalMessage.getRequestHeader().getURI(); String path = uri.getPath(); if (path == null) path = ""; // String filename = path.substring( path.lastIndexOf('/')+1, path.length() ); String urlfilename = uri.getName(); String fileExtension = null; if (urlfilename.contains(".")) { fileExtension = urlfilename.substring(urlfilename.lastIndexOf(".") + 1); fileExtension = fileExtension.toUpperCase(); } // do not recurse into a Subversion folder... this would cause infinite recursion issues in // Attack Mode. (which goes depth first!) // in any event, it doesn't make sense to do this. if (path.contains("/.svn/") || path.endsWith("/.svn")) { if (log.isDebugEnabled()) log.debug( "Nope. It doesn't make any sense to look for a Subversion repo *within* a Subversion repo"); return false; } // Look for SVN < 1.7 metadata (ie internal SVN format < 29) containing source code // These versions all store the pristine copies in the the same format (insofar as the logic // here is concerned, at least) try { String pathminusfilename = path.substring(0, path.lastIndexOf(urlfilename)); HttpMessage svnsourcefileattackmsg = new HttpMessage(new URI(uri.getScheme(), uri.getAuthority(), pathminusfilename + ".svn/text-base/" + urlfilename + ".svn-base", null, null)); svnsourcefileattackmsg.setCookieParams(this.getBaseMsg().getCookieParams()); // svnsourcefileattackmsg.setRequestHeader(this.getBaseMsg().getRequestHeader()); sendAndReceive(svnsourcefileattackmsg, false); // do not follow redirects int attackmsgResponseStatusCode = svnsourcefileattackmsg.getResponseHeader().getStatusCode(); if (shouldStop(alertThreshold, attackmsgResponseStatusCode)) { return false; } if (originalMessage.getResponseBody().toString() .equals(svnsourcefileattackmsg.getResponseBody().toString())) { if (log.isDebugEnabled()) { log.debug("Response bodies are exactly the same, so can not be the source code"); } } else if (!UNWANTED_RESPONSE_CODES.contains(attackmsgResponseStatusCode)) { // If the response is wanted (not on the // unwanted list) String attackFilename = uri.getScheme() + "://" + uri.getAuthority() + pathminusfilename + ".svn/text-base/" + urlfilename + ".svn-base"; if (log.isDebugEnabled()) { log.debug("The contents for request '" + attackFilename + "' do not return 404 or 3**, so we possibly have the source code using SVN < 1.7"); } // check the contents of the output to some degree, if we have a file extension. // if not, just try it (could be a false positive, but hey) String evidence = findEvidenceForExtension(svnsourcefileattackmsg.getResponseBody().getBytes(), fileExtension); if (evidence != null) { // if we get to here, is is very likely that we have source file inclusion // attack. alert it. bingo(Alert.RISK_MEDIUM, getConfidence(attackmsgResponseStatusCode), getName(), getDescription(), getBaseMsg().getRequestHeader().getURI().getURI(), null, attackFilename, getExtraInfo(urlfilename, attackFilename), getSolution(), evidence, svnsourcefileattackmsg); // if we found one, do not even try the "super" method, which tries each of the // parameters, // since this is slow, and we already found an instance return true; } else { if (log.isDebugEnabled()) log.debug("The HTML output does not look like source code of type " + fileExtension); } } else { if (log.isDebugEnabled()) { log.debug("Got an unsuitable response code " + svnsourcefileattackmsg.getResponseHeader().getStatusCode() + ", so it looks like SVN < 1.7 source code file was not found"); } } } catch (Exception e) { log.warn("Got an error trying to find source code using the format used by SVN < 1.7", e); } // try again, by assuming that SVN 1.7 or later is used. These versions use a different // internal format, and store the source code in different locations compared to SVN < 1.7. // Note that it's not as simple this time around, because the name of the file that contains // the source code is based on a SHA1 hash of the file contents, rather than being based on // the source file name. // In other words, we can't guess the name of the internal SVN file, and we can't just // calculate it from the file name. The good news is that the file name that we need is // contained in the centralised // "wc.db" SVN metadata file that is associated with SVN >= 1.7. // "wc.db" lives in ".svn/wc.db". This file contains data for all of the files in the repo // (ie, it contains data for the root directory and all subdirectories of the repo). // The only real issue we have is the question of where within the web folder structure (or // mappings) that the "wc.db" file resides. // For instance, the ".svn" directory might have been deployed into // "http://www.example.com/.svn", // or it *might* have been deployed into "http://www.example.com/dir1/dir2/.svn". // If we're looking for the SVN >= 1.7 source for // "http://www.example.com/dir1/dir2/login.php", for instance, we need to check for the // "wc.db" file in the following locations: // "http://www.example.com/dir1/dir2/.svn/wc.db" // "http://www.example.com/dir1/.svn/wc.db" // "http://www.example.com/.svn/wc.db" // ie, we need to traverse all the way back to the web root looking for it. // Once we've found the "wc.db" file, we use it as an index, looking up the name of the file // for which we're trying to get the source code. // That gives us the internal SVN file name (containing the SHA1 value), which we can (in // theory) then retrieve. If it works, we will retrieve the source code for the file! try { String pathminusfilename = path.substring(0, path.lastIndexOf(urlfilename)); while (!pathminusfilename.equals("/")) { HttpMessage svnWCDBAttackMsg = new HttpMessage( new URI(uri.getScheme(), uri.getAuthority(), pathminusfilename + ".svn/wc.db", null, null)); svnWCDBAttackMsg.setCookieParams(this.getBaseMsg().getCookieParams()); // svnsourcefileattackmsg.setRequestHeader(this.getBaseMsg().getRequestHeader()); sendAndReceive(svnWCDBAttackMsg, false); // do not follow redirects int svnWCDBAttackMsgStatusCode = svnWCDBAttackMsg.getResponseHeader().getStatusCode(); if (shouldStop(alertThreshold, svnWCDBAttackMsgStatusCode)) { return false; } if (originalMessage.getResponseBody().toString() .equals(svnWCDBAttackMsg.getResponseBody().toString())) { if (log.isDebugEnabled()) { log.debug("Response bodies are exactly the same, so can not be the source code"); } } else if (!UNWANTED_RESPONSE_CODES.contains(svnWCDBAttackMsgStatusCode)) { // If the response is wanted (not on the // unwanted list) // calculate the path used to access the wc.db, as well as the matching relpath // to query the wc.db // since the relpath is calculated from the original message URL path, after // removing the base used in the wc.db url path String wcdbAttackFilename = uri.getScheme() + "://" + uri.getAuthority() + pathminusfilename + ".svn/wc.db"; String relPath = path.substring(path.indexOf(pathminusfilename) + pathminusfilename.length()); if (log.isDebugEnabled()) { log.debug("The contents for request '" + wcdbAttackFilename + "' do not return 404 or 3**, so we found the '.svn/wc.db' file for SVN >= 1.7.."); log.debug("The relpath to query SQLite is '" + relPath + "'"); } // so we found the wc.db file... handle it. // get the binary data, and put it in a temp file we can use with the SQLite // JDBC driver // Note: File is not AutoClosable, so cannot use a "try with resources" to // manage it File tempSqliteFile; tempSqliteFile = File.createTempFile("sqlite_svn_wc_db", null); tempSqliteFile.deleteOnExit(); OutputStream fos = new FileOutputStream(tempSqliteFile); fos.write(svnWCDBAttackMsg.getResponseBody().getBytes()); fos.close(); if (log.isDebugEnabled()) { org.sqlite.JDBC jdbcDriver = new org.sqlite.JDBC(); log.debug("Created a temporary SQLite database file '" + tempSqliteFile + "'"); log.debug("SQLite JDBC Driver is version " + jdbcDriver.getMajorVersion() + "." + jdbcDriver.getMinorVersion()); } // now load the temporary SQLite file using JDBC, and query the file entries // within. Class.forName("org.sqlite.JDBC"); String sqliteConnectionUrl = "jdbc:sqlite:" + tempSqliteFile.getAbsolutePath(); try (Connection conn = DriverManager.getConnection(sqliteConnectionUrl)) { if (conn != null) { Statement pragmaStatement = null; PreparedStatement nodeStatement = null; ResultSet rsSVNWCFormat = null; ResultSet rsNode = null; ResultSet rsRepo = null; try { pragmaStatement = conn.createStatement(); rsSVNWCFormat = pragmaStatement.executeQuery("pragma USER_VERSION"); // get the precise internal version of SVN in use // this will inform how the scanner should proceed in an efficient // manner. int svnFormat = 0; while (rsSVNWCFormat.next()) { if (log.isDebugEnabled()) log.debug("Got a row from 'pragma USER_VERSION'"); svnFormat = rsSVNWCFormat.getInt(1); break; } if (svnFormat < 29) { throw new Exception( "The SVN Working Copy Format of the SQLite database should be >= 29. We found " + svnFormat); } if (svnFormat > 31) { throw new Exception("SVN Working Copy Format " + svnFormat + " is not supported at this time. We support up to and including format 31 (~ SVN 1.8.5)"); } if (log.isDebugEnabled()) { log.debug("Internal SVN Working Copy Format for " + tempSqliteFile + " is " + svnFormat); log.debug( "Refer to http://svn.apache.org/repos/asf/subversion/trunk/subversion/libsvn_wc/wc.h for more details!"); } // allow future changes to be easily handled switch (svnFormat) { case 29: case 30: case 31: nodeStatement = conn.prepareStatement( "select kind,local_relpath,'pristine/'||substr(checksum,7,2) || \"/\" || substr(checksum,7)|| \".svn-base\" from nodes where local_relpath = ? order by wc_id"); break; } // now set the parameter, and execute the query nodeStatement.setString(1, relPath); rsNode = nodeStatement.executeQuery(); // and get the internal name of the SVN file stored in the SVN repo while (rsNode.next()) { if (log.isDebugEnabled()) log.debug("Got a Node from the SVN wc.db file (format " + svnFormat + ")"); // String kind = rsNode.getString(1); // String filename = rsNode.getString(2); String svnFilename = rsNode.getString(3); if (svnFilename != null && svnFilename.length() > 0) { log.debug("Found " + relPath + " in the wc.db: " + svnFilename); // try get the source, using the internal SVN file path, // building the path back up correctly HttpMessage svnSourceFileAttackMsg = new HttpMessage( new URI(uri.getScheme(), uri.getAuthority(), pathminusfilename + ".svn/" + svnFilename, null, null)); svnSourceFileAttackMsg.setCookieParams(this.getBaseMsg().getCookieParams()); // svnsourcefileattackmsg.setRequestHeader(this.getBaseMsg().getRequestHeader()); sendAndReceive(svnSourceFileAttackMsg, false); // do not follow redirects int svnSourceFileAttackMsgStatusCode = svnSourceFileAttackMsg .getResponseHeader().getStatusCode(); if (shouldStop(alertThreshold, svnSourceFileAttackMsgStatusCode)) { return false; } if (!UNWANTED_RESPONSE_CODES.contains(svnSourceFileAttackMsgStatusCode)) { // If the // response is // wanted (not // on the // unwanted // list) String attackFilename = uri.getScheme() + "://" + uri.getAuthority() + pathminusfilename + ".svn/" + svnFilename; if (log.isDebugEnabled()) { log.debug("The contents for request '" + attackFilename + "' do not return 404 or 3**, so we possibly have the source code using SVN >= 1.7"); } // check the contents of the output to some degree, if // we have a file extension. // if not, just try it (could be a false positive, but // hey) String evidence = findEvidenceForExtension( svnSourceFileAttackMsg.getResponseBody().getBytes(), fileExtension); if (evidence != null) { // if we get to here, is is very likely that we have // source file inclusion attack. alert it. bingo(Alert.RISK_MEDIUM, getConfidence(svnSourceFileAttackMsgStatusCode), getName(), getDescription(), getBaseMsg().getRequestHeader().getURI().getURI(), null, attackFilename, getExtraInfo(urlfilename, attackFilename), getSolution(), evidence, svnSourceFileAttackMsg); // do not return.. need to tidy up first } else { if (log.isDebugEnabled()) log.debug( "The HTML output does not look like source code of type " + fileExtension); } } else { if (log.isDebugEnabled()) { log.debug("Got an unsuitable response code " + svnSourceFileAttackMsg.getResponseHeader().getStatusCode() + ", so it looks like SVN >= 1.7 source code file was not found"); } } break; // out of the loop. even though there should be just // 1 entry } } } catch (SQLException sqlEx) { StringBuilder errorSb = new StringBuilder(300); errorSb.append("Error executing SQL on temporary SVN SQLite database '"); errorSb.append(sqliteConnectionUrl); errorSb.append("': "); errorSb.append(sqlEx); errorSb.append("\nThe saved response likely wasn't a SQLite db."); log.debug(errorSb); } catch (Exception e) { log.debug("An error has occurred, related to the temporary SVN SQLite DB. " + e); } finally { // the JDBC driver in use does not play well with "try with // resource" construct. I tried! if (rsRepo != null) rsRepo.close(); if (rsNode != null) rsNode.close(); if (rsSVNWCFormat != null) rsSVNWCFormat.close(); if (pragmaStatement != null) pragmaStatement.close(); if (nodeStatement != null) nodeStatement.close(); } } else throw new SQLException("Could not open a JDBC connection to SQLite file " + tempSqliteFile.getAbsolutePath()); } catch (Exception e) { // the connection will have been closed already, since we're used a try with // resources log.error("Error parsing temporary SVN SQLite database " + sqliteConnectionUrl); } finally { // delete the temp file. // this will be deleted when the VM is shut down anyway, but better to be // safe than to run out of disk space. tempSqliteFile.delete(); } break; // out of the while loop } // non 404, 300, etc for "wc.db", for SVN >= 1.7 // set up the parent directory name pathminusfilename = pathminusfilename.substring(0, pathminusfilename.substring(0, pathminusfilename.length() - 1).lastIndexOf("/") + 1); } } catch (Exception e) { log.warn("Got an error trying to find source code using the format used by SVN >= 1.7", e); } return false; }
From source file:ru.org.linux.util.formatter.ToHtmlFormatter.java
public String memberURL(User user, boolean secure) throws URIException { URI mainUri = configuration.getMainURI(); String scheme;// www . j av a2s . c om if (secure) { scheme = "https"; } else { scheme = "http"; } return (new URI(scheme, null, mainUri.getHost(), mainUri.getPort(), String.format("/people/%s/profile", user.getNick()))).getEscapedURIReference(); }