Projects STRLCPY js-link-finder Commits 55768a17
🤬
  • ■ ■ ■ ■ ■ ■
    BurpJSLinkFinderTEST.py
    1  -#
    2  -# BurpLinkFinder - Find links within JS files.
    3  -#
    4  -# Copyright (c) 2019 Frans Hendrik Botes
    5  -# Credit to https://github.com/GerbenJavado/LinkFinder for the idea and regex
    6  -#
    7  -from burp import IBurpExtender, IScannerCheck, IScanIssue, ITab
    8  -from java.io import PrintWriter
    9  -from java.net import URL
    10  -from java.util import ArrayList, List
    11  -from java.util.regex import Matcher, Pattern
    12  -import binascii
    13  -import base64
    14  -import re
    15  -from javax import swing
    16  -from java.awt import Font, Color
    17  -from threading import Thread
    18  -from array import array
    19  -from java.awt import EventQueue
    20  -from java.lang import Runnable
    21  -from multiprocessing.dummy import Pool as ThreadPool
    22  - 
    23  -# Using the Runnable class for thread-safety with Swing
    24  -class Run(Runnable):
    25  - def __init__(self, runner):
    26  - self.runner = runner
    27  - 
    28  - def run(self):
    29  - self.runner()
    30  - 
    31  - 
    32  -class BurpExtender(IBurpExtender, IScannerCheck, ITab):
    33  - def registerExtenderCallbacks(self, callbacks):
    34  - self.callbacks = callbacks
    35  - self.helpers = callbacks.getHelpers()
    36  - callbacks.setExtensionName("BurpJSLinkFinder")
    37  - 
    38  - callbacks.issueAlert("BurpJSLinkFinder Passive Scanner enabled")
    39  - 
    40  - stdout = PrintWriter(callbacks.getStdout(), True)
    41  - stderr = PrintWriter(callbacks.getStderr(), True)
    42  - callbacks.registerScannerCheck(self)
    43  - self.initUI()
    44  - self.callbacks.addSuiteTab(self)
    45  -
    46  - print ("Burp JS LinkFinder loaded.")
    47  - print ("Copyright (c) 2019 Frans Hendrik Botes")
    48  - self.outputTxtArea.setText("Burp JS LinkFinder loaded." + "\n" + "Copyright (c) 2019 Frans Hendrik Botes" + "\n")
    49  - 
    50  - def initUI(self):
    51  - self.tab = swing.JPanel()
    52  - 
    53  - # UI for Output
    54  - self.outputLabel = swing.JLabel("LinkFinder Log:")
    55  - self.outputLabel.setFont(Font("Tahoma", Font.BOLD, 14))
    56  - self.outputLabel.setForeground(Color(255,102,52))
    57  - self.logPane = swing.JScrollPane()
    58  - self.outputTxtArea = swing.JTextArea()
    59  - self.outputTxtArea.setFont(Font("Consolas", Font.PLAIN, 12))
    60  - self.outputTxtArea.setLineWrap(True)
    61  - self.logPane.setViewportView(self.outputTxtArea)
    62  - self.clearBtn = swing.JButton("Clear Log", actionPerformed=self.clearLog)
    63  - 
    64  - # Layout
    65  - layout = swing.GroupLayout(self.tab)
    66  - layout.setAutoCreateGaps(True)
    67  - layout.setAutoCreateContainerGaps(True)
    68  - self.tab.setLayout(layout)
    69  -
    70  - layout.setHorizontalGroup(
    71  - layout.createParallelGroup()
    72  - .addGroup(layout.createSequentialGroup()
    73  - .addGroup(layout.createParallelGroup()
    74  - .addComponent(self.outputLabel)
    75  - .addComponent(self.logPane)
    76  - .addComponent(self.clearBtn)
    77  - )
    78  - )
    79  - )
    80  -
    81  - layout.setVerticalGroup(
    82  - layout.createParallelGroup()
    83  - .addGroup(layout.createParallelGroup()
    84  - .addGroup(layout.createSequentialGroup()
    85  - .addComponent(self.outputLabel)
    86  - .addComponent(self.logPane)
    87  - .addComponent(self.clearBtn)
    88  - )
    89  - )
    90  - )
    91  - 
    92  - def getTabCaption(self):
    93  - return "BurpJSLinkFinder"
    94  - 
    95  - def getUiComponent(self):
    96  - return self.tab
    97  - 
    98  - def clearLog(self, event):
    99  - self.outputTxtArea.setText("Burp JS LinkFinder loaded." + "\n" + "Copyright (c) 2019 Frans Hendrik Botes" + "\n" )
    100  - 
    101  - def doPassiveScan(self, ihrr):
    102  - try:
    103  - url = ihrr.getUrl()
    104  - linkA = linkAnalyse(ihrr,self.helpers)
    105  - # make the Pool of workers
    106  - pool = ThreadPool(4)
    107  - if ".js" in str(url):
    108  - self.outputTxtArea.append("\n" + "[+] Valid URL found: " + str(url))
    109  - issueText = pool.map(linkA.analyseURL())
    110  - for counter, issueText in enumerate(issueText):
    111  - #print("TEST Value returned SUCCESS")
    112  - self.outputTxtArea.append("\n" + "\t" + str(counter)+' - ' +issueText['link'])
    113  - 
    114  - issues = ArrayList()
    115  - issues.add(SRI(ihrr, self.helpers))
    116  - pool.close()
    117  - pool.join()
    118  - return issues
    119  - except UnicodeEncodeError:
    120  - print ("Error in URL decode.")
    121  - return None
    122  - 
    123  - 
    124  - def consolidateDuplicateIssues(self, isb, isa):
    125  - return -1
    126  - 
    127  - def extensionUnloaded(self):
    128  - print "Burp JS LinkFinder unloaded"
    129  - return
    130  - 
    131  -class linkAnalyse():
    132  -
    133  - def __init__(self, reqres, helpers):
    134  - self.helpers = helpers
    135  - self.reqres = reqres
    136  -
    137  - 
    138  - regex_str = """
    139  -
    140  - (?:"|') # Start newline delimiter
    141  -
    142  - (
    143  - ((?:[a-zA-Z]{1,10}://|//) # Match a scheme [a-Z]*1-10 or //
    144  - [^"'/]{1,}\. # Match a domainname (any character + dot)
    145  - [a-zA-Z]{2,}[^"']{0,}) # The domainextension and/or path
    146  -
    147  - |
    148  -
    149  - ((?:/|\.\./|\./) # Start with /,../,./
    150  - [^"'><,;| *()(%%$^/\\\[\]] # Next character can't be...
    151  - [^"'><,;|()]{1,}) # Rest of the characters can't be
    152  -
    153  - |
    154  -
    155  - ([a-zA-Z0-9_\-/]{1,}/ # Relative endpoint with /
    156  - [a-zA-Z0-9_\-/]{1,} # Resource name
    157  - \.(?:[a-zA-Z]{1,4}|action) # Rest + extension (length 1-4 or action)
    158  - (?:[\?|/][^"|']{0,}|)) # ? mark with parameters
    159  -
    160  - |
    161  -
    162  - ([a-zA-Z0-9_\-]{1,} # filename
    163  - \.(?:php|asp|aspx|jsp|json|
    164  - action|html|js|txt|xml) # . + extension
    165  - (?:\?[^"|']{0,}|)) # ? mark with parameters
    166  -
    167  - )
    168  -
    169  - (?:"|') # End newline delimiter
    170  -
    171  - """
    172  - 
    173  - def parser_file(self, content, regex_str, mode=1, more_regex=None, no_dup=1):
    174  - #print ("TEST parselfile #2")
    175  - regex = re.compile(regex_str, re.VERBOSE)
    176  - items = [{"link": m.group(1)} for m in re.finditer(regex, content)]
    177  - if no_dup:
    178  - # Remove duplication
    179  - all_links = set()
    180  - no_dup_items = []
    181  - for item in items:
    182  - if item["link"] not in all_links:
    183  - all_links.add(item["link"])
    184  - no_dup_items.append(item)
    185  - items = no_dup_items
    186  -
    187  - # Match Regex
    188  - filtered_items = []
    189  - for item in items:
    190  - # Remove other capture groups from regex results
    191  - if more_regex:
    192  - if re.search(more_regex, item["link"]):
    193  - #print ("TEST parselfile #3")
    194  - filtered_items.append(item)
    195  - else:
    196  - filtered_items.append(item)
    197  - return filtered_items
    198  - 
    199  - def threadAnalysis(self):
    200  - thread = Thread(target=self.analyseURL(), args=(session,))
    201  - thread.daemon = True
    202  - thread.start()
    203  - 
    204  - def analyseURL(self):
    205  -
    206  - endpoints = ""
    207  - #print("TEST AnalyseURL #1")
    208  - mime_type=self.helpers.analyzeResponse(self.reqres.getResponse()).getStatedMimeType()
    209  - if mime_type.lower() == 'script':
    210  - url = self.reqres.getUrl()
    211  - encoded_resp=binascii.b2a_base64(self.reqres.getResponse())
    212  - decoded_resp=base64.b64decode(encoded_resp)
    213  - endpoints=self.parser_file(decoded_resp, self.regex_str)
    214  - #print("TEST AnalyseURL #2")
    215  - return endpoints
    216  - return endpoints
    217  - 
    218  - 
    219  -class SRI(IScanIssue,ITab):
    220  - def __init__(self, reqres, helpers):
    221  - self.helpers = helpers
    222  - self.reqres = reqres
    223  - 
    224  - def getHost(self):
    225  - return self.reqres.getHost()
    226  - 
    227  - def getPort(self):
    228  - return self.reqres.getPort()
    229  - 
    230  - def getProtocol(self):
    231  - return self.reqres.getProtocol()
    232  - 
    233  - def getUrl(self):
    234  - return self.reqres.getUrl()
    235  - 
    236  - def getIssueName(self):
    237  - return "Linkfinder Analysed JS files"
    238  - 
    239  - def getIssueType(self):
    240  - return 0x08000000 # See http:#portswigger.net/burp/help/scanner_issuetypes.html
    241  - 
    242  - def getSeverity(self):
    243  - return "Information" # "High", "Medium", "Low", "Information" or "False positive"
    244  - 
    245  - def getConfidence(self):
    246  - return "Certain" # "Certain", "Firm" or "Tentative"
    247  - 
    248  - def getIssueBackground(self):
    249  - return str("JS files holds links to other parts of web applications. Refer to TAB for results.")
    250  - 
    251  - def getRemediationBackground(self):
    252  - return "This is an <b>informational</b> finding only.<br>"
    253  - 
    254  - def getIssueDetail(self):
    255  - return str("Burp Scanner has analysed the following JS file for links: <b>"
    256  - "%s</b><br><br>" % (self.reqres.getUrl().toString()))
    257  - 
    258  - def getRemediationDetail(self):
    259  - return None
    260  - 
    261  - def getHttpMessages(self):
    262  - #print ("................raising issue................")
    263  - rra = [self.reqres]
    264  - return rra
    265  -
    266  - def getHttpService(self):
    267  - return self.reqres.getHttpService()
    268  -
    269  -
    270  - 
    271  -if __name__ in ('__main__', 'main'):
    272  - EventQueue.invokeLater(Run(BurpExtender))
    273  - 
Please wait...
Page is in error, reload to recover