├── README.md └── burp-payload-processor.py /README.md: -------------------------------------------------------------------------------- 1 | # Burp Luhn Payload Processor 2 | 3 | By: [Sandro Gauci](mailto:sandro@enablesecurity.com) 4 | 5 | This is a payload pre-processor module to add a check digit to numeric values 6 | passed to Burp's Intruder. 7 | 8 | For example, if you want to generate requests with 10000 to 99999 and ending 9 | with a check digit, this module will generate the following values for you: 10 | 11 | 100008 12 | 100016 13 | 100024 14 | ... 15 | 999995 16 | 17 | 18 | -------------------------------------------------------------------------------- /burp-payload-processor.py: -------------------------------------------------------------------------------- 1 | from burp import IBurpExtender 2 | from burp import IBurpExtenderCallbacks 3 | from burp import IIntruderPayloadProcessor 4 | from java.io import PrintWriter 5 | 6 | 7 | 8 | 9 | class BurpExtender(IBurpExtender, IIntruderPayloadProcessor): 10 | def registerExtenderCallbacks(self, callbacks): 11 | self._callbacks = callbacks 12 | self._helpers = callbacks.getHelpers() 13 | 14 | 15 | # Register methods for error reporting 16 | self.stdout = PrintWriter(callbacks.getStdout(), True) 17 | self.stderr = PrintWriter(callbacks.getStderr(), True) 18 | 19 | self.stdout.println("Module loaded successfully!") 20 | callbacks.setExtensionName('Add Luhn check digit') 21 | callbacks.registerIntruderPayloadProcessor(self) 22 | return 23 | 24 | def getProcessorName(self): 25 | return "Add Luhn check digit to the number" 26 | 27 | def processPayload(self,currentPayload, originalPayload, baseValue): 28 | payload = self._helpers.bytesToString(currentPayload) 29 | if not payload.isdigit(): 30 | print "You need to pass a digit" 31 | return currentPayload 32 | try: 33 | # Data will be outputted to Burp UI by default 34 | self.stdout.println("currentPayload: %s" % payload) 35 | payload = addluhn(payload) 36 | self.stdout.println("newPayload: %s" % payload) 37 | except: 38 | print "Unexpected error:", sys.exc_info()[0] 39 | newPayload = self._helpers.stringToBytes(payload) 40 | return newPayload 41 | 42 | def addluhn(data): 43 | checkdigit = generate(data) 44 | data += checkdigit 45 | return data 46 | 47 | 48 | 49 | decimal_decoder = lambda s: int(s, 10) 50 | decimal_encoder = lambda i: str(i) 51 | 52 | 53 | def luhn_sum_mod_base(string, base=10, decoder=decimal_decoder): 54 | # Adapted from http://en.wikipedia.org/wiki/Luhn_algorithm 55 | digits = list(map(decoder, string)) 56 | return ( 57 | sum(digits[::-2]) + 58 | sum(list(map(lambda d: sum(divmod(2 * d, base)), digits[-2::-2]))) 59 | ) % base 60 | 61 | 62 | def generate(string, base=10, encoder=decimal_encoder, 63 | decoder=decimal_decoder): 64 | """ 65 | Calculates the Luhn mod N check character for the given input string. This 66 | character should be appended to the input string to produce a valid Luhn 67 | mod N string in the given base. 68 | 69 | >>> value = '4205092350249' 70 | >>> generate(value) 71 | '1' 72 | 73 | When operating in a base other than decimal, encoder and decoder callables 74 | should be supplied. The encoder should take a single argument, an integer, 75 | and return the character corresponding to that integer in the operating 76 | base. Conversely, the decoder should take a string containing a single 77 | character and return its integer value in the operating base. Note that 78 | the mapping between values and characters defined by the encoder and 79 | decoder should be one-to-one. 80 | 81 | For example, when working in hexadecimal: 82 | 83 | >>> hex_alphabet = '0123456789abcdef' 84 | >>> hex_encoder = lambda i: hex_alphabet[i] 85 | >>> hex_decoder = lambda s: hex_alphabet.index(s) 86 | >>> value = 'a8b56f' 87 | >>> generate(value, base=16, encoder=hex_encoder, decoder=hex_decoder) 88 | 'b' 89 | >>> verify('a8b56fb', base=16, decoder=hex_decoder) 90 | True 91 | >>> verify('a8b56fc', base=16, decoder=hex_decoder) 92 | False 93 | 94 | """ 95 | 96 | d = luhn_sum_mod_base(string + encoder(0), base=base, decoder=decoder) 97 | if d != 0: 98 | d = base - d 99 | return encoder(d) 100 | 101 | 102 | def verify(string, base=10, decoder=decimal_decoder): 103 | """ 104 | Verifies that the given string is a valid Luhn mod N string. 105 | 106 | >>> verify('5105105105105100') # MasterCard test number 107 | True 108 | 109 | When operating in a base other than decimal, encoder and decoder callables 110 | should be supplied. The encoder should take a single argument, an integer, 111 | and return the character corresponding to that integer in the operating 112 | base. Conversely, the decoder should take a string containing a single 113 | character and return its integer value in the operating base. Note that 114 | the mapping between values and characters defined by the encoder and 115 | decoder should be one-to-one. 116 | 117 | For example, 'b' is the correct check character for the hexadecimal string 118 | 'a8b56f': 119 | 120 | >>> hex_decoder = lambda s: '0123456789abcdef'.index(s) 121 | >>> verify('a8b56fb', base=16, decoder=hex_decoder) 122 | True 123 | 124 | Any other check digit (in this example: 'c'), will result in a failed 125 | verification: 126 | 127 | >>> verify('a8b56fc', base=16, decoder=hex_decoder) 128 | False 129 | 130 | """ 131 | 132 | return luhn_sum_mod_base(string, base=base, decoder=decoder) == 0 133 | --------------------------------------------------------------------------------