├── README.md ├── java ├── .gitignore ├── BurpExtender.java ├── build.gradle └── settings.gradle ├── python └── IntruderPayloads.py ├── ruby └── IntruderPayloads.rb └── server ├── Demo.aspx └── server.js /README.md: -------------------------------------------------------------------------------- 1 | # Sample Burp Suite extension: Intruder payloads 2 | 3 | This example shows how you can use an extension to: 4 | - Generate custom Intruder payloads 5 | - Apply custom processing to Intruder payloads (including built-in ones) 6 | 7 | When an extension registers itself as an Intruder payload provider, this will 8 | be available within the Intruder UI for the user to select as the payload 9 | source for an attack. When an extension registers itself as a payload 10 | processor, the user can create a payload processing rule and select the 11 | extension's processor as the rule's action. 12 | 13 | When Burp calls out to a payload provider to generate a payload, it passes the 14 | base value of the payload position as a parameter. This allows you to create 15 | attacks in which a whole block of serialized data is marked as the payload 16 | position, and your extension places payloads into suitable locations within 17 | that data, and re-serializes the data to create a valid request. Hence, you can 18 | use Intruder's powerful attack engine to automatically manipulate input deep 19 | within complex data structures. 20 | 21 | This example is artificially simple, and generates two payloads: one to identify 22 | basic XSS, and one to trigger the ficititious vulnerability that was used in the 23 | [custom scanner checks 24 | example](//github.com/PortSwigger/example-scanner-checks). It then uses a custom 25 | payload processor to reconstruct the serialized data structure around the custom 26 | payload. 27 | 28 | This repository includes source code for Java, Python and Ruby. It also includes 29 | a server (for ASP.NET and NodeJS) that extends the [serialization 30 | example](//github.com/PortSwigger/example-custom-editor-tab) to add some 31 | fictitious bugs so that you can test the custom payloads, and see that the two 32 | vulnerabilities are triggered. 33 | 34 | After loading the extension, you'll need to: 35 | - Select "Extension-generated" payloads as your Intruder payloads type. 36 | - Add a payload processing rule choosing the "Invoke Burp extension" processor. 37 | - Start an attack against a POST sent to the included webserver. 38 | 39 | Note: the sample server uses the JavaScript btoa() function to perform 40 | Base64-encoding on the client side. This function is not supported by Internet 41 | Explorer, but works on most other browsers. 42 | -------------------------------------------------------------------------------- /java/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle/ 2 | build/ 3 | -------------------------------------------------------------------------------- /java/BurpExtender.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | public class BurpExtender implements IBurpExtender, IIntruderPayloadGeneratorFactory, IIntruderPayloadProcessor 4 | { 5 | private IExtensionHelpers helpers; 6 | 7 | // hard-coded payloads 8 | // [in reality, you would use an extension for something cleverer than this] 9 | private static final byte[][] PAYLOADS = 10 | { 11 | "|".getBytes(), 12 | "".getBytes(), 13 | }; 14 | 15 | // 16 | // implement IBurpExtender 17 | // 18 | 19 | @Override 20 | public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks) 21 | { 22 | // obtain an extension helpers object 23 | helpers = callbacks.getHelpers(); 24 | 25 | // set our extension name 26 | callbacks.setExtensionName("Custom intruder payloads"); 27 | 28 | // register ourselves as an Intruder payload generator 29 | callbacks.registerIntruderPayloadGeneratorFactory(this); 30 | 31 | // register ourselves as an Intruder payload processor 32 | callbacks.registerIntruderPayloadProcessor(this); 33 | } 34 | 35 | // 36 | // implement IIntruderPayloadGeneratorFactory 37 | // 38 | 39 | @Override 40 | public String getGeneratorName() 41 | { 42 | return "My custom payloads"; 43 | } 44 | 45 | @Override 46 | public IIntruderPayloadGenerator createNewInstance(IIntruderAttack attack) 47 | { 48 | // return a new IIntruderPayloadGenerator to generate payloads for this attack 49 | return new IntruderPayloadGenerator(); 50 | } 51 | 52 | // 53 | // implement IIntruderPayloadProcessor 54 | // 55 | 56 | @Override 57 | public String getProcessorName() 58 | { 59 | return "Serialized input wrapper"; 60 | } 61 | 62 | @Override 63 | public byte[] processPayload(byte[] currentPayload, byte[] originalPayload, byte[] baseValue) 64 | { 65 | // decode the base value 66 | String dataParameter = helpers.bytesToString(helpers.base64Decode(helpers.urlDecode(baseValue))); 67 | 68 | // parse the location of the input string in the decoded data 69 | int start = dataParameter.indexOf("input=") + 6; 70 | if (start == -1) 71 | return currentPayload; 72 | String prefix = dataParameter.substring(0, start); 73 | int end = dataParameter.indexOf("&", start); 74 | if (end == -1) 75 | end = dataParameter.length(); 76 | String suffix = dataParameter.substring(end, dataParameter.length()); 77 | 78 | // rebuild the serialized data with the new payload 79 | dataParameter = prefix + helpers.bytesToString(currentPayload) + suffix; 80 | return helpers.stringToBytes(helpers.urlEncode(helpers.base64Encode(dataParameter))); 81 | } 82 | 83 | // 84 | // class to generate payloads from a simple list 85 | // 86 | 87 | class IntruderPayloadGenerator implements IIntruderPayloadGenerator 88 | { 89 | int payloadIndex; 90 | 91 | @Override 92 | public boolean hasMorePayloads() 93 | { 94 | return payloadIndex < PAYLOADS.length; 95 | } 96 | 97 | @Override 98 | public byte[] getNextPayload(byte[] baseValue) 99 | { 100 | byte[] payload = PAYLOADS[payloadIndex]; 101 | payloadIndex++; 102 | return payload; 103 | } 104 | 105 | @Override 106 | public void reset() 107 | { 108 | payloadIndex = 0; 109 | } 110 | } 111 | } -------------------------------------------------------------------------------- /java/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | repositories { 4 | mavenCentral() 5 | } 6 | 7 | dependencies { 8 | compile 'net.portswigger.burp.extender:burp-extender-api:1.7.13' 9 | } 10 | 11 | sourceSets { 12 | main { 13 | java { 14 | srcDir '.' 15 | } 16 | } 17 | } 18 | 19 | task fatJar(type: Jar) { 20 | baseName = project.name + '-all' 21 | from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } 22 | with jar 23 | } 24 | -------------------------------------------------------------------------------- /java/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'IntruderPayloads' 2 | -------------------------------------------------------------------------------- /python/IntruderPayloads.py: -------------------------------------------------------------------------------- 1 | from burp import IBurpExtender 2 | from burp import IIntruderPayloadGeneratorFactory 3 | from burp import IIntruderPayloadProcessor 4 | from burp import IIntruderPayloadGenerator 5 | 6 | # hard-coded payloads 7 | # [in reality, you would use an extension for something cleverer than this] 8 | 9 | PAYLOADS = [ 10 | bytearray("|"), 11 | bytearray("") 12 | ] 13 | 14 | class BurpExtender(IBurpExtender, IIntruderPayloadGeneratorFactory, IIntruderPayloadProcessor): 15 | 16 | # 17 | # implement IBurpExtender 18 | # 19 | 20 | def registerExtenderCallbacks(self, callbacks): 21 | # obtain an extension helpers object 22 | self._helpers = callbacks.getHelpers() 23 | 24 | # set our extension name 25 | callbacks.setExtensionName("Custom intruder payloads") 26 | 27 | # register ourselves as an Intruder payload generator 28 | callbacks.registerIntruderPayloadGeneratorFactory(self) 29 | 30 | # register ourselves as an Intruder payload processor 31 | callbacks.registerIntruderPayloadProcessor(self) 32 | 33 | # 34 | # implement IIntruderPayloadGeneratorFactory 35 | # 36 | 37 | def getGeneratorName(self): 38 | return "My custom payloads" 39 | 40 | def createNewInstance(self, attack): 41 | # return a new IIntruderPayloadGenerator to generate payloads for this attack 42 | return IntruderPayloadGenerator() 43 | 44 | # 45 | # implement IIntruderPayloadProcessor 46 | # 47 | 48 | def getProcessorName(self): 49 | return "Serialized input wrapper" 50 | 51 | def processPayload(self, currentPayload, originalPayload, baseValue): 52 | # decode the base value 53 | dataParameter = self._helpers.bytesToString( 54 | self._helpers.base64Decode(self._helpers.urlDecode(baseValue))) 55 | 56 | # parse the location of the input string in the decoded data 57 | start = dataParameter.index("input=") + 6 58 | if start == -1: 59 | return currentPayload 60 | 61 | prefix = dataParameter[0:start] 62 | end = dataParameter.index("&", start) 63 | if end == -1: 64 | end = len(dataParameter) 65 | 66 | suffix = dataParameter[end:len(dataParameter)] 67 | 68 | # rebuild the serialized data with the new payload 69 | dataParameter = prefix + self._helpers.bytesToString(currentPayload) + suffix 70 | return self._helpers.stringToBytes( 71 | self._helpers.urlEncode(self._helpers.base64Encode(dataParameter))) 72 | 73 | # 74 | # class to generate payloads from a simple list 75 | # 76 | 77 | class IntruderPayloadGenerator(IIntruderPayloadGenerator): 78 | def __init__(self): 79 | self._payloadIndex = 0 80 | 81 | def hasMorePayloads(self): 82 | return self._payloadIndex < len(PAYLOADS) 83 | 84 | def getNextPayload(self, baseValue): 85 | payload = PAYLOADS[self._payloadIndex] 86 | self._payloadIndex = self._payloadIndex + 1 87 | 88 | return payload 89 | 90 | def reset(self): 91 | self._payloadIndex = 0 92 | -------------------------------------------------------------------------------- /ruby/IntruderPayloads.rb: -------------------------------------------------------------------------------- 1 | java_import 'burp.IBurpExtender' 2 | java_import 'burp.IIntruderPayloadGeneratorFactory' 3 | java_import 'burp.IIntruderPayloadProcessor' 4 | java_import 'burp.IIntruderPayloadGenerator' 5 | 6 | # hard-coded payloads 7 | # [in reality, you would use an extension for something cleverer than this] 8 | 9 | PAYLOADS = [ 10 | "|".bytes.to_a, 11 | "".bytes.to_a 12 | ] 13 | 14 | class BurpExtender 15 | include IBurpExtender, IIntruderPayloadGeneratorFactory, IIntruderPayloadProcessor 16 | 17 | # 18 | # implement IBurpExtender 19 | # 20 | 21 | def registerExtenderCallbacks(callbacks) 22 | # obtain an extension helpers object 23 | @helpers = callbacks.getHelpers 24 | 25 | # set our extension name 26 | callbacks.setExtensionName "Custom intruder payloads" 27 | 28 | # register ourselves as an Intruder payload generator 29 | callbacks.registerIntruderPayloadGeneratorFactory self 30 | 31 | # register ourselves as an Intruder payload processor 32 | callbacks.registerIntruderPayloadProcessor self 33 | end 34 | 35 | # 36 | # implement IIntruderPayloadGeneratorFactory 37 | # 38 | 39 | def getGeneratorName() 40 | "My custom payloads" 41 | end 42 | 43 | def createNewInstance(attack) 44 | # return a new IIntruderPayloadGenerator to generate payloads for this attack 45 | IntruderPayloadGenerator.new 46 | end 47 | 48 | # 49 | # implement IIntruderPayloadProcessor 50 | # 51 | 52 | def getProcessorName() 53 | "Serialized input wrapper" 54 | end 55 | 56 | def processPayload(currentPayload, originalPayload, baseValue) 57 | # decode the base value 58 | dataParameter = @helpers.bytesToString( 59 | @helpers.base64Decode(@helpers.urlDecode baseValue)) 60 | 61 | # parse the location of the input string in the decoded data 62 | start = dataParameter.index("input=") + 6 63 | return currentPayload if start == -1 64 | 65 | prefix = dataParameter[0...start] 66 | end_ = dataParameter.index("&", start) 67 | end_ = dataParameter.length if end_ == -1 68 | 69 | suffix = dataParameter[end_..-1] 70 | 71 | # rebuild the serialized data with the new payload 72 | dataParameter = prefix + @helpers.bytesToString(currentPayload) + suffix 73 | return @helpers.stringToBytes( 74 | @helpers.urlEncode(@helpers.base64Encode dataParameter)) 75 | end 76 | end 77 | 78 | # 79 | # class to generate payloads from a simple list 80 | # 81 | 82 | class IntruderPayloadGenerator 83 | include IIntruderPayloadGenerator 84 | 85 | def initialize() 86 | @payloadIndex = 0 87 | end 88 | 89 | def hasMorePayloads() 90 | @payloadIndex < PAYLOADS.length 91 | end 92 | 93 | def getNextPayload(baseValue) 94 | payload = PAYLOADS[@payloadIndex] 95 | @payloadIndex = @payloadIndex + 1 96 | 97 | return payload 98 | end 99 | 100 | def reset() 101 | @payloadIndex = 0 102 | end 103 | end 104 | -------------------------------------------------------------------------------- /server/Demo.aspx: -------------------------------------------------------------------------------- 1 | <%@ Page Language="C#" %> 2 | 3 | <% 4 | string data = Request.Form["data"]; 5 | if (data != null) 6 | { 7 | try 8 | { 9 | data = Encoding.ASCII.GetString(Convert.FromBase64String(data)); 10 | data = HttpUtility.ParseQueryString(data)["input"]; 11 | 12 | // add a fictitious input vulnerability 13 | if (data.Contains("|")) 14 | throw new Exception("Unexpected pipe"); 15 | 16 | output.InnerHtml = "Input received: " + data; 17 | } 18 | catch (Exception ex) 19 | { 20 | output.InnerHtml = ex.ToString(); 21 | } 22 | } 23 | %> 24 | 25 | 26 | 27 |
28 |