//////////////////////////////////////////////////////////////////////// // // Copyright (c) 2009-2013 Denim Group, Ltd. // // The contents of this file are subject to the Mozilla Public License // Version 2.0 (the "License"); you may not use this file except in // compliance with the License. You may obtain a copy of the License at // http://www.mozilla.org/MPL/ // // Software distributed under the License is distributed on an "AS IS" // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the // License for the specific language governing rights and limitations // under the License. // // The Original Code is ThreadFix. // // The Initial Developer of the Original Code is Denim Group, Ltd. // Portions created by Denim Group, Ltd. are Copyright (C) // Denim Group, Ltd. All Rights Reserved. // // Contributor(s): Denim Group, Ltd. // //////////////////////////////////////////////////////////////////////// package com.denimgroup.threadfix.service.channel; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; import org.springframework.beans.factory.annotation.Autowired; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import com.denimgroup.threadfix.data.dao.ChannelSeverityDao; import com.denimgroup.threadfix.data.dao.ChannelTypeDao; import com.denimgroup.threadfix.data.dao.ChannelVulnerabilityDao; import com.denimgroup.threadfix.data.entities.ChannelType; import com.denimgroup.threadfix.data.entities.Finding; import com.denimgroup.threadfix.data.entities.Scan; import com.denimgroup.threadfix.webapp.controller.ScanCheckResultBean; /** * * @author mcollins * */ public class BurpSuiteChannelImporter extends AbstractChannelImporter { private static final String TEMPLATE_NAME = "name of an arbitrarily supplied request"; private static final String REST_URL_PARAM = "REST URL parameter"; private static final String MANUAL_INSERTION_POINT = "manual insertion point"; @Autowired public BurpSuiteChannelImporter(ChannelTypeDao channelTypeDao, ChannelVulnerabilityDao channelVulnerabilityDao, ChannelSeverityDao channelSeverityDao) { this.channelTypeDao = channelTypeDao; this.channelVulnerabilityDao = channelVulnerabilityDao; this.channelSeverityDao = channelSeverityDao; this.channelType = channelTypeDao.retrieveByName(ChannelType.BURPSUITE); doSAXExceptionCheck = false; } @Override public Scan parseInput() { cleanInputStream(); return parseSAXInput(new BurpSuiteSAXParser()); } public void cleanInputStream() { if (inputStream == null) return; BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); String line = null, fullString = ""; try { StringBuffer buffer = new StringBuffer(); while ((line = reader.readLine()) != null) { while (line.contains("]]]")) line = line.replace("]]]", "] ]]"); buffer.append(line); } fullString = buffer.toString(); } catch (IOException e) { log.warn("IOException while trying to clean input stream", e); } try { reader.close(); } catch (IOException e) { log.warn("IOException while trying to close file input stream.", e); } inputStream = new ByteArrayInputStream(fullString.getBytes()); } public class BurpSuiteSAXParser extends HandlerWithBuilder { private boolean getChannelVulnText = false; private boolean getUrlText = false; private boolean getParamText = false; private boolean getSeverityText = false; private boolean getHostText = false; private boolean getBackupParameter = false; private boolean getSerialNumber = false; private String currentChannelVulnCode = null; private String currentUrlText = null; private String currentParameter = null; private String currentSeverityCode = null; private String currentHostText = null; private String currentBackupParameter = null; private String currentSerialNumber = null; private void add(Finding finding) { if (finding != null) { if (currentSerialNumber != null) { finding.setNativeId(currentSerialNumber); } else { finding.setNativeId(getNativeId(finding)); } finding.setIsStatic(false); saxFindingList.add(finding); } } //////////////////////////////////////////////////////////////////// // Event handlers. //////////////////////////////////////////////////////////////////// public void startElement (String uri, String name, String qName, Attributes atts) { if ("type".equals(qName)) { getChannelVulnText = true; } else if ("location".equals(qName)) { getUrlText = true; } else if ("serialNumber".equals(qName)) { getSerialNumber = true; } else if ("host".equals(qName)) { getHostText = true; } else if ("severity".equals(qName)) { getSeverityText = true; } else if ("issues".equals(qName)) { date = getCalendarFromString("EEE MMM dd kk:mm:ss zzz yyyy", atts.getValue("exportTime")); } else if ("request".equals(qName)) { getBackupParameter = true; } } public void endElement (String uri, String name, String qName) { if (getChannelVulnText) { currentChannelVulnCode = getBuilderText(); getChannelVulnText = false; } else if (getHostText) { currentHostText = getBuilderText(); getHostText = false; } else if (getUrlText) { currentUrlText = getBuilderText(); if (currentUrlText != null) { currentParameter = getRegexResult(currentUrlText, "\\[(.*) parameter\\]"); currentUrlText = getRegexResult(currentUrlText, "^([^\\[]+)"); if (currentUrlText != null) currentUrlText = currentUrlText.trim(); } getUrlText = false; } else if (getParamText) { currentParameter = getBuilderText(); getParamText = false; } else if (getSerialNumber) { currentSerialNumber = getBuilderText(); getSerialNumber = false; } else if (getSeverityText) { currentSeverityCode = getBuilderText(); getSeverityText = false; } else if (getBackupParameter) { String tempURL = getBuilderText(); if (tempURL != null && tempURL.contains("HTTP")) { tempURL = tempURL.substring(0, tempURL.indexOf("HTTP")); } if (tempURL != null && tempURL.contains("=") && tempURL.indexOf('=') == tempURL.lastIndexOf('=')) { currentBackupParameter = getRegexResult(tempURL, "\\?(.*?)="); } getBackupParameter = false; } if ("issue".equals(qName)) { // This is a temporary fix, we should take another look at why burp did this // before deciding on a final strategy if (currentParameter != null && currentParameter.equals(TEMPLATE_NAME)) { currentParameter = currentBackupParameter; } if (currentParameter != null && (currentParameter.startsWith(REST_URL_PARAM) || currentParameter.startsWith(MANUAL_INSERTION_POINT))) { currentParameter = ""; } Finding finding = constructFinding(currentHostText + currentUrlText, currentParameter, currentChannelVulnCode, currentSeverityCode); add(finding); currentChannelVulnCode = null; currentSeverityCode = null; currentParameter = null; currentUrlText = null; currentSerialNumber = null; currentBackupParameter = null; } } public void characters (char ch[], int start, int length) { if (getChannelVulnText || getHostText || getUrlText || getParamText || getSeverityText || getBackupParameter || getSerialNumber) { addTextToBuilder(ch,start,length); } } } @Override public ScanCheckResultBean checkFile() { return testSAXInput(new BurpSuiteSAXValidator()); } public class BurpSuiteSAXValidator extends DefaultHandler { private boolean hasFindings = false; private boolean hasDate = false; private boolean correctFormat = false; private void setTestStatus() { if (!correctFormat) testStatus = ScanImportStatus.WRONG_FORMAT_ERROR; else if (hasDate) testStatus = checkTestDate(); if (ScanImportStatus.SUCCESSFUL_SCAN == testStatus && !hasFindings) testStatus = ScanImportStatus.EMPTY_SCAN_ERROR; else if (testStatus == null) testStatus = ScanImportStatus.SUCCESSFUL_SCAN; } //////////////////////////////////////////////////////////////////// // Event handlers. //////////////////////////////////////////////////////////////////// public void endDocument() { setTestStatus(); } public void startElement (String uri, String name, String qName, Attributes atts) throws SAXException { if ("issues".equals(qName)) { testDate = getCalendarFromString("EEE MMM dd kk:mm:ss zzz yyyy", atts.getValue("exportTime")); if (testDate != null) hasDate = true; correctFormat = atts.getValue("burpVersion") != null; } if ("issue".equals(qName)) { hasFindings = true; setTestStatus(); throw new SAXException(FILE_CHECK_COMPLETED); } } } }