Created
January 27, 2026 08:57
-
-
Save mrrootsec/b7f1554bc1a13d4d57968469ee05811c to your computer and use it in GitHub Desktop.
Get rid of junk headers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| """ | |
| Burp Suite Extension (Jython / Python 2.7) | |
| Adds a Repeater request-editor context menu item that removes "unnecessary" | |
| browser/client-hint headers: | |
| - Sec-Fetch-* | |
| - Sec-CH-* (a.k.a. Sec-Ch-*) | |
| How it integrates with Burp: | |
| - Implements IBurpExtender to register with Burp | |
| - Implements IContextMenuFactory to add a right-click menu item | |
| - On click, reads the selected request, filters headers, rebuilds the message, | |
| and writes the modified request back into Repeater. | |
| """ | |
| from burp import IBurpExtender, IContextMenuFactory | |
| from javax.swing import JMenuItem | |
| import re | |
| class BurpExtender(IBurpExtender, IContextMenuFactory): | |
| # Compile regexes once (fast + accurate). Anchored to header name only. | |
| _UNWANTED_HEADER_NAME_REGEXES = [ | |
| re.compile(r"^Sec-Fetch-.*$", re.IGNORECASE), | |
| re.compile(r"^Sec-CH-.*$", re.IGNORECASE), # covers Sec-CH-* canonical form | |
| re.compile(r"^Sec-Ch-.*$", re.IGNORECASE), # covers mixed-case variants | |
| ] | |
| def registerExtenderCallbacks(self, callbacks): | |
| self._callbacks = callbacks | |
| self._helpers = callbacks.getHelpers() | |
| callbacks.setExtensionName("Repeater Header Cleanup (Sec-Fetch/Sec-CH)") | |
| callbacks.registerContextMenuFactory(self) | |
| self._callbacks.printOutput("[+] Repeater Header Cleanup loaded") | |
| return | |
| def createMenuItems(self, invocation): | |
| """ | |
| Called by Burp when building a right-click context menu. | |
| We only show the menu item for: | |
| - Request editor context (works in Repeater request editor) | |
| - When there's at least one selected message | |
| """ | |
| try: | |
| if invocation.getInvocationContext() != invocation.CONTEXT_MESSAGE_EDITOR_REQUEST: | |
| return [] | |
| messages = invocation.getSelectedMessages() | |
| if not messages or len(messages) == 0: | |
| return [] | |
| item = JMenuItem("Remove Sec-Fetch-* and Sec-CH-* headers") | |
| item.addActionListener(lambda event, msgs=messages: self._handle_click(msgs)) | |
| return [item] | |
| except Exception as e: | |
| self._callbacks.printError("Menu error: %s" % e) | |
| return [] | |
| def _handle_click(self, messages): | |
| """ | |
| Handle menu click. We apply to ALL selected messages (Burp may allow multi-select). | |
| """ | |
| for msg in messages: | |
| try: | |
| original = msg.getRequest() | |
| cleaned, removed = self._remove_unwanted_headers(original) | |
| msg.setRequest(cleaned) | |
| if removed: | |
| self._callbacks.printOutput("[+] Removed headers: %s" % ", ".join(removed)) | |
| else: | |
| self._callbacks.printOutput("[*] No matching headers found to remove") | |
| except Exception as e: | |
| self._callbacks.printError("Processing error: %s" % e) | |
| def _remove_unwanted_headers(self, request_bytes): | |
| """ | |
| Parse request -> filter headers -> rebuild request. | |
| Returns: | |
| (new_request_bytes, removed_header_names_list) | |
| """ | |
| req_info = self._helpers.analyzeRequest(request_bytes) | |
| headers = list(req_info.getHeaders()) | |
| body_offset = req_info.getBodyOffset() | |
| body = request_bytes[body_offset:] | |
| filtered_headers = [] | |
| removed_names = [] | |
| # Burp's headers list includes the request line as first entry: "GET / HTTP/1.1" | |
| # Keep it as-is, only evaluate actual header lines with ":". | |
| for h in headers: | |
| if ":" not in h: | |
| filtered_headers.append(h) | |
| continue | |
| name = h.split(":", 1)[0].strip() | |
| if self._is_unwanted_header_name(name): | |
| removed_names.append(name) | |
| continue | |
| filtered_headers.append(h) | |
| new_request = self._helpers.buildHttpMessage(filtered_headers, body) | |
| return new_request, removed_names | |
| def _is_unwanted_header_name(self, header_name): | |
| """ | |
| Regex detection for unwanted header names. Only checks the header NAME (not value), | |
| so we don't accidentally remove headers based on their content. | |
| """ | |
| for rx in self._UNWANTED_HEADER_NAME_REGEXES: | |
| if rx.match(header_name): | |
| return True | |
| return False |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment