|  | 
|  | 1 | +package burp; | 
|  | 2 | + | 
|  | 3 | +import java.io.PrintWriter; | 
|  | 4 | +import java.net.URL; | 
|  | 5 | + | 
|  | 6 | +public class Extractor implements IHttpListener { | 
|  | 7 | +    private ExtractorMainTab extractorMainTab; | 
|  | 8 | +    private IExtensionHelpers helpers; | 
|  | 9 | +    private Logger logger; | 
|  | 10 | + | 
|  | 11 | +    public Extractor(ExtractorMainTab extractorMainTab, IBurpExtenderCallbacks callbacks) { | 
|  | 12 | +        this.extractorMainTab = extractorMainTab; | 
|  | 13 | +        this.helpers = callbacks.getHelpers(); | 
|  | 14 | + | 
|  | 15 | +        this.logger = new Logger(new PrintWriter(callbacks.getStdout(), true)); | 
|  | 16 | +        // Logger.setLogLevel(Logger.INFO); | 
|  | 17 | +    } | 
|  | 18 | + | 
|  | 19 | +    @Override | 
|  | 20 | +    public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) { | 
|  | 21 | +        if (messageIsRequest) { | 
|  | 22 | +            logger.debug("Processing request..."); | 
|  | 23 | +            byte[] requestBytes = messageInfo.getRequest(); | 
|  | 24 | +            String request = this.helpers.bytesToString(requestBytes); | 
|  | 25 | + | 
|  | 26 | +            // Loop over each tab to perform whatever replacement is necessary | 
|  | 27 | +            String extractedData; | 
|  | 28 | +            boolean edited = false; | 
|  | 29 | +            for (ExtractorTab extractorTab : this.extractorMainTab.getExtractorTabs()) { | 
|  | 30 | + | 
|  | 31 | +                // Determine if this message is in scope, and the user wants requests edited at this time | 
|  | 32 | +                URL url = this.helpers.analyzeRequest(messageInfo.getHttpService(), requestBytes).getUrl(); | 
|  | 33 | +                if (extractorTab.requestIsInScope(url, | 
|  | 34 | +                        messageInfo.getHttpService().getHost(), | 
|  | 35 | +                        toolFlag) && | 
|  | 36 | +                        extractorTab.shouldModifyRequests()) { | 
|  | 37 | +                    logger.debug("Request is in scope and Extractor tab is active."); | 
|  | 38 | + | 
|  | 39 | +                    // Check if we have the necessary components to do replacement | 
|  | 40 | +                    String[] requestSelectionRegex = extractorTab.getRequestSelectionRegex(); | 
|  | 41 | +                    extractedData = extractorTab.getDataToInsert(); | 
|  | 42 | +                    if (!extractedData.equals("") | 
|  | 43 | +                            && !requestSelectionRegex[0].equals("") | 
|  | 44 | +                            && !requestSelectionRegex[1].equals("")) { | 
|  | 45 | +                        logger.debug("Attempting replacement..."); | 
|  | 46 | +                        int[] selectionBounds = Utils.getSelectionBounds(request, requestSelectionRegex[0], requestSelectionRegex[1]); | 
|  | 47 | +                        if (selectionBounds != null) { | 
|  | 48 | +                            logger.info("Replacing request after regex \"" + requestSelectionRegex[0] + "\" with \"" + extractedData + "\""); | 
|  | 49 | +                            int[] clHeaderBounds = Utils.getSelectionBounds(request, "(?i)\\r\\nContent-Length: ", "\\r\\n"); | 
|  | 50 | +                            int[] headersEndBounds = Utils.getSelectionBounds(request, "\\r\\n\\r\\n", ""); | 
|  | 51 | +                            // The following rewrite of the Content-Length | 
|  | 52 | +                            // header aims at maintaining the integrity between | 
|  | 53 | +                            // the header's claim and the rewritten content's | 
|  | 54 | +                            // length. The Content-Length rewrite can still be | 
|  | 55 | +                            // insufficient.  For example, the rewrite will not | 
|  | 56 | +                            // fix the MIME parts of a request body that carry | 
|  | 57 | +                            // own content length headers.  The Content-Length | 
|  | 58 | +                            // rewrite will not fix the claimed length of a | 
|  | 59 | +                            // chunk in a a chunked Transfer-Encoding. | 
|  | 60 | +                            String dangerousContentLengthRewrite = null; | 
|  | 61 | +                            if ((clHeaderBounds != null) && (headersEndBounds != null) && | 
|  | 62 | +                                    (clHeaderBounds[0] < headersEndBounds[0]) && (headersEndBounds[0] < selectionBounds[0])) { | 
|  | 63 | +                                int origContentLength = Integer.parseInt(request.substring(clHeaderBounds[0], | 
|  | 64 | +                                            clHeaderBounds[1])); | 
|  | 65 | +                                int replacedLength = selectionBounds[1] - selectionBounds[0]; | 
|  | 66 | +                                int replacedContentLength = origContentLength - replacedLength + extractedData.length(); | 
|  | 67 | +                                if (origContentLength != replacedContentLength) { | 
|  | 68 | +                                    logger.info("Updating Content-Length: " + origContentLength + " with " + replacedContentLength); | 
|  | 69 | +                                    dangerousContentLengthRewrite = request.substring(0, clHeaderBounds[0]) +  | 
|  | 70 | +                                        Integer.toString(replacedContentLength) + | 
|  | 71 | +                                        request.substring(clHeaderBounds[1], selectionBounds[0]); | 
|  | 72 | +                                } | 
|  | 73 | +                            } | 
|  | 74 | +                            String contentBeforeRewrite; | 
|  | 75 | +                            if (dangerousContentLengthRewrite == null) { | 
|  | 76 | +                                contentBeforeRewrite = request.substring(0, selectionBounds[0]); | 
|  | 77 | +                            } else { | 
|  | 78 | +                                contentBeforeRewrite = dangerousContentLengthRewrite; | 
|  | 79 | +                            } | 
|  | 80 | +                            request = contentBeforeRewrite | 
|  | 81 | +                                    + extractedData | 
|  | 82 | +                                    + request.substring(selectionBounds[1], request.length()); | 
|  | 83 | +                            edited = true; | 
|  | 84 | +                            logger.debug("Finished replacement"); | 
|  | 85 | +                        } | 
|  | 86 | +                    } | 
|  | 87 | +                } | 
|  | 88 | +            } | 
|  | 89 | +            if (edited) { | 
|  | 90 | +                messageInfo.setRequest(this.helpers.stringToBytes(request)); | 
|  | 91 | +            } | 
|  | 92 | +        } else if (!messageIsRequest) { | 
|  | 93 | + | 
|  | 94 | +            logger.debug("Processing response..."); | 
|  | 95 | +            byte[] responseBytes = messageInfo.getResponse(); | 
|  | 96 | +            String response = this.helpers.bytesToString(responseBytes); | 
|  | 97 | + | 
|  | 98 | +            // Loop over each tab, and grab whatever data item is necessary | 
|  | 99 | +            for (ExtractorTab extractorTab : this.extractorMainTab.getExtractorTabs()) { | 
|  | 100 | + | 
|  | 101 | +                // Check if message is in scope | 
|  | 102 | +                IHttpService service = messageInfo.getHttpService(); | 
|  | 103 | +                URL url; | 
|  | 104 | +                try { | 
|  | 105 | +                    url = new URL(service.getProtocol(), service.getHost(), service.getPort(), ""); | 
|  | 106 | +                } catch(java.net.MalformedURLException e) { | 
|  | 107 | +                    throw new RuntimeException(e); | 
|  | 108 | +                } | 
|  | 109 | +                if (extractorTab.responseIsInScope(url, | 
|  | 110 | +                        service.getHost(), | 
|  | 111 | +                        toolFlag)) { | 
|  | 112 | +                    logger.debug("Response is in scope."); | 
|  | 113 | + | 
|  | 114 | +                    String[] responseSelectionRegex = extractorTab.getResponseSelectionRegex(); | 
|  | 115 | + | 
|  | 116 | +                    // Grab text from response | 
|  | 117 | +                    if (responseSelectionRegex[0] != "" && responseSelectionRegex[1] != "") { | 
|  | 118 | +                        int[] selectionBounds = Utils.getSelectionBounds(response, responseSelectionRegex[0], responseSelectionRegex[1]); | 
|  | 119 | +                        if (selectionBounds != null) { | 
|  | 120 | +                            logger.info("Found a match in the response after regex \"" + responseSelectionRegex[0] + "\": \"" + | 
|  | 121 | +                                    response.substring(selectionBounds[0], selectionBounds[1]) + "\""); | 
|  | 122 | +                            extractorTab.setDataToInsert(response.substring(selectionBounds[0], selectionBounds[1])); | 
|  | 123 | +                        } | 
|  | 124 | +                    } else { | 
|  | 125 | +                        logger.debug("Before and after regex not defined"); | 
|  | 126 | +                    } | 
|  | 127 | +                } | 
|  | 128 | +            } | 
|  | 129 | +        } | 
|  | 130 | +    } | 
|  | 131 | +} | 
0 commit comments