diff --git a/pom.xml b/pom.xml index e1c2154..6184dc2 100644 --- a/pom.xml +++ b/pom.xml @@ -151,6 +151,14 @@ 2.0b5 + + + commons-validator + commons-validator + 1.8.0 + + + knife diff --git a/src/burp/BurpExtender.java b/src/burp/BurpExtender.java index 8c5243c..3bf7387 100644 --- a/src/burp/BurpExtender.java +++ b/src/burp/BurpExtender.java @@ -41,6 +41,7 @@ import manager.ChunkManager; import manager.DismissedTargetsManager; import manager.HeaderManager; +import org.apache.commons.lang3.StringUtils; public class BurpExtender extends GUI implements IBurpExtender, IContextMenuFactory, ITab, IHttpListener, IProxyListener, IExtensionStateListener { @@ -54,7 +55,6 @@ public class BurpExtender extends GUI implements IBurpExtender, IContextMenuFact public static PrintWriter stdout; public static PrintWriter stderr; public IContextMenuInvocation invocation; - public int proxyServerIndex = -1; public static String ExtensionName = "Knife"; public static String Version = bsh.This.class.getPackage().getImplementationVersion(); @@ -75,15 +75,12 @@ public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) { configPanel.setViewportView(table); String content = callbacks.loadExtensionSetting("knifeconfig"); - if (content != null) { - config = new Gson().fromJson(content, Config.class); - showToUI(config); - } else { - showToUI(new Gson().fromJson(initConfig(), Config.class)); + if (StringUtils.isEmpty(content)) { + content = initConfig(); } - table.setupTypeColumn();//call this function must after table data loaded !!!! - table.tableHeaderLengthInit(); + config = new Gson().fromJson(content, Config.class); + showToUI(config); ChineseTabFactory chntabFactory = new ChineseTabFactory(null, false, helpers, callbacks); @@ -171,7 +168,7 @@ public List createMenuItems(IContextMenuInvocation invocation) { Iterator it = menu_item_list.iterator(); while (it.hasNext()) { JMenuItem item = it.next(); - if (item.getText() == null || item.getText().equals("")) { + if (StringUtils.isEmpty(item.getText())) { it.remove(); } } @@ -306,41 +303,6 @@ public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequ } } - public static void confirmProxy() { - String proxy = JOptionPane.showInputDialog("Confirm Proxy Of Burp", "127.0.0.1:8080"); - if (proxy != null) { - BurpExtender.CurrentProxy = proxy.trim(); - } - } - - public static String getProxyHost() { - try { - if (CurrentProxy == null || CurrentProxy.equals("") || CurrentProxy.split(":").length != 2) { - confirmProxy(); - } - String proxyHost = CurrentProxy.split(":")[0]; - return proxyHost; - } catch (Exception e) { - e.printStackTrace(); - CurrentProxy = "";//设置为空,以便重新获取。 - return null; - } - } - - public static int getProxyPort() { - try { - if (CurrentProxy == null || CurrentProxy.equals("") || CurrentProxy.split(":").length != 2) { - confirmProxy(); - } - String proxyPort = CurrentProxy.split(":")[1]; - return Integer.parseInt(proxyPort); - } catch (Exception e) { - e.printStackTrace(); - CurrentProxy = "";//设置为空,以便重新获取。 - return -1; - } - } - public List GetSetCookieHeaders(String cookies) { if (cookies.startsWith("Cookie: ")) { cookies = cookies.replaceFirst("Cookie: ", ""); diff --git a/src/burp/Proxy.java b/src/burp/Proxy.java new file mode 100644 index 0000000..a1094df --- /dev/null +++ b/src/burp/Proxy.java @@ -0,0 +1,82 @@ +package burp; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.validator.routines.DomainValidator; +import org.apache.commons.validator.routines.InetAddressValidator; + +import javax.swing.*; + +public class Proxy { + + String host; + int port; + + public Proxy(String proxyStr) throws IllegalArgumentException { + if (StringUtils.isEmpty(proxyStr)) { + throw new IllegalArgumentException("input is empty"); + } + try { + String[] parts = proxyStr.split(":"); + if (parts.length != 2) { + throw new IllegalArgumentException("not valid host:port format"); + } + host = parts[0]; + if (!isValidIPAddress(host) && !isValidDomainName(host)) { + throw new IllegalArgumentException("host is not valid IP address and domain name"); + } + String portStr = parts[1]; + port = Integer.parseInt(portStr); + if (port >= 0 && port <= 65535) { + throw new IllegalArgumentException("invalid port range"); + } + } catch (Exception e) { + throw new IllegalArgumentException(e.getMessage()); + } + } + + // 检查是否是合法的IP地址 + public static boolean isValidIPAddress(String ipAddress) { + InetAddressValidator validator = InetAddressValidator.getInstance(); + return validator.isValidInet4Address(ipAddress); + } + + // 检查是否是合法的域名 + public static boolean isValidDomainName(String domainName) { + DomainValidator validator = DomainValidator.getInstance(); + return validator.isValid(domainName); + } + + public static Proxy inputProxy() { + int retry = 3; + while (retry > 0) { + String proxy = JOptionPane.showInputDialog("Confirm Proxy Of Burp", "127.0.0.1:8080"); + try { + return new Proxy(proxy); + } catch (IllegalArgumentException e) { + BurpExtender.getStderr().println(e); + retry = retry - 1; + } + } + return null; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getProxyStr() { + return host + ":" + port; + } +} diff --git a/src/config/GUI.java b/src/config/GUI.java index 6a5b46d..7ea1825 100644 --- a/src/config/GUI.java +++ b/src/config/GUI.java @@ -417,7 +417,10 @@ public void showToUI(Config config) { ConfigEntry entry = new ConfigEntry().FromJson(stringEntry); tableModel.addNewConfigEntry(entry); } - table.setupTypeColumn();// must setup again when data cleaned + + table.setupTypeColumn(); + //call this function must after table data loaded !!!! + // must setup again when data cleaned table.tableHeaderLengthInit(); diff --git a/src/knife/FindUrlAndRequest.java b/src/knife/FindUrlAndRequest.java index e8acefa..17a14d1 100644 --- a/src/knife/FindUrlAndRequest.java +++ b/src/knife/FindUrlAndRequest.java @@ -18,250 +18,241 @@ import base.RequestTask; import base.RequestType; -import burp.BurpExtender; -import burp.HelperPlus; -import burp.IBurpExtenderCallbacks; -import burp.IContextMenuInvocation; -import burp.IExtensionHelpers; -import burp.IHttpRequestResponse; -import burp.Utils; -import burp.threadRequester; +import burp.*; +import org.apache.commons.lang3.StringUtils; public class FindUrlAndRequest extends JMenuItem { - /** - * - */ - private static final long serialVersionUID = 1L; - - //JMenuItem vs. JMenu - public FindUrlAndRequest(BurpExtender burp){ - this.setText("^_^ Find URL And Request"); - this.addActionListener(new FindUrl_Action(burp,burp.invocation)); - } - - public static void main(String[] args) { - String url = "./abac/aaa.jpg"; - if (url.startsWith("./")) { - url = url.replaceFirst("\\./", ""); - } - System.out.println(url); - } + /** + * + */ + private static final long serialVersionUID = 1L; + + //JMenuItem vs. JMenu + public FindUrlAndRequest(BurpExtender burp) { + this.setText("^_^ Find URL And Request"); + this.addActionListener(new FindUrl_Action(burp, burp.invocation)); + } + + public static void main(String[] args) { + String url = "./abac/aaa.jpg"; + if (url.startsWith("./")) { + url = url.replaceFirst("\\./", ""); + } + System.out.println(url); + } } -class FindUrl_Action implements ActionListener{ - private IContextMenuInvocation invocation; - public IExtensionHelpers helpers; - public PrintWriter stdout; - public PrintWriter stderr; - public IBurpExtenderCallbacks callbacks; - public BurpExtender burp; - public static final String[] blackHostList = {"www.w3.org","ns.adobe.com","iptc.org","openoffice.org" - ,"schemas.microsoft.com","schemas.openxmlformats.org","sheetjs.openxmlformats.org"}; - - public FindUrl_Action(BurpExtender burp,IContextMenuInvocation invocation) { - this.burp = burp; - this.invocation = invocation; - this.helpers = burp.helpers; - this.callbacks = BurpExtender.callbacks; - this.stderr = BurpExtender.stderr; - this.stdout = BurpExtender.stdout; - } - - @Override - public void actionPerformed(ActionEvent event) { - Runnable requestRunner = new Runnable() { - String siteBaseUrl = null; - Set baseUrls = new HashSet(); - List urls = new ArrayList(); - @Override - public void run() { - try{ - IHttpRequestResponse[] messages = invocation.getSelectedMessages(); - if (messages == null || messages.length <=0) { - return; - } - - BlockingQueue inputQueue = new LinkedBlockingQueue(); - - try { - findUrls(messages[0]); - - if (baseUrls.size() <=0) { - return; - } - String baseurl = choseAndEditBaseURL(baseUrls); - - if (null==baseurl) { - return; - } - - for (String url:urls) { - if (Utils.uselessExtension(url)) { - continue; - } - if (!url.startsWith("http://") && !url.startsWith("https://")) { - if (url.startsWith("/")) { - url = url.replaceFirst("/", ""); - } - if (url.startsWith("./")) { - url = url.replaceFirst("\\./", ""); - } - url = baseurl+url; //baseurl统一以“/”结尾;url统一删除“/”的开头 - inputQueue.put(new RequestTask(url,RequestType.GET)); - inputQueue.put(new RequestTask(url,RequestType.POST)); - inputQueue.put(new RequestTask(url,RequestType.JSON)); - } - } - } catch (Exception e) { - e.printStackTrace(BurpExtender.getStderr()); - } - - doRequest(inputQueue,siteBaseUrl); - } - catch (Exception e1) - { - e1.printStackTrace(stderr); - } - } - - - /** - * 根据当前web的baseUrl找JS,特征就是referer以它开头 - * @param currentBaseUrl - * @return - */ - public void findUrls(IHttpRequestResponse message){ - HelperPlus getter = new HelperPlus(helpers); - - String current_referUrl = getter.getHeaderValueOf(true,message,"Referer"); - String current_fullUrl = getter.getFullURL(message).toString(); - - if (current_referUrl != null) { - baseUrls.add(current_referUrl); - } - baseUrls.add(current_fullUrl); - - if (current_fullUrl != null) { - siteBaseUrl = Utils.getBaseUrl(current_referUrl); - } - if (siteBaseUrl == null) { - siteBaseUrl = Utils.getBaseUrl(current_fullUrl); - } - - - IHttpRequestResponse[] messages = BurpExtender.getCallbacks().getSiteMap(null); - for (IHttpRequestResponse item:messages) { - int code = getter.getStatusCode(item); - URL url = getter.getFullURL(item); - String referUrl = getter.getHeaderValueOf(true,item,"Referer"); - if (referUrl == null || url== null || code <=0) { - continue; - } - if (!url.toString().toLowerCase().endsWith(".js")) { - continue; - } - if (referUrl.toLowerCase().startsWith(siteBaseUrl.toLowerCase()+"/")) { - byte[] respBody = getter.getBody(false, item); - String body = new String(respBody); - urls.addAll(Utils.grepURL(body)); - baseUrls.addAll(findPossibleBaseURL(urls)); - } - } - } - - }; - new Thread(requestRunner).start(); - } - - - - - /** - * 多线程执行请求 - * @param inputQueue - */ - public void doRequest(BlockingQueue inputQueue,String referUrl) { - String proxyHost = BurpExtender.getProxyHost(); - int proxyPort = BurpExtender.getProxyPort(); - - if (proxyHost == null || proxyPort == -1) { - return; - } - - int max = threadNumberShouldUse(inputQueue.size()); - - for (int i=0;i<=max;i++) { - threadRequester requester = new threadRequester(inputQueue,proxyHost,proxyPort,referUrl,i); - requester.start(); - } - } - - /** - * 根据已有的域名梳理,预估应该使用的线程数 - * 假设1个任务需要1秒钟。线程数在1-100之间,如何选择线程数使用最小的时间? - * @param domains - * @return - */ - public static int threadNumberShouldUse(int domainNum) { - - int tmp = (int) Math.sqrt(domainNum); - if (tmp <=1) { - return 1; - }else if(tmp>=10) { - return 10; - }else { - return tmp; - } - } - - public static Set findPossibleBaseURL(List urls) { - Set baseURLs = new HashSet(); - for (String tmpurl:urls) { - //这部分提取的是含有协议头的完整URL地址 - if (tmpurl.toLowerCase().startsWith("http://") - ||tmpurl.toLowerCase().startsWith("https://")){ - - try { - String host= new URL(tmpurl).getHost(); - if (Arrays.asList(blackHostList).contains(host)) { - continue; - } - }catch(Exception E) { - continue; - } - - baseURLs.add(tmpurl); - } - } - return baseURLs; - } - - public static String choseAndEditBaseURL(Set inputs) { - - ArrayList tmpList = new ArrayList(inputs); - Collections.sort(tmpList); - int n = inputs.size()+1; - String[] possibleValues = new String[n]; - - // Copying contents of domains to arr[] - System.arraycopy(tmpList.toArray(), 0, possibleValues, 0, n-1); - possibleValues[n-1] = "Let Me Input"; - - String selectedValue = (String) JOptionPane.showInputDialog(null, - "Chose Base URL", "Chose And Edit Base URL", - JOptionPane.INFORMATION_MESSAGE, null, - possibleValues, possibleValues[0]); - if (null != selectedValue) { - String baseUrl = JOptionPane.showInputDialog("Confirm The Base URL", selectedValue); - if (baseUrl == null) { - return null; - } - if (!baseUrl.endsWith("/")) { - baseUrl = baseUrl.trim()+"/"; - } - return baseUrl.trim(); - } - return selectedValue; - } +class FindUrl_Action implements ActionListener { + private IContextMenuInvocation invocation; + public IExtensionHelpers helpers; + public PrintWriter stdout; + public PrintWriter stderr; + public IBurpExtenderCallbacks callbacks; + public BurpExtender burp; + public static final String[] blackHostList = {"www.w3.org", "ns.adobe.com", "iptc.org", "openoffice.org" + , "schemas.microsoft.com", "schemas.openxmlformats.org", "sheetjs.openxmlformats.org"}; + + public FindUrl_Action(BurpExtender burp, IContextMenuInvocation invocation) { + this.burp = burp; + this.invocation = invocation; + this.helpers = burp.helpers; + this.callbacks = BurpExtender.callbacks; + this.stderr = BurpExtender.stderr; + this.stdout = BurpExtender.stdout; + } + + @Override + public void actionPerformed(ActionEvent event) { + Runnable requestRunner = new Runnable() { + String siteBaseUrl = null; + Set baseUrls = new HashSet(); + List urls = new ArrayList(); + + @Override + public void run() { + try { + IHttpRequestResponse[] messages = invocation.getSelectedMessages(); + if (messages == null || messages.length <= 0) { + return; + } + + BlockingQueue inputQueue = new LinkedBlockingQueue(); + + try { + findUrls(messages[0]); + + if (baseUrls.size() <= 0) { + return; + } + String baseurl = choseAndEditBaseURL(baseUrls); + + if (null == baseurl) { + return; + } + + for (String url : urls) { + if (Utils.uselessExtension(url)) { + continue; + } + if (!url.startsWith("http://") && !url.startsWith("https://")) { + if (url.startsWith("/")) { + url = url.replaceFirst("/", ""); + } + if (url.startsWith("./")) { + url = url.replaceFirst("\\./", ""); + } + url = baseurl + url; //baseurl统一以“/”结尾;url统一删除“/”的开头 + inputQueue.put(new RequestTask(url, RequestType.GET)); + inputQueue.put(new RequestTask(url, RequestType.POST)); + inputQueue.put(new RequestTask(url, RequestType.JSON)); + } + } + } catch (Exception e) { + e.printStackTrace(BurpExtender.getStderr()); + } + + doRequest(inputQueue, siteBaseUrl); + } catch (Exception e1) { + e1.printStackTrace(stderr); + } + } + + + /** + * 根据当前web的baseUrl找JS,特征就是referer以它开头 + * @param currentBaseUrl + * @return + */ + public void findUrls(IHttpRequestResponse message) { + HelperPlus getter = new HelperPlus(helpers); + + String current_referUrl = getter.getHeaderValueOf(true, message, "Referer"); + String current_fullUrl = getter.getFullURL(message).toString(); + + if (current_referUrl != null) { + baseUrls.add(current_referUrl); + } + baseUrls.add(current_fullUrl); + + if (current_fullUrl != null) { + siteBaseUrl = Utils.getBaseUrl(current_referUrl); + } + if (siteBaseUrl == null) { + siteBaseUrl = Utils.getBaseUrl(current_fullUrl); + } + + + IHttpRequestResponse[] messages = BurpExtender.getCallbacks().getSiteMap(null); + for (IHttpRequestResponse item : messages) { + int code = getter.getStatusCode(item); + URL url = getter.getFullURL(item); + String referUrl = getter.getHeaderValueOf(true, item, "Referer"); + if (referUrl == null || url == null || code <= 0) { + continue; + } + if (!url.toString().toLowerCase().endsWith(".js")) { + continue; + } + if (referUrl.toLowerCase().startsWith(siteBaseUrl.toLowerCase() + "/")) { + byte[] respBody = getter.getBody(false, item); + String body = new String(respBody); + urls.addAll(Utils.grepURL(body)); + baseUrls.addAll(findPossibleBaseURL(urls)); + } + } + } + + }; + new Thread(requestRunner).start(); + } + + + /** + * 多线程执行请求 + * + * @param inputQueue + */ + public void doRequest(BlockingQueue inputQueue, String referUrl) { + Proxy proxy = Proxy.inputProxy(); + if (proxy == null) { + return; + } + + int max = threadNumberShouldUse(inputQueue.size()); + + for (int i = 0; i <= max; i++) { + threadRequester requester = new threadRequester(inputQueue, proxy.getHost(), proxy.getPort(), referUrl, i); + requester.start(); + } + } + + /** + * 根据已有的域名梳理,预估应该使用的线程数 + * 假设1个任务需要1秒钟。线程数在1-100之间,如何选择线程数使用最小的时间? + * + * @param domains + * @return + */ + public static int threadNumberShouldUse(int domainNum) { + + int tmp = (int) Math.sqrt(domainNum); + if (tmp <= 1) { + return 1; + } else if (tmp >= 10) { + return 10; + } else { + return tmp; + } + } + + public static Set findPossibleBaseURL(List urls) { + Set baseURLs = new HashSet(); + for (String tmpurl : urls) { + //这部分提取的是含有协议头的完整URL地址 + if (tmpurl.toLowerCase().startsWith("http://") + || tmpurl.toLowerCase().startsWith("https://")) { + + try { + String host = new URL(tmpurl).getHost(); + if (Arrays.asList(blackHostList).contains(host)) { + continue; + } + } catch (Exception E) { + continue; + } + + baseURLs.add(tmpurl); + } + } + return baseURLs; + } + + public static String choseAndEditBaseURL(Set inputs) { + + ArrayList tmpList = new ArrayList(inputs); + Collections.sort(tmpList); + int n = inputs.size() + 1; + String[] possibleValues = new String[n]; + + // Copying contents of domains to arr[] + System.arraycopy(tmpList.toArray(), 0, possibleValues, 0, n - 1); + possibleValues[n - 1] = "Let Me Input"; + + String selectedValue = (String) JOptionPane.showInputDialog(null, + "Chose Base URL", "Chose And Edit Base URL", + JOptionPane.INFORMATION_MESSAGE, null, + possibleValues, possibleValues[0]); + if (null != selectedValue) { + String baseUrl = JOptionPane.showInputDialog("Confirm The Base URL", selectedValue); + if (baseUrl == null) { + return null; + } + if (!baseUrl.endsWith("/")) { + baseUrl = baseUrl.trim() + "/"; + } + return baseUrl.trim(); + } + return selectedValue; + } }