【BurpSuite】插件开发学习之J2EEScan(上)-被动扫描
前言
插件开发学习第5套。前置文章:
【BurpSuite】插件开发学习之Log4shell
【BurpSuite】插件开发学习之Software Vulnerability Scanner
【BurpSuite】插件开发学习之dotnet-Beautifier
【BurpSuite】插件开发学习之active-scan-plus-plus
J2EEScan
https://github.com/PortSwigger/j2ee-scan.git
逻辑代码在
|____src
| |____main
| | |____java
| | | |____burp
| | | | |____HTTPMatcher.java
| | | | |____J2EELFIRetriever.java
| | | | |____SoftwareVersions.java
| | | | |____WeakPasswordBruteforcer.java
| | | | |____j2ee
| | | | | |____PassiveScanner.java
| | | | | |____Confidence.java
| | | | | |____annotation
| | | | | | |____RunOnlyOnce.java
| | | | | | |____RunOnlyOnceForApplicationContext.java
| | | | | |____Risk.java
| | | | | |____passive
| | | | | | |____SessionFixation.java
| | | | | | |____ApacheStrutsS2023Rule.java
| | | | | | |____JettyRule.java
| | | | | | |____HttpServerHeaderRule.java
| | | | | | |____SqlQueryRule.java
| | | | | | |____PassiveRule.java
| | | | | | |____strutstoken
| | | | | | | |____StrutsTokenCracker.java
| | | | | | | |____ReplayRandom.java
| | | | | | |____ApacheTomcatRule.java
| | | | | | |____SessionIDInURL.java
| | | | | | |____JSPostMessage.java
| | | | | | |____ExceptionRule.java
| | | | | |____IssuesHandler.java
| | | | | |____lib
| | | | | | |____TesterAjpMessage.java
| | | | | | |____SimpleAjpClient.java
| | | | | |____issues
| | | | | | |____impl
| | | | | | | |____OracleEBSSSRF.java
| | | | | | | |____OracleEBSSSRFLCMServiceController.java
| | | | | | | |____ApacheStrutsS2032.java
| | | | | | | |____NodeJSRedirect.java
| | | | | | | |____ApacheRollerOGNLInjection.java
| | | | | | | |____ApacheStrutsDebugMode.java
| | | | | | | |____ApacheAxis.java
| | | | | | | |____HTTPWeakPassword.java
| | | | | | | |____HTTPProxy.java
| | | | | | | |____PrimeFacesELInjection.java
| | | | | | | |____WeblogicUDDIExplorer.java
| | | | | | | |____ApacheStrutsS2052.java
| | | | | | | |____JBossWebConsole.java
| | | | | | | |____EL3Injection.java
| | | | | | | |____XXEParameterModule.java
| | | | | | | |____UndertowTraversal.java
| | | | | | | |____LFIModule.java
| | | | | | | |____ApacheStrutsS2043.java
| | | | | | | |____FastJsonRCE.java
| | | | | | | |____OracleReportService.java
| | | | | | | |____SnoopResource.java
| | | | | | | |____JBossJMXReadOnly.java
| | | | | | | |____WebInfInformationDisclosure.java
| | | | | | | |____XInclude.java
| | | | | | | |____JavaServerFacesTraversal.java
| | | | | | | |____Seam2RCE.java
| | | | | | | |____WeblogicConsole.java
| | | | | | | |____RESTAPISwagger.java
| | | | | | | |____JettyRemoteLeakage.java
| | | | | | | |____JBossJMXInvoker.java
| | | | | | | |____OASConfigFilesDisclosure.java
| | | | | | | |____JacksonDataBindCVE20177525.java
| | | | | | | |____XXEModule.java
| | | | | | | |____WeblogicCVE20192725.java
| | | | | | | |____WeblogicWebServiceTestPageCVE20182894.java
| | | | | | | |____JKStatus.java
| | | | | | | |____WeblogicCVE201710271.java
| | | | | | | |____LFIAbsoluteModule.java
| | | | | | | |____ApacheStrutsS2016.java
| | | | | | | |____ApacheStrutsShowcase.java
| | | | | | | |____ApacheStrutsWebConsole.java
| | | | | | | |____ApacheStrutsS2020.java
| | | | | | | |____StatusServlet.java
| | | | | | | |____UTF8ResponseSplitting.java
| | | | | | | |____TomcatHostManager.java
| | | | | | | |____SpringBootRestRCE.java
| | | | | | | |____PivotalSpringTraversalCVE20143625.java
| | | | | | | |____Htaccess.java
| | | | | | | |____JBossjBPMAdminConsole.java
| | | | | | | |____ELInjection.java
| | | | | | | |____NodeJSPathTraversal.java
| | | | | | | |____ApacheStrutsS2017.java
| | | | | | | |____ApacheSolrXXE.java
| | | | | | | |____OASSqlnetLogDisclosure.java
| | | | | | | |____NodeJSResponseSplitting.java
| | | | | | | |____URINormalizationTomcat.java
| | | | | | | |____JBossWS.java
| | | | | | | |____SpringCloudConfigPathTraversal.java
| | | | | | | |____InfrastructurePathTraversal.java
| | | | | | | |____AJPDetector.java
| | | | | | | |____JBossAdminConsole.java
| | | | | | | |____SSRFScanner.java
| | | | | | | |____SpringDataCommonRCE.java
| | | | | | | |____JavascriptSSRF.java
| | | | | | | |____ApacheWicketArbitraryResourceAccess.java
| | | | | | | |____SpringBootActuator.java
| | | | | | | |____IDocInjection.java
| | | | | | | |____TomcatManager.java
| | | | | | | |____NextFrameworkPathTraversal.java
| | | | | | | |____OracleCGIPrintEnv.java
| | | | | | | |____JBossJuddi.java
| | | | | | | |____AJP_Tomcat_GhostCat.java
| | | | | | | |____SpringWebFlowDataBindExpressionCVE20174971.java
| | | | | | |____IModule.java
| | | | | |____CustomScanIssue.java
| | | | |____J2EELocalAssessment.java
| | | | |____WeakPassword.java
| | | | |____HTTPParser.java
| | | | |____CustomHttpRequestResponse.java
| | | | |____BurpExtender.java
这个代码是基于java写的
BurpExtender
老样子,继承BurpExtender
class BurpExtender(IBurpExtender):
基本信息也和java差不多
public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks) {
// keep a reference to our callbacks object
this.callbacks = callbacks;
this.callbacks.registerExtensionStateListener(this);
// obtain an extension helpers object
helpers = callbacks.getHelpers();
// obtain our output stream
stdout = new PrintWriter(callbacks.getStdout(), true);
stderr = new PrintWriter(callbacks.getStderr(), true);
// set our extension name
callbacks.setExtensionName("J2EE Advanced Tests");
然后创建了一个临时数据库文件并连接了
j2eeDBState = File.createTempFile("burpsuite-j2eescan-state", ".db");
stdout.println("Using temporary db state file: " + j2eeDBState.getAbsolutePath());
stdout.println("This internal state is used to avoid duplicate infrastructure security "
+ "checks on the same host, improving the scan performance");
connectToDatabase(j2eeDBState.getAbsolutePath());
初始化的数据库表executed_plugins
String fields = "plugin, host, port";
conn.createStatement().executeUpdate("CREATE TABLE IF NOT EXISTS executed_plugins ("
+ " plugin TEXT PRIMARY KEY,"
+ " host TEXT,"
+ " port INTEGER,"
+ " UNIQUE(" + fields + "))");
doPassiveScan
重写了被动扫描,在PassiveScanner这个类里。
PassiveScanner.scanVulnerabilities(baseRequestResponse, callbacks);
遍历如下规则进行扫描
static PassiveRule[] PASSIVE_RULES = {
new ApacheTomcatRule(),
new ExceptionRule(),
new HttpServerHeaderRule(),
new SqlQueryRule(),
new ApacheStrutsS2023Rule(),
new JettyRule(),
new SessionIDInURL(),
new JSPostMessage(),
new SessionFixation()
};
一个一个看,
ApacheTomcatRule
【1】tomcat版本发现
Risk.Low
Pattern.compile("Apache Tomcat/([\\d\\.]+)"
【2】tomcat远程jvm虚拟机
Risk.Information
Pattern.compile("\"><small>(1\\.\\d\\.[\\w\\-\\_\\.]+)<"
ExceptionRule
【3】Apache Struts 测试页面
判断struts是开发环境还是dev环境
Risk.Low
"<title>Struts Problem Report</title>".getBytes();
【4】Apache Tapestry 异常错误展示
Risk.Low
byte[] tapestryException = "<h1 class=\"t-exception-report\">An unexpected application exception has occurred.</h1>".getBytes();
【5】Grails 异常错误展示
Risk.Low
byte[] grailsException = "<h1>Grails Runtime Exception</h1>".getBytes();
【6】GWT 异常错误展示
Risk.Low
byte[] gwtException = "com.google.gwt.http.client.RequestException".getBytes();
【7】java 常见的应用异常错误展示
Risk.Low
List<byte[]> javaxServletExceptions = Arrays.asList(
"javax.servlet.ServletException".getBytes(),
"οnclick=\"toggle('full exception chain stacktrace".getBytes(),
"at org.apache.catalina".getBytes(),
"at org.apache.coyote.".getBytes(),
"at org.jboss.seam.".getBytes(),
"at org.apache.tomcat.".getBytes(),
"<title>JSP Processing Error</title>".getBytes(), // WAS
"The full stack trace of the root cause is available in".getBytes());
"<pre><code>com.sun.facelets.FaceletException".getBytes(),
"Generated by MyFaces - for information on disabling".getBytes(),
"<title>Error - org.apache.myfaces".getBytes(),
"org.primefaces.webapp".getBytes());
HttpServerHeaderRule
http 头泄露应用版本号
【8】Java&Jetty &GlassFish&Weblogic
Pattern.compile("java\\/([\\d\\.\\_]+)"
Pattern.compile("Jetty.([\\d\\.]+)"
Pattern.compile("GlassFish Server Open Source Edition ([\\d\\.]+)"
Pattern.compile("WebLogic (:?Server )?([\\d\\.]+)"
【10】 oracle
ORACLE_APPLICATION_SERVER_RE.add(Pattern.compile("Oracle Application Server Containers for J2EE 10g \\(([\\d\\.]+)\\)", Pattern.DOTALL));
ORACLE_APPLICATION_SERVER_RE.add(Pattern.compile("Oracle.Application.Server.10g\\/([\\d\\.]+)", Pattern.DOTALL));
ORACLE_APPLICATION_SERVER_RE.add(Pattern.compile("Oracle Application Server\\/([\\d\\.]+)", Pattern.DOTALL));
ORACLE_APPLICATION_SERVER_RE.add(Pattern.compile("Oracle9iAS\\/([\\d\\.]+)", Pattern.DOTALL));
【11】nodejs
if (xPoweredByHeader.trim().equals("Express")) {
SqlQueryRule
【12】SQL exception
SQL_QUERIES_RE.add(Pattern.compile("select ", Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE));
SQL_QUERIES_RE.add(Pattern.compile("IS NOT NULL", Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE));
ApacheStrutsS2023Rule
【13】StrutsTokenCracker
提取token
private final Pattern TOKEN_FIELD_PATTERN = Pattern.compile("<input type=\"hidden\" name=\"token\" value=\"([^\"]+)\"");
转int,按固定长度切割
int[] tokenInts = bytesToInt(bigIntToByte(token));
根据int找到seed
long seed = findSeed(reverseByteOrder(tokenInts[1]), reverseByteOrder(tokenInts[2]));
根据种子预测随机数,和就token匹配,如果能匹配上,说明种子是对的,也就是说明token可预测。
int[] nextInts = new int[4];
for(int i=0;i<nextInts.length;i++) {
nextInts[i] = reverseByteOrder(random.nextInt());
}
boolean match1 = tokenInts[2] == nextInts[0];
boolean match2 = tokenInts[3] == nextInts[1];
boolean match3 = tokenInts[4] == nextInts[2];
JettyRule
【14】Jetty发现
private static final Pattern JETTY_PATTERN = Pattern.compile("><small>Powered by Jetty", Pattern.DOTALL | Pattern.MULTILINE);
SessionIDInURL
【15】Session Token in URL
private static final List<String> SESSIONIDs = new ArrayList<>(Arrays.asList(";jsessionid"));
JSPostMessage
【16】JSPostMessage函数
js的跨域信息通信的函数。
POSTMESSAGE_PATTERNS.add(Pattern.compile(".addEventListener\\(\"message", Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE));
POSTMESSAGE_PATTERNS.add(Pattern.compile("window\\).on\\(\"message", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE));
POSTMESSAGE_PATTERNS.add(Pattern.compile(".postMessage\\(", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE));
SessionFixation
【17】session fixation attack(固定会话攻击)
先检查url,这个检查很粗糙,直接判断后缀,还是黑名单,没有后缀就默认通过
isJavaApplicationByURL(curURL)
然后条件是请求包有JSESSIONID且返回包含有账号等信息
if (requestCookie != null && requestCookie.contains("JSESSIONID")) {
String reqBodyLowercase = reqBody.toLowerCase();
if (reqBodyLowercase != null
&& (reqBodyLowercase.contains("password") || reqBodyLowercase.contains("pwd") || reqBodyLowercase.contains("passw"))
&& (reqBodyLowercase.contains("user") || reqBodyLowercase.contains("uid") || reqBodyLowercase.contains("mail"))) {
并且返回包没有setcookie(说明固定了会话),或者setcookie字段里包含JSESSIONID
这种校验比较粗糙,注释也说了
Due to the nature of the vulnerability, this check is prone to False Positives and must be manually confirmed
后话
主动扫描有点多,放在一个文章显得有点重,拆成两个。