From 9e22898da7b9626b5267fee4b68b7f5609512bd1 Mon Sep 17 00:00:00 2001 From: Corey <1339555+CoreyD97@users.noreply.github.com> Date: Thu, 29 Sep 2022 15:30:07 +0100 Subject: [PATCH] Fix LogEntry to store both the final request and response. --- build.gradle | 9 +- .../exports/Base64Exporter.java | 4 +- .../loggerplusplus/exports/HarSerializer.java | 56 +++--- .../grepper/GrepperController.java | 12 +- .../loggerplusplus/grepper/GrepperPanel.java | 9 +- .../loggerplusplus/logentry/LogEntry.java | 185 ++++++++++-------- .../logview/MultipleLogEntryMenu.java | 32 +-- .../logview/SingleLogEntryMenu.java | 41 ++-- .../entryviewer/RequestViewerController.java | 52 +++-- .../logview/logtable/LogTable.java | 11 +- .../logview/logtable/LogTableModel.java | 4 +- .../logview/processor/LogProcessor.java | 8 +- 12 files changed, 218 insertions(+), 205 deletions(-) diff --git a/build.gradle b/build.gradle index d42b4c19..e69a0901 100644 --- a/build.gradle +++ b/build.gradle @@ -13,6 +13,9 @@ repositories { dependencies { compileOnly 'net.portswigger.burp.extender:burp-extender-api:2.3' // compileOnly 'net.portswigger.burp.extender:montoya-api:0.9.5' + compileOnly 'org.projectlombok:lombok:1.18.24' + annotationProcessor 'org.projectlombok:lombok:1.18.24' + implementation 'org.swinglabs:swingx:1.6.1' implementation 'com.github.CoreyD97:BurpExtenderUtilities:e800fd2d' implementation 'com.google.code.gson:gson:2.8.2' @@ -22,11 +25,13 @@ dependencies { implementation 'org.apache.logging.log4j:log4j-core:2.17.0' } -jar{ +jar { baseName = project.name + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + from { (configurations.runtimeClasspath).collect { it.isDirectory() ? it : zipTree(it) } - }{ + } { exclude "META-INF/*.SF" exclude "META-INF/*.DSA" exclude "META-INF/*.RSA" diff --git a/src/main/java/com/nccgroup/loggerplusplus/exports/Base64Exporter.java b/src/main/java/com/nccgroup/loggerplusplus/exports/Base64Exporter.java index 83582a88..7b3262e8 100644 --- a/src/main/java/com/nccgroup/loggerplusplus/exports/Base64Exporter.java +++ b/src/main/java/com/nccgroup/loggerplusplus/exports/Base64Exporter.java @@ -46,12 +46,12 @@ protected Void doInBackground() throws Exception { JsonObject jsonEntry = new JsonObject(); if (includeRequest) { jsonEntry.addProperty("request", - encoder.encodeToString(entry.requestResponse.getRequest())); + encoder.encodeToString(entry.getRequest())); } if (includeResponse) { jsonEntry.addProperty("response", - encoder.encodeToString(entry.requestResponse.getResponse())); + encoder.encodeToString(entry.getResponse())); } jsonEntries.add(jsonEntry); } diff --git a/src/main/java/com/nccgroup/loggerplusplus/exports/HarSerializer.java b/src/main/java/com/nccgroup/loggerplusplus/exports/HarSerializer.java index d313449c..942d05f1 100644 --- a/src/main/java/com/nccgroup/loggerplusplus/exports/HarSerializer.java +++ b/src/main/java/com/nccgroup/loggerplusplus/exports/HarSerializer.java @@ -53,20 +53,20 @@ public void write(JsonWriter writer, List logEntries) throws IOExcepti final String pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern); - writer.name("startedDateTime").value(simpleDateFormat.format(logEntry.requestDateTime)); + writer.name("startedDateTime").value(simpleDateFormat.format(logEntry.getRequestDateTime())); - long time = logEntry.responseDateTime.getTime() - logEntry.requestDateTime.getTime(); + long time = logEntry.getResponseDateTime().getTime() - logEntry.getRequestDateTime().getTime(); time = time < 0 ? 0 : time; writer.name("time").value(time); writer.name("request").beginObject(); - writer.name("method").value(logEntry.method); - writer.name("url").value(logEntry.url.toString()); - writer.name("httpVersion").value(logEntry.requestHttpVersion); - writer.name("origin").value(logEntry.origin); + writer.name("method").value(logEntry.getMethod()); + writer.name("url").value(logEntry.getUrl().toString()); + writer.name("httpVersion").value(logEntry.getRequestHttpVersion()); + writer.name("origin").value(logEntry.getOrigin()); writer.name("cookies").beginArray(); - if (logEntry.hasCookieParam) { - List cookies = getRequestParametersByType(logEntry.requestResponse.getRequest(), + if (logEntry.isHasCookieParam()) { + List cookies = getRequestParametersByType(logEntry.getRequest(), IParameter.PARAM_COOKIE); for (IParameter cookie : cookies) { writer.beginObject(); @@ -78,7 +78,7 @@ public void write(JsonWriter writer, List logEntries) throws IOExcepti writer.endArray(); // end request cookies array writer.name("headers").beginArray(); - for (String headerString : logEntry.requestHeaders) { + for (String headerString : logEntry.getRequestHeaders()) { if (headerString.contains(":")) { writer.beginObject(); String headerArray[] = headerString.split(":", 2); @@ -90,8 +90,8 @@ public void write(JsonWriter writer, List logEntries) throws IOExcepti writer.endArray(); // end request headers array writer.name("queryString").beginArray(); - if (logEntry.url.getQuery() != null) { - List queryParams = getRequestParametersByType(logEntry.requestResponse.getRequest(), + if (logEntry.getUrl().getQuery() != null) { + List queryParams = getRequestParametersByType(logEntry.getRequest(), IParameter.PARAM_URL); for (IParameter queryParam : queryParams) { writer.beginObject(); @@ -102,10 +102,10 @@ public void write(JsonWriter writer, List logEntries) throws IOExcepti } writer.endArray(); // end request queryString array - if (logEntry.hasBodyParam) { + if (logEntry.isHasBodyParam()) { writer.name("postData").beginObject(); - writer.name("mimeType").value(logEntry.requestContentType); - List bodyParams = getRequestBodyParameters(logEntry.requestResponse.getRequest()); + writer.name("mimeType").value(logEntry.getRequestContentType()); + List bodyParams = getRequestBodyParameters(logEntry.getRequest()); writer.name("params").beginArray(); for (IParameter bodyParam : bodyParams) { writer.beginObject(); @@ -118,19 +118,19 @@ public void write(JsonWriter writer, List logEntries) throws IOExcepti writer.endObject(); // end postData object } - writer.name("headersSize").value(logEntry.requestResponse.getRequest().length - logEntry.requestBodyLength); - writer.name("bodySize").value(logEntry.requestBodyLength); + writer.name("headersSize").value(logEntry.getRequest().length - logEntry.getRequestBodyLength()); + writer.name("bodySize").value(logEntry.getRequestBodyLength()); writer.endObject(); // end request object writer.name("response").beginObject(); - writer.name("status").value(logEntry.responseStatus); - writer.name("statusText").value(logEntry.responseStatusText); - writer.name("httpVersion").value(logEntry.responseHttpVersion); + writer.name("status").value(logEntry.getResponseStatus()); + writer.name("statusText").value(logEntry.getResponseStatusText()); + writer.name("httpVersion").value(logEntry.getResponseHttpVersion()); writer.name("cookies").beginArray(); - if (logEntry.hasSetCookies) { - List cookies = getResponseCookies(logEntry.requestResponse.getResponse()); + if (logEntry.isHasSetCookies()) { + List cookies = getResponseCookies(logEntry.getResponse()); for (ICookie cookie : cookies) { writer.beginObject(); @@ -144,8 +144,8 @@ public void write(JsonWriter writer, List logEntries) throws IOExcepti writer.endArray(); // end response cookies array writer.name("headers").beginArray(); - if (logEntry.responseHeaders != null) { - for (String headerString : logEntry.responseHeaders) { + if (logEntry.getResponseHeaders() != null) { + for (String headerString : logEntry.getResponseHeaders()) { if (headerString.contains(":")) { writer.beginObject(); String headerArray[] = headerString.split(":", 2); @@ -158,9 +158,9 @@ public void write(JsonWriter writer, List logEntries) throws IOExcepti writer.endArray(); // end response headers array writer.name("redirectURL").value(String.valueOf(logEntry.getValueByKey(LogEntryField.REDIRECT_URL))); - if (logEntry.requestResponse.getResponse() != null) { - writer.name("headersSize").value(logEntry.requestResponse.getResponse().length - logEntry.responseBodyLength); - writer.name("bodySize").value(logEntry.responseBodyLength); + if (logEntry.getResponse() != null) { + writer.name("headersSize").value(logEntry.getResponse().length - logEntry.getResponseBodyLength()); + writer.name("bodySize").value(logEntry.getResponseBodyLength()); } else { writer.name("headersSize").value(0); writer.name("bodySize").value(0); @@ -168,8 +168,8 @@ public void write(JsonWriter writer, List logEntries) throws IOExcepti writer.name("content").beginObject(); // start content object - writer.name("size").value(logEntry.responseBodyLength); - writer.name("mimeType").value(logEntry.responseMimeType); + writer.name("size").value(logEntry.getResponseBodyLength()); + writer.name("mimeType").value(logEntry.getResponseMimeType()); writer.name("text").value(String.valueOf(logEntry.getValueByKey(LogEntryField.RESPONSE_BODY))); writer.endObject(); //end content object diff --git a/src/main/java/com/nccgroup/loggerplusplus/grepper/GrepperController.java b/src/main/java/com/nccgroup/loggerplusplus/grepper/GrepperController.java index 4b78e1f2..09128b1b 100644 --- a/src/main/java/com/nccgroup/loggerplusplus/grepper/GrepperController.java +++ b/src/main/java/com/nccgroup/loggerplusplus/grepper/GrepperController.java @@ -95,13 +95,13 @@ public void beginSearch(final Pattern pattern, final boolean inScopeOnly, final private GrepResults processEntry(LogEntry entry, Pattern pattern, final boolean searchRequests, final boolean searchResponses) { GrepResults grepResults = null; - if (entry.requestResponse != null) { + if (entry != null) { grepResults = new GrepResults(entry); - if (entry.requestResponse.getRequest() != null && searchRequests) { - processMatches(grepResults, pattern, entry.requestResponse.getRequest(), true); + if (entry.getRequest() != null && searchRequests) { + processMatches(grepResults, pattern, entry.getRequest(), true); } - if (entry.requestResponse.getResponse() != null && searchResponses) { - processMatches(grepResults, pattern, entry.requestResponse.getResponse(), false); + if (entry.getResponse() != null && searchResponses) { + processMatches(grepResults, pattern, entry.getResponse(), false); } } return grepResults; @@ -128,7 +128,7 @@ private Runnable createProcessThread(final LogEntry logEntry, final Pattern patt return () -> { if (Thread.currentThread().isInterrupted()) return; GrepResults grepResults = null; - if (!inScopeOnly || LoggerPlusPlus.callbacks.isInScope(logEntry.url)) { + if (!inScopeOnly || LoggerPlusPlus.callbacks.isInScope(logEntry.getUrl())) { grepResults = processEntry(logEntry, pattern, searchRequests, searchResponses); } for (GrepperListener listener : this.listeners) { diff --git a/src/main/java/com/nccgroup/loggerplusplus/grepper/GrepperPanel.java b/src/main/java/com/nccgroup/loggerplusplus/grepper/GrepperPanel.java index ce31eb3c..98159d68 100644 --- a/src/main/java/com/nccgroup/loggerplusplus/grepper/GrepperPanel.java +++ b/src/main/java/com/nccgroup/loggerplusplus/grepper/GrepperPanel.java @@ -1,10 +1,9 @@ package com.nccgroup.loggerplusplus.grepper; -import burp.IHttpRequestResponse; -import burp.IHttpRequestResponseWithMarkers; import com.coreyd97.BurpExtenderUtilities.HistoryField; import com.coreyd97.BurpExtenderUtilities.PanelBuilder; import com.coreyd97.BurpExtenderUtilities.Preferences; +import com.nccgroup.loggerplusplus.logentry.LogEntry; import com.nccgroup.loggerplusplus.logview.entryviewer.RequestViewerController; import com.nccgroup.loggerplusplus.util.Globals; @@ -83,7 +82,7 @@ public void actionPerformed(ActionEvent e) { selectedMatch = (GrepResults.Match) selectedPath.getPath()[2]; } - IHttpRequestResponse requestResponse = grepResultEntry.getLogEntry().getRequestResponse(); + LogEntry requestResponse = grepResultEntry.getLogEntry(); List matches; if (selectedMatch != null) { @@ -92,10 +91,10 @@ public void actionPerformed(ActionEvent e) { matches = grepResultEntry.getMatches(); } - IHttpRequestResponseWithMarkers markedRequestResponse = controller.addMarkers(requestResponse, matches); - requestViewerController.setDisplayedEntity(markedRequestResponse); + requestViewerController.setDisplayedEntity(requestResponse); //TODO Setup message editor to support highlighting. Code is ready, waiting on API support. +// IHttpRequestResponseWithMarkers markedRequestResponse = controller.addMarkers(requestResponse, matches); //https://forum.portswigger.net/thread/eeditor-custom-highlighting-991b1a7e?CategoryId=burp-extensions }); diff --git a/src/main/java/com/nccgroup/loggerplusplus/logentry/LogEntry.java b/src/main/java/com/nccgroup/loggerplusplus/logentry/LogEntry.java index bf32d8ee..0007cfcf 100644 --- a/src/main/java/com/nccgroup/loggerplusplus/logentry/LogEntry.java +++ b/src/main/java/com/nccgroup/loggerplusplus/logentry/LogEntry.java @@ -18,8 +18,12 @@ import com.nccgroup.loggerplusplus.filter.colorfilter.ColorFilter; import com.nccgroup.loggerplusplus.filter.tag.Tag; import com.nccgroup.loggerplusplus.logview.processor.LogProcessor; +import com.nccgroup.loggerplusplus.logview.processor.LogProcessorHelper; import com.nccgroup.loggerplusplus.reflection.ReflectionController; import com.nccgroup.loggerplusplus.util.Globals; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; @@ -30,60 +34,66 @@ import java.util.regex.Matcher; import java.util.stream.Collectors; +@Getter +@Setter public class LogEntry { Status previousStatus; Status status = Status.UNPROCESSED; - public transient IHttpRequestResponse requestResponse; - - public UUID identifier; - public int tool; - public String toolName; - public String hostname = ""; - public String host = ""; // TODO better name? - public String method = ""; - public URL url; - public boolean params = false; - public Short responseStatus = -1; - public String responseStatusText = ""; - public String responseHttpVersion = ""; - public boolean hasBodyParam = false; - public boolean hasCookieParam = false; - public String title = ""; - public String newCookies = ""; - public String sentCookies = ""; - public String listenerInterface = ""; - public boolean isSSL = false; - public String urlExtension = ""; - public String referrerURL = ""; - public String requestHttpVersion = ""; - public String requestContentType = ""; - public String protocol = ""; - public int targetPort = -1; - public int requestBodyLength = -1; - public String clientIP = ""; - public boolean hasSetCookies = false; - public String formattedResponseTime = ""; - public String responseMimeType = ""; - public String responseInferredMimeType = ""; - public int responseBodyLength = -1; - public String responseContentType = ""; - public boolean complete = false; - public CookieJarStatus usesCookieJar = CookieJarStatus.NO; - public String responseHash; - public String redirectURL; - public String origin = ""; - // public String[] regexAllReq = {"","","","",""}; - // public String[] regexAllResp = {"","","","",""}; - - public List matchingColorFilters; - public List matchingTags; - public String formattedRequestTime; - public Date responseDateTime = new Date(0); //Zero epoch dates to prevent null. Response date pulled from response headers - public Date requestDateTime = new Date(0); //Zero epoch dates to prevent null. Response date pulled from response headers - public int requestResponseDelay = -1; - public List responseHeaders; - public List requestHeaders; + + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) + private IHttpRequestResponse requestResponse; //Only used for request, comment and HTTP Service. + private byte[] response; + + private UUID identifier; + private int tool; + private String toolName; + private String hostname = ""; + private String host = ""; // TODO better name? + private String method = ""; + private URL url; + private boolean params = false; + private Short responseStatus = -1; + private String responseStatusText = ""; + private String responseHttpVersion = ""; + private boolean hasBodyParam = false; + private boolean hasCookieParam = false; + private String title = ""; + private String newCookies = ""; + private String sentCookies = ""; + private String listenerInterface = ""; + private boolean isSSL = false; + private String urlExtension = ""; + private String referrerURL = ""; + private String requestHttpVersion = ""; + private String requestContentType = ""; + private String protocol = ""; + private int targetPort = -1; + private int requestBodyLength = -1; + private String clientIP = ""; + private boolean hasSetCookies = false; + private String formattedResponseTime = ""; + private String responseMimeType = ""; + private String responseInferredMimeType = ""; + private int responseBodyLength = -1; + private String responseContentType = ""; + private boolean complete = false; + private CookieJarStatus usesCookieJar = CookieJarStatus.NO; + private String responseHash; + private String redirectURL; + private String origin = ""; + // private String[] regexAllReq = {"","","","",""}; + // private String[] regexAllResp = {"","","","",""}; + + private List matchingColorFilters; + private List matchingTags; + private String formattedRequestTime; + private Date responseDateTime = new Date(0); //Zero epoch dates to prevent null. Response date pulled from response headers + private Date requestDateTime = new Date(0); //Zero epoch dates to prevent null. Response date pulled from response headers + private int requestResponseDelay = -1; + private List responseHeaders; + private List requestHeaders; private List tempParameters; private List parameters; private List reflectedParameters; @@ -99,6 +109,7 @@ public LogEntry(int tool, IHttpRequestResponse requestResponse) { this.tool = tool; this.toolName = LoggerPlusPlus.callbacks.getToolName(tool); this.requestResponse = requestResponse; + this.response = requestResponse.getResponse(); } /** @@ -126,7 +137,7 @@ public void process() { // Else continue, fall through to process response } case AWAITING_RESPONSE: { - if (this.requestResponse.getResponse() == null) { + if (this.response == null) { this.status = Status.AWAITING_RESPONSE; break; } @@ -161,7 +172,6 @@ private Status processRequest() { if (!LoggerPlusPlus.isUrlInScope(uUrl)) return Status.IGNORED; - IHttpService tempRequestResponseHttpService = requestResponse.getHttpService(); requestHeaders = tempAnalyzedReq.getHeaders(); // Get HTTP Version, which would be the last token in "GET /admin/login/?next\u003d/admin/ HTTP/1.1" @@ -173,10 +183,10 @@ private Status processRequest() { this.parameters = tempParameters.stream().map(IParameter::getName).collect(Collectors.toList()); this.url = tempAnalyzedReq.getUrl(); - this.hostname = tempRequestResponseHttpService.getHost(); - this.protocol = tempRequestResponseHttpService.getProtocol(); + this.hostname = this.requestResponse.getHttpService().getHost(); + this.protocol = this.requestResponse.getHttpService().getProtocol(); this.isSSL = this.protocol.equals("https"); - this.targetPort = tempRequestResponseHttpService.getPort(); + this.targetPort = this.requestResponse.getHttpService().getPort(); boolean isDefaultPort = (this.protocol.equals("https") && this.targetPort == 443) || (this.protocol.equals("http") && this.targetPort == 80); @@ -196,7 +206,7 @@ private Status processRequest() { this.urlExtension = ""; } - this.requestBodyLength = requestResponse.getRequest().length - tempAnalyzedReq.getBodyOffset(); + this.requestBodyLength = this.getRequest().length - tempAnalyzedReq.getBodyOffset(); this.hasBodyParam = requestBodyLength > 0; this.params = this.url.getQuery() != null || this.hasBodyParam; this.hasCookieParam = false; @@ -310,16 +320,20 @@ private Status processRequest() { */ public void addResponse(IHttpRequestResponse requestResponse, Date arrivalTime) { this.responseDateTime = arrivalTime; - this.requestResponse = requestResponse; + + //IHttpRequestResponse objects received by the proxy listener do not contain the latest request. + //So we must store the content separately. + this.response = requestResponse.getResponse(); + this.setComment(requestResponse.getComment()); //Update the comment with the current comment } private Status processResponse() { reflectedParameters = new ArrayList<>(); IResponseInfo tempAnalyzedResp = LoggerPlusPlus.callbacks.getHelpers() - .analyzeResponse(requestResponse.getResponse()); + .analyzeResponse(response); this.responseStatus = tempAnalyzedResp.getStatusCode(); - this.responseBodyLength = requestResponse.getResponse().length - tempAnalyzedResp.getBodyOffset(); + this.responseBodyLength = response.length - tempAnalyzedResp.getBodyOffset(); this.responseMimeType = tempAnalyzedResp.getStatedMimeType(); this.responseInferredMimeType = tempAnalyzedResp.getInferredMimeType(); @@ -395,18 +409,18 @@ private Status processResponse() { **************************************/ Long maxRespSize = ((Integer) LoggerPlusPlus.instance.getPreferencesController().getPreferences().getSetting(Globals.PREF_MAX_RESP_SIZE)) * 1000000L; - int bodyOffset = requestResponse.getResponse().length - responseBodyLength; + int bodyOffset = response.length - responseBodyLength; if (responseBodyLength < maxRespSize) { //Only title match HTML files. Prevents expensive regex running on e.g. binary downloads. if (this.responseInferredMimeType.equalsIgnoreCase("HTML")) { - String strFullResponse = new String(requestResponse.getResponse()); + String strFullResponse = new String(response); Matcher titleMatcher = Globals.HTML_TITLE_PATTERN.matcher(strFullResponse); if (titleMatcher.find()) { this.title = titleMatcher.group(1); } } - String responseBody = new String(requestResponse.getResponse(), bodyOffset, responseBodyLength); + String responseBody = new String(response, bodyOffset, responseBodyLength); ReflectionController reflectionController = LoggerPlusPlus.instance.getReflectionController(); reflectedParameters = tempParameters.parallelStream() .filter(iParameter -> !reflectionController.isParameterFiltered(iParameter) @@ -419,17 +433,11 @@ private Status processResponse() { ReflectionController reflectionController = LoggerPlusPlus.instance.getReflectionController(); reflectedParameters = tempParameters.parallelStream() .filter(iParameter -> !reflectionController.isParameterFiltered(iParameter) - && reflectionController.validReflection(new String(requestResponse.getResponse(), 0, bodyOffset), iParameter)) + && reflectionController.validReflection(new String(response, 0, bodyOffset), iParameter)) .map(IParameter::getName).collect(Collectors.toList()); //Trim the response down to a maximum size, but at least keep the headers! - //Then save the buffers to temp files - //And finally restore the original body to the original IHttpRequestResponse object so we don't modify the content sent to the browser. - IHttpRequestResponse original = this.requestResponse; - byte[] originalResponse = this.requestResponse.getResponse(); - requestResponse.setResponse((new String(this.requestResponse.getResponse(), 0, requestResponse.getResponse().length - responseBodyLength) + "Response body trimmed by Logger++. To prevent this, increase \"Maximum Response Size\" in the Logger++ options.").getBytes(StandardCharsets.UTF_8)); -// this.requestResponse = LoggerPlusPlus.callbacks.saveBuffersToTempFiles(requestResponse); - original.setResponse(originalResponse); + this.response = (new String(this.response, 0, bodyOffset) + "Response body trimmed by Logger++. To prevent this, increase \"Maximum Response Size\" in the Logger++ options.").getBytes(StandardCharsets.UTF_8); } @@ -494,8 +502,12 @@ private Status processResponse() { // } } - public IHttpRequestResponse getRequestResponse() { - return requestResponse; + public byte[] getRequest() { + return this.requestResponse.getRequest(); + } + + public byte[] getResponse() { + return response; } public UUID getIdentifier() { @@ -516,13 +528,18 @@ public void setComment(String comment) { this.requestResponse.setComment(comment); } + public String getComment() { + return this.requestResponse.getComment(); + } + + public Object getValueByKey(LogEntryField columnName) { try { switch (columnName) { case PROXY_TOOL: case REQUEST_TOOL: - return toolName; + return getToolName(); case TAGS: return this.matchingTags.stream().map(Tag::getName).collect(Collectors.toList()); case URL: @@ -629,12 +646,12 @@ public Object getValueByKey(LogEntryField columnName) { return reflectedParameters.size(); case REQUEST_BODY: // request if (requestBodyLength == 0) return ""; - return new String(requestResponse.getRequest(), requestResponse.getRequest().length - requestBodyLength, requestBodyLength); -// .substring(requestResponse.getRequest().length - requestBodyLength); + return new String(getRequest(), getRequest().length - requestBodyLength, requestBodyLength); +// .substring(request.length - requestBodyLength); case RESPONSE_BODY: // response if (responseBodyLength == 0) return ""; - return new String(requestResponse.getResponse(), requestResponse.getResponse().length - responseBodyLength, responseBodyLength); -// .substring(requestResponse.getResponse().length - responseBodyLength); + return new String(response, response.length - responseBodyLength, responseBodyLength); +// .substring(response.length - responseBodyLength); case RTT: return requestResponseDelay; case REQUEST_HEADERS: @@ -644,9 +661,9 @@ public Object getValueByKey(LogEntryField columnName) { case REDIRECT_URL: return redirectURL; case BASE64_REQUEST: - return Base64.getEncoder().encodeToString(requestResponse.getRequest()); + return Base64.getEncoder().encodeToString(this.getRequest()); case BASE64_RESPONSE: - return Base64.getEncoder().encodeToString(requestResponse.getResponse()); + return Base64.getEncoder().encodeToString(response); case RESPONSE_HASH: { if (responseHash == null) { responseHash = DigestUtils @@ -670,6 +687,10 @@ public List getMatchingTags() { return matchingTags; } + public IHttpService getHttpService() { + return this.requestResponse.getHttpService(); + } + public enum CookieJarStatus { YES("Yes"), NO("No"), PARTIALLY("Partially"); @@ -747,6 +768,10 @@ public boolean testTag(Tag tag, boolean retest) { @Override public String toString() { - return this.status + " " + this.url.toString(); + return this.url.toString(); + } + + public static UUID extractAndRemoveUUIDFromComment(String instanceIdentifier, LogEntry logEntry) { + return LogProcessorHelper.extractAndRemoveUUIDFromRequestResponseComment(instanceIdentifier, logEntry.requestResponse); } } diff --git a/src/main/java/com/nccgroup/loggerplusplus/logview/MultipleLogEntryMenu.java b/src/main/java/com/nccgroup/loggerplusplus/logview/MultipleLogEntryMenu.java index d36fa5ea..015bd619 100644 --- a/src/main/java/com/nccgroup/loggerplusplus/logview/MultipleLogEntryMenu.java +++ b/src/main/java/com/nccgroup/loggerplusplus/logview/MultipleLogEntryMenu.java @@ -95,7 +95,7 @@ public void actionPerformed(ActionEvent actionEvent) { @Override public void actionPerformed(ActionEvent actionEvent) { for (LogEntry entry : selectedEntries) { - LoggerPlusPlus.callbacks.sendToSpider(entry.url); + LoggerPlusPlus.callbacks.sendToSpider(entry.getUrl()); } } }); @@ -105,7 +105,7 @@ public void actionPerformed(ActionEvent actionEvent) { @Override public void actionPerformed(ActionEvent actionEvent) { for (LogEntry entry : selectedEntries) { - LoggerPlusPlus.callbacks.doActiveScan(entry.hostname, entry.targetPort, entry.isSSL, entry.requestResponse.getRequest()); + LoggerPlusPlus.callbacks.doActiveScan(entry.getHostname(), entry.getTargetPort(), entry.isSSL(), entry.getRequest()); } } }); @@ -116,8 +116,8 @@ public void actionPerformed(ActionEvent actionEvent) { @Override public void actionPerformed(ActionEvent actionEvent) { for (LogEntry entry : selectedEntries) { - if(entry.complete) { //Cannot scan entries without response - LoggerPlusPlus.callbacks.doPassiveScan(entry.hostname, entry.targetPort, entry.isSSL, entry.requestResponse.getRequest(), entry.requestResponse.getResponse()); + if (entry.isComplete()) { //Cannot scan entries without response + LoggerPlusPlus.callbacks.doPassiveScan(entry.getHostname(), entry.getTargetPort(), entry.isSSL(), entry.getRequest(), entry.getResponse()); } } } @@ -131,7 +131,7 @@ public void actionPerformed(ActionEvent actionEvent) { @Override public void actionPerformed(ActionEvent actionEvent) { for (LogEntry entry : selectedEntries) { - LoggerPlusPlus.callbacks.sendToRepeater(entry.hostname, entry.targetPort, entry.isSSL, entry.requestResponse.getRequest(), "L++"); + LoggerPlusPlus.callbacks.sendToRepeater(entry.getHostname(), entry.getTargetPort(), entry.isSSL(), entry.getRequest(), "L++"); } } }); @@ -141,7 +141,7 @@ public void actionPerformed(ActionEvent actionEvent) { @Override public void actionPerformed(ActionEvent actionEvent) { for (LogEntry entry : selectedEntries) { - LoggerPlusPlus.callbacks.sendToIntruder(entry.hostname, entry.targetPort, entry.isSSL, entry.requestResponse.getRequest()); + LoggerPlusPlus.callbacks.sendToIntruder(entry.getHostname(), entry.getTargetPort(), entry.isSSL(), entry.getRequest()); } } }); @@ -152,7 +152,7 @@ public void actionPerformed(ActionEvent actionEvent) { @Override public void actionPerformed(ActionEvent actionEvent) { for (LogEntry entry : selectedEntries) { - LoggerPlusPlus.callbacks.sendToComparer(entry.requestResponse.getRequest()); + LoggerPlusPlus.callbacks.sendToComparer(entry.getRequest()); } } }); @@ -161,8 +161,8 @@ public void actionPerformed(ActionEvent actionEvent) { @Override public void actionPerformed(ActionEvent actionEvent) { for (LogEntry entry : selectedEntries) { - if(entry.complete) { //Do not add entries without a response - LoggerPlusPlus.callbacks.sendToComparer(entry.requestResponse.getResponse()); + if (entry.isComplete()) { //Do not add entries without a response + LoggerPlusPlus.callbacks.sendToComparer(entry.getResponse()); } } } @@ -191,10 +191,16 @@ private void copySelected(List items, Scope scope, boolean onlyUnique) if(onlyUnique) values = new LinkedHashSet(); else values = new LinkedList<>(); for (LogEntry item : items) { - switch (scope){ - case URL: values.add(String.valueOf(item.url)); break; - case PATH: values.add(item.url.getPath()); break; - case DOMAIN: values.add(item.hostname); break; + switch (scope) { + case URL: + values.add(String.valueOf(item.getUrl())); + break; + case PATH: + values.add(item.getUrl().getPath()); + break; + case DOMAIN: + values.add(item.getHostname()); + break; } } diff --git a/src/main/java/com/nccgroup/loggerplusplus/logview/SingleLogEntryMenu.java b/src/main/java/com/nccgroup/loggerplusplus/logview/SingleLogEntryMenu.java index 66878652..84ff316c 100644 --- a/src/main/java/com/nccgroup/loggerplusplus/logview/SingleLogEntryMenu.java +++ b/src/main/java/com/nccgroup/loggerplusplus/logview/SingleLogEntryMenu.java @@ -14,9 +14,7 @@ import com.nccgroup.loggerplusplus.logview.logtable.LogTable; import com.nccgroup.loggerplusplus.logview.logtable.LogTableController; import com.nccgroup.loggerplusplus.logview.processor.LogProcessor; -import com.nccgroup.loggerplusplus.util.Globals; import com.nccgroup.loggerplusplus.util.userinterface.dialog.ColorFilterDialog; -import org.apache.logging.log4j.Level; import javax.swing.*; import java.awt.event.ActionEvent; @@ -112,7 +110,7 @@ public void actionPerformed(ActionEvent actionEvent) { } this.add(new JPopupMenu.Separator()); - final boolean inScope = LoggerPlusPlus.callbacks.isInScope(entry.url); + final boolean inScope = LoggerPlusPlus.callbacks.isInScope(entry.getUrl()); JMenuItem scopeItem; if(!inScope){ scopeItem = new JMenu("Add to scope"); @@ -120,7 +118,7 @@ public void actionPerformed(ActionEvent actionEvent) { @Override public void actionPerformed(ActionEvent actionEvent) { try { - URL domainURL = new URL(entry.protocol, entry.hostname, entry.targetPort, ""); + URL domainURL = new URL(entry.getProtocol(), entry.getHostname(), entry.getTargetPort(), ""); LoggerPlusPlus.callbacks.includeInScope(domainURL); } catch (MalformedURLException e) { JOptionPane.showMessageDialog(scopeItem, "Could not build URL for scope entry. Sorry!", "Add to scope", JOptionPane.ERROR_MESSAGE); @@ -130,14 +128,14 @@ public void actionPerformed(ActionEvent actionEvent) { scopeItem.add(new JMenuItem(new AbstractAction("Domain + Path") { @Override public void actionPerformed(ActionEvent actionEvent) { - LoggerPlusPlus.callbacks.includeInScope(entry.url); + LoggerPlusPlus.callbacks.includeInScope(entry.getUrl()); } })); }else{ scopeItem = new JMenuItem(new AbstractAction("Remove from scope") { @Override public void actionPerformed(ActionEvent actionEvent) { - LoggerPlusPlus.callbacks.excludeFromScope(entry.url); + LoggerPlusPlus.callbacks.excludeFromScope(entry.getUrl()); } }); } @@ -162,7 +160,7 @@ public void actionPerformed(ActionEvent actionEvent) { JMenuItem spider = new JMenuItem(new AbstractAction("Spider from here") { @Override public void actionPerformed(ActionEvent actionEvent) { - LoggerPlusPlus.callbacks.sendToSpider(entry.url); + LoggerPlusPlus.callbacks.sendToSpider(entry.getUrl()); } }); this.add(spider); @@ -170,7 +168,7 @@ public void actionPerformed(ActionEvent actionEvent) { JMenuItem activeScan = new JMenuItem(new AbstractAction("Do an active scan") { @Override public void actionPerformed(ActionEvent actionEvent) { - LoggerPlusPlus.callbacks.doActiveScan(entry.hostname, entry.targetPort, entry.isSSL, entry.requestResponse.getRequest()); + LoggerPlusPlus.callbacks.doActiveScan(entry.getHostname(), entry.getTargetPort(), entry.isSSL(), entry.getRequest()); } }); this.add(activeScan); @@ -179,10 +177,10 @@ public void actionPerformed(ActionEvent actionEvent) { JMenuItem passiveScan = new JMenuItem(new AbstractAction("Do a passive scan") { @Override public void actionPerformed(ActionEvent actionEvent) { - LoggerPlusPlus.callbacks.doPassiveScan(entry.hostname, entry.targetPort, entry.isSSL, entry.requestResponse.getRequest(), entry.requestResponse.getResponse()); + LoggerPlusPlus.callbacks.doPassiveScan(entry.getHostname(), entry.getTargetPort(), entry.isSSL(), entry.getRequest(), entry.getResponse()); } }); - passiveScan.setEnabled(entry.complete && isPro); + passiveScan.setEnabled(entry.isComplete() && isPro); this.add(passiveScan); this.add(new JPopupMenu.Separator()); @@ -190,7 +188,7 @@ public void actionPerformed(ActionEvent actionEvent) { JMenuItem sendToRepeater = new JMenuItem(new AbstractAction("Send to Repeater") { @Override public void actionPerformed(ActionEvent actionEvent) { - LoggerPlusPlus.callbacks.sendToRepeater(entry.hostname, entry.targetPort, entry.isSSL, entry.requestResponse.getRequest(), "L++"); + LoggerPlusPlus.callbacks.sendToRepeater(entry.getHostname(), entry.getTargetPort(), entry.isSSL(), entry.getRequest(), "L++"); } }); this.add(sendToRepeater); @@ -198,7 +196,7 @@ public void actionPerformed(ActionEvent actionEvent) { JMenuItem sendToIntruder = new JMenuItem(new AbstractAction("Send to Intruder") { @Override public void actionPerformed(ActionEvent actionEvent) { - LoggerPlusPlus.callbacks.sendToIntruder(entry.hostname, entry.targetPort, entry.isSSL, entry.requestResponse.getRequest()); + LoggerPlusPlus.callbacks.sendToIntruder(entry.getHostname(), entry.getTargetPort(), entry.isSSL(), entry.getRequest()); } }); this.add(sendToIntruder); @@ -207,34 +205,19 @@ public void actionPerformed(ActionEvent actionEvent) { JMenuItem comparerRequest = new JMenuItem(new AbstractAction("Request") { @Override public void actionPerformed(ActionEvent actionEvent) { - LoggerPlusPlus.callbacks.sendToComparer(entry.requestResponse.getRequest()); + LoggerPlusPlus.callbacks.sendToComparer(entry.getRequest()); } }); sendToComparer.add(comparerRequest); JMenuItem comparerResponse = new JMenuItem(new AbstractAction("Response") { @Override public void actionPerformed(ActionEvent actionEvent) { - LoggerPlusPlus.callbacks.sendToComparer(entry.requestResponse.getResponse()); + LoggerPlusPlus.callbacks.sendToComparer(entry.getResponse()); } }); sendToComparer.add(comparerResponse); this.add(sendToComparer); - if (logTableController.getPreferences().getSetting(Globals.PREF_LOG_LEVEL) == Level.DEBUG && entry.requestResponse != null) { - this.add(new JPopupMenu.Separator()); - JMenuItem reprocessItem = new JMenuItem(new AbstractAction("Reprocess Entry") { - @Override - public void actionPerformed(ActionEvent actionEvent) { - new Thread(() -> { - //TODO ADD IMPLEMENTATION - entry.reprocess(); - }).start(); - } - }); - this.add(reprocessItem); - } - - this.add(new JPopupMenu.Separator()); JMenuItem removeItem = new JMenuItem(new AbstractAction("Remove Item") { diff --git a/src/main/java/com/nccgroup/loggerplusplus/logview/entryviewer/RequestViewerController.java b/src/main/java/com/nccgroup/loggerplusplus/logview/entryviewer/RequestViewerController.java index ee198f62..e4d8fc7d 100644 --- a/src/main/java/com/nccgroup/loggerplusplus/logview/entryviewer/RequestViewerController.java +++ b/src/main/java/com/nccgroup/loggerplusplus/logview/entryviewer/RequestViewerController.java @@ -1,11 +1,11 @@ package com.nccgroup.loggerplusplus.logview.entryviewer; -import burp.IHttpRequestResponse; import burp.IHttpService; import burp.IMessageEditor; import burp.IMessageEditorController; import com.coreyd97.BurpExtenderUtilities.Preferences; import com.nccgroup.loggerplusplus.LoggerPlusPlus; +import com.nccgroup.loggerplusplus.logentry.LogEntry; public class RequestViewerController implements IMessageEditorController { @@ -14,7 +14,7 @@ public class RequestViewerController implements IMessageEditorController { private final IMessageEditor responseEditor; private final RequestViewerPanel requestViewerPanel; - private IHttpRequestResponse currentItem; + private LogEntry currentEntry; public RequestViewerController(Preferences preferences, boolean requestEditable, boolean responseEditable) { this.preferences = preferences; @@ -23,23 +23,18 @@ public RequestViewerController(Preferences preferences, boolean requestEditable, this.requestViewerPanel = new RequestViewerPanel(this); } - public IHttpRequestResponse getDisplayedEntity() { - return this.currentItem; - } - - public void setDisplayedEntity(IHttpRequestResponse requestResponse) { - if(requestResponse != null && requestResponse.equals(currentItem)) return; + public void setDisplayedEntity(LogEntry logEntry) { + this.currentEntry = logEntry; - this.currentItem = requestResponse; - if (requestResponse != null && requestResponse.getRequest() != null) { - requestEditor.setMessage(requestResponse.getRequest(), true); - }else{ + if (logEntry.getRequest() != null) { + requestEditor.setMessage(logEntry.getRequest(), true); + } else { requestEditor.setMessage(new byte[0], false); } - if (requestResponse != null && requestResponse.getResponse() != null) { - responseEditor.setMessage(requestResponse.getResponse(), false); - }else { + if (logEntry.getResponse() != null) { + responseEditor.setMessage(logEntry.getResponse(), false); + } else { responseEditor.setMessage(new byte[0], false); } } @@ -61,27 +56,26 @@ public RequestViewerPanel getRequestViewerPanel() { } @Override - public byte[] getRequest() - { - if(currentItem == null || currentItem.getRequest() == null) + public byte[] getRequest() { + if (currentEntry != null && currentEntry.getRequest() != null) { + return currentEntry.getRequest(); + } else { return new byte[0]; - else - return currentItem.getRequest(); + } } @Override - public byte[] getResponse() - { - if(currentItem == null || currentItem.getResponse() == null) + public byte[] getResponse() { + if (currentEntry != null && currentEntry.getResponse() != null) { + return currentEntry.getResponse(); + } else { return new byte[0]; - else - return currentItem.getResponse(); + } } @Override - public IHttpService getHttpService() - { - if(currentItem == null) return null; - return currentItem.getHttpService(); + public IHttpService getHttpService() { + if (currentEntry == null) return null; + return currentEntry.getHttpService(); } } diff --git a/src/main/java/com/nccgroup/loggerplusplus/logview/logtable/LogTable.java b/src/main/java/com/nccgroup/loggerplusplus/logview/logtable/LogTable.java index f43b38af..3e5a06b6 100644 --- a/src/main/java/com/nccgroup/loggerplusplus/logview/logtable/LogTable.java +++ b/src/main/java/com/nccgroup/loggerplusplus/logview/logtable/LogTable.java @@ -12,20 +12,21 @@ import com.nccgroup.loggerplusplus.logview.MultipleLogEntryMenu; import com.nccgroup.loggerplusplus.logview.SingleLogEntryMenu; import com.nccgroup.loggerplusplus.logview.entryviewer.RequestViewerController; -import com.nccgroup.loggerplusplus.util.userinterface.renderer.BooleanRenderer; import com.nccgroup.loggerplusplus.util.Globals; +import com.nccgroup.loggerplusplus.util.userinterface.renderer.BooleanRenderer; import javax.swing.*; import javax.swing.event.RowSorterEvent; import javax.swing.event.TableModelEvent; -import javax.swing.event.TableModelListener; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableRowSorter; import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.util.Collections; import java.util.List; -import java.util.*; +import java.util.Map; +import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -84,8 +85,8 @@ public class LogTable extends JTable }else { // Use a relative instead of an absolute index (This prevents an issue when a filter is set) LogEntry logEntry = getModel().getData().get(convertRowIndexToModel(selectedRow)); - if (logEntry.requestResponse != null) { - requestViewerController.setDisplayedEntity(logEntry.requestResponse); + if (logEntry != null) { + requestViewerController.setDisplayedEntity(logEntry); } } }); diff --git a/src/main/java/com/nccgroup/loggerplusplus/logview/logtable/LogTableModel.java b/src/main/java/com/nccgroup/loggerplusplus/logview/logtable/LogTableModel.java index 72e5af8a..a6c013ab 100644 --- a/src/main/java/com/nccgroup/loggerplusplus/logview/logtable/LogTableModel.java +++ b/src/main/java/com/nccgroup/loggerplusplus/logview/logtable/LogTableModel.java @@ -146,7 +146,7 @@ public void onColorFilterRemove(final ColorFilter filter) { @Override protected Void doInBackground() { for (int i = 0; i < entries.size(); i++) { - boolean wasPresent = entries.get(i).matchingColorFilters.remove(filter.getUUID()); + boolean wasPresent = entries.get(i).getMatchingColorFilters().remove(filter.getUUID()); if (wasPresent) { publish(i); } @@ -208,7 +208,7 @@ public void onTagRemove(final Tag filter) { @Override protected Void doInBackground() { for (int i = 0; i < entries.size(); i++) { - boolean wasPresent = entries.get(i).matchingTags.remove(filter); + boolean wasPresent = entries.get(i).getMatchingTags().remove(filter); if (wasPresent) { publish(i); } diff --git a/src/main/java/com/nccgroup/loggerplusplus/logview/processor/LogProcessor.java b/src/main/java/com/nccgroup/loggerplusplus/logview/processor/LogProcessor.java index 22bdec48..321679c1 100644 --- a/src/main/java/com/nccgroup/loggerplusplus/logview/processor/LogProcessor.java +++ b/src/main/java/com/nccgroup/loggerplusplus/logview/processor/LogProcessor.java @@ -327,18 +327,18 @@ public void run() { Map.Entry> abandonedEntry = iter.next(); if(abandonedEntry.getValue().isDone()){ LogEntry logEntry = abandonedEntry.getValue().get(); - if(logEntry.requestDateTime == null){ + if (logEntry.getRequestDateTime() == null) { //Should never be the case. //Entries should always have request times unless they are imported, //In which case they will never be awaiting a response so never in this list. continue; } - long entryTime = logEntry.requestDateTime.getTime(); + long entryTime = logEntry.getRequestDateTime().getTime(); long responseTimeout = 1000 * ((Integer) preferences.getSetting(PREF_RESPONSE_TIMEOUT)).longValue(); if (timeNow - entryTime > responseTimeout) { iter.remove(); - LogProcessorHelper.extractAndRemoveUUIDFromRequestResponseComment(instanceIdentifier, logEntry.requestResponse); - logEntry.requestResponse.setComment("Timed Out " + logEntry.requestResponse.getComment()); + LogEntry.extractAndRemoveUUIDFromComment(instanceIdentifier, logEntry); + logEntry.setComment("Timed Out " + logEntry.getComment()); removedUUIDs.add(abandonedEntry.getKey()); } }