From fbde050dd1304a492bb36c5549dcbdf3757ebc5a Mon Sep 17 00:00:00 2001 From: ViRIS Date: Fri, 24 Jan 2025 10:38:18 +0100 Subject: [PATCH] Fix for timestamps when importing from history! --- build.gradle | 3 ++- .../exports/ElasticExporter.java | 16 ++++++++++++++- .../loggerplusplus/logentry/LogEntry.java | 14 ++++++++++--- .../logview/processor/EntryImportWorker.java | 20 ++++++++++++++++++- 4 files changed, 47 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index af903933..669df9a4 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,8 @@ repositories { } dependencies { - implementation 'net.portswigger.burp.extensions:montoya-api:2023.10.4' + // implementation 'net.portswigger.burp.extensions:montoya-api:2023.10.4' + implementation 'net.portswigger.burp.extensions:montoya-api:2024.12' implementation 'org.swinglabs:swingx:1.6.1' implementation 'com.github.CoreyD97:Burp-Montoya-Utilities:54678c64' implementation 'co.elastic.clients:elasticsearch-java:8.8.2' diff --git a/src/main/java/com/nccgroup/loggerplusplus/exports/ElasticExporter.java b/src/main/java/com/nccgroup/loggerplusplus/exports/ElasticExporter.java index f2d71c45..3b10fae5 100644 --- a/src/main/java/com/nccgroup/loggerplusplus/exports/ElasticExporter.java +++ b/src/main/java/com/nccgroup/loggerplusplus/exports/ElasticExporter.java @@ -46,6 +46,8 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import java.text.SimpleDateFormat; + @Log4j2 public class ElasticExporter extends AutomaticLogExporter implements ExportPanelProvider, ContextMenuExportProvider { @@ -289,6 +291,7 @@ public EntrySerializer(Class t) { @Override public void serialize(LogEntry logEntry, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeStartObject(); + String stamp = null; for (LogEntryField field : ElasticExporter.this.fields) { Object value = logEntry.getValueByKey(field); if(value == null) continue; @@ -299,13 +302,24 @@ public void serialize(LogEntry logEntry, JsonGenerator gen, SerializerProvider p case "Double": gen.writeNumberField(field.getFullLabel(), (Double) value); break; case "String": gen.writeStringField(field.getFullLabel(), value.toString()); break; case "Boolean": gen.writeBooleanField(field.getFullLabel(), (Boolean) value); break; - case "Date": gen.writeNumberField(field.getFullLabel(), ((Date) value).getTime()); break; + case "Date": + String label = field.getFullLabel(); + if(label.equals("Request.Time")){ + SimpleDateFormat simpleDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); + simpleDate.setTimeZone(TimeZone.getTimeZone("UTC")); + stamp = simpleDate.format(((Date) value)); + } + gen.writeStringField(field.getFullLabel(), ((Date) value).toLocaleString()); + break; default: log.error("Unhandled field type: " + field.getType().getSimpleName()); } }catch (Exception e){ log.error("ElasticExporter: Couldn't serialize field. The field was ommitted from the export."); } } + if(stamp != null){ + gen.writeStringField("@timestamp", stamp); + } gen.writeEndObject(); } } diff --git a/src/main/java/com/nccgroup/loggerplusplus/logentry/LogEntry.java b/src/main/java/com/nccgroup/loggerplusplus/logentry/LogEntry.java index 92a619a9..8b2eed5f 100644 --- a/src/main/java/com/nccgroup/loggerplusplus/logentry/LogEntry.java +++ b/src/main/java/com/nccgroup/loggerplusplus/logentry/LogEntry.java @@ -99,8 +99,8 @@ public class LogEntry { 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 Date responseDateTime = null; // If null, gets pulled from the response + private Date requestDateTime = null; // If null, becomes epoch start time private int requestResponseDelay = -1; private List responseHeaders; private List requestHeaders; @@ -121,6 +121,14 @@ public LogEntry(ToolType tool, HttpRequest request) { public LogEntry(ToolType tool, HttpRequest request, HttpResponse response){ this(tool, request); this.response = response; + this.requestDateTime = new Date(0); + } + + public LogEntry(ToolType tool, HttpRequest request, HttpResponse response, Date requestTime, Date responseTime){ + this(tool, request); + this.response = response; + this.requestDateTime = requestTime; + this.responseDateTime = responseTime; } /** @@ -314,7 +322,7 @@ private Status processResponse() { this.newCookies = response.cookies().stream().map(cookie -> String.format("%s=%s", cookie.name(), cookie.value())).collect(Collectors.toList()); this.hasSetCookies = !newCookies.isEmpty(); - + // If response date isn't set, pull it from response headers if (this.responseDateTime == null) { // If it didn't have an arrival time set, parse the response for it. if (headers.get("date") != null && !StringUtils.isBlank(headers.get("date"))) { diff --git a/src/main/java/com/nccgroup/loggerplusplus/logview/processor/EntryImportWorker.java b/src/main/java/com/nccgroup/loggerplusplus/logview/processor/EntryImportWorker.java index c1f29bc4..a4ebc0be 100644 --- a/src/main/java/com/nccgroup/loggerplusplus/logview/processor/EntryImportWorker.java +++ b/src/main/java/com/nccgroup/loggerplusplus/logview/processor/EntryImportWorker.java @@ -13,6 +13,10 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.ThreadPoolExecutor; import java.util.function.Consumer; +import java.util.Date; +import java.time.ZonedDateTime; +import java.time.Instant; +import burp.api.montoya.http.handler.TimingData; public class EntryImportWorker extends SwingWorker { @@ -47,14 +51,28 @@ protected Void doInBackground() throws Exception { if(entryImportExecutor.isShutdown() || this.isCancelled()) return null; HttpRequest request; HttpResponse response; + Date requestTime = null; + Date responseTime = null; if(isProxyEntries){ request = proxyEntries.get(index).finalRequest(); response = proxyEntries.get(index).originalResponse(); + + TimingData timingData = proxyEntries.get(index).timingData(); + requestTime = Date.from(timingData.timeRequestSent().toInstant()); + responseTime = Date.from(timingData.timeRequestSent().plusNanos(timingData.timeBetweenRequestSentAndEndOfResponse().getNano()).toInstant()); }else{ request = httpEntries.get(index).request(); response = httpEntries.get(index).response(); + TimingData timingData = httpEntries.get(index).timingData().orElse(null); + if(timingData != null){ + requestTime = Date.from(timingData.timeRequestSent().toInstant()); + responseTime = Date.from(timingData.timeRequestSent().plusNanos(timingData.timeBetweenRequestSentAndEndOfResponse().getNano()).toInstant()); + }else{ + //Zero epoch date to prevent null. Response date pulled from response headers + requestTime = new Date(0); + } } - final LogEntry logEntry = new LogEntry(originatingTool, request, response); + final LogEntry logEntry = new LogEntry(originatingTool, request, response, requestTime, responseTime); int finalIndex = index; entryImportExecutor.submit(() -> { if(this.isCancelled()) return;