Reorganizing the code + Swagger browsing and clear-all buttons added

This commit is contained in:
Sebastien Dudek 2021-07-20 21:08:40 +02:00
parent f0f9cfeba7
commit 2bf4f3af91
4 changed files with 214 additions and 158 deletions

View File

@ -7,21 +7,31 @@
from burp import IBurpExtender, ITab
from burp import IMessageEditorController, IContextMenuFactory
from javax import swing
from java.awt import Font
from java.awt import BorderLayout
from java.awt import Color
from java.awt import BorderLayout
from javax.swing import JButton
from javax.swing import JFileChooser
from javax.swing import JMenuItem
from java.awt import (
Font,
BorderLayout,
Color,
Desktop,
Button,
)
from java.awt.event import ActionListener
from javax.swing import (
JButton,
JFileChooser,
JMenuItem,
)
from javax.swing.text import DefaultHighlighter
from java.awt import Button
from java.util import LinkedList
from java.net import URI
import sys
import yaml
import json
from urlparse import urlparse
from utils.OpenAPI3GPP import *
__AUTHOR__ = "Sebastien Dudek (FlUxIuS)"
__VERSION__ = "1.2"
SWAGGER_URL = "https://jdegre.github.io/editor/?url=https://raw.githubusercontent.com/jdegre/5GC_APIs/master/"
try:
@ -30,6 +40,15 @@ except ImportError:
pass
class CallbackActionListener(ActionListener):
def __init__(self, callback):
ActionListener.__init__(self)
self._callback = callback
def actionPerformed(self, event):
self._callback(event)
class BurpExtender(IBurpExtender, ITab, IMessageEditorController, IContextMenuFactory):
editboxes = []
@ -60,7 +79,7 @@ class BurpExtender(IBurpExtender, ITab, IMessageEditorController, IContextMenuFa
# YAML file label
toptextLabel = swing.JLabel('5GC Network Function parser (version 1.1)')
toptextLabel = swing.JLabel('5GC Network Function parser (version 1.2)')
boxHorizontal.add(toptextLabel)
author = swing.JLabel('By @FlUxIuS at https://penthertz.com')
@ -103,7 +122,7 @@ class BurpExtender(IBurpExtender, ITab, IMessageEditorController, IContextMenuFa
# Created a tabbed pane to go in the center of the
# main tab, below the text area
self.tabbedPane = swing.JTabbedPane()
self.tab.add("Center", self.tabbedPane);
self.tab.add("Center", self.tabbedPane)
# Add the text panel to the top of the main tab
@ -114,6 +133,15 @@ class BurpExtender(IBurpExtender, ITab, IMessageEditorController, IContextMenuFa
return
def clearTabs(self, event):
"""
Clear all tabs
"""
self.tab.remove(self.tabbedPane)
self.tabbedPane = swing.JTabbedPane()
self.tab.add("Center", self.tabbedPane)
def selectFile(self, event):
"""
SelectFile - Open file select popup
@ -217,154 +245,16 @@ class BurpExtender(IBurpExtender, ITab, IMessageEditorController, IContextMenuFa
boxVertical.add(SendTobutton)
rTextArea.setText(params)
class OpenAPI3GPP(object):
yamlinst = None
urltobrowse = SWAGGER_URL + "/" + self.textAreaFile.getText().split("/")[-1]
btnswag = Button('Open similar API file\'s Swagger')
btnswag.addActionListener(
CallbackActionListener(lambda _: Desktop.getDesktop().browse(URI(urltobrowse)))
)
boxVertical.add(btnswag)
ClearAllbutton = Button('Clear all', actionPerformed=self.clearTabs)
boxVertical.add(ClearAllbutton)
def __init__(self, yamlfile):
self.yamlinst = yaml.load(open(yamlfile), Loader=yaml.FullLoader)
def listpaths(self):
return [k for k, v in self.yamlinst['paths'].items()]
def getEndPointVerbs(self, path):
return [k for k, v in self.yamlinst['paths'][path].items()]
def getEndPointSummary(self, path, verb):
try:
return self.yamlinst['paths'][path][verb]['summary']
except:
return None
def getTags(self, path, verb):
try:
return ''.join(self.yamlinst['paths'][path][verb]['tags'])
except:
return None
def getParameters(self, path, verb):
try:
return self.yamlinst['paths'][path][verb]['parameters']
except:
return []
def getRequestBody(self, path, verb):
try:
return self.yamlinst['paths'][path][verb]['callbacks']
except:
return []
def dumpParameters(self, path, verb):
try:
return yaml.dump(self.getParameters(path, verb))
except:
return None
def dumpRequestBody(self, path, verb):
try:
return yaml.dump(self.getRequestBody(path, verb))
except:
return None
def getRequestJSObject(self, path, verb):
try:
return self.yamlinst['paths'][path][verb]['requestBody']['content']['application/json']['schema']['$ref']
except:
return None
def fetchObject(self, path):
prop = self.yamlinst
for k in path:
prop = prop[k]
return prop
def getPropJSObject(self, path, verb):
jsobj = self.getRequestJSObject(path, verb)
paths = jsobj.split('/')[1:]
obj = self.fetchObject(paths)
required = obj['required']
return (obj['properties'], required)
def buildJSObj(self, path, verb):
props, reqs = self.getPropJSObject(path, verb)
ojson = {}
for k, v in props.items():
if k in reqs:
ojson[k] = 'required_value'
else:
ojson[k] = 'value'
return repr(ojson)
# default field values
field_table = { 'Content-Encoding' : 'gzip, deflate',
'Accept-Encoding' : 'gzip, deflate',
'User-Agent' : 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:90.0) Gecko/20100101 Firefox/90.0',
'Accept' : 'application/json',
}
def buildDefaultHeaders(self, netloc):
string = ""
string += 'Host: ' + netloc + "\r\n"
string += 'User-Agent: ' + self.field_table['User-Agent'] + "\r\n"
string += 'Accept: ' + self.field_table['Accept'] + "\r\n"
return string
def buildRequest(self, path, verb, reqpath, netloc):
"""
Builds web request from YAML file
"""
parameters = self.getParameters(path, verb)
reqpath = self.yamlinst['servers'][0]['url'].format(apiRoot=reqpath) + path
extrpath = ""
headers = self.buildDefaultHeaders(netloc)
body = "\r\n"
reqstring = "{verb} {path} HTTP/1.1\r\n"
for param in parameters:
try:
value = "{value}"
if param['name'] in self.field_table:
value = self.field_table[param['name']]
if param['in'] == 'header':
headers += "%s: %s\r\n" % (param['name'], value)
elif param['in'] == 'query':
if extrpath == '':
extrpath += "?"
required = ''
if 'required' in param:
if param['required'] == True:
required = "required"
extrpath += "%s=%s&" % (param['name'], required)
except:
pass
reqpath += extrpath[:-1] # delete last and op
reqstring = reqstring.format(verb=verb.upper(), path=reqpath)
reqstring += headers
reqstring += "\n"
reqObj = self.getRequestJSObject(path, verb)
if reqObj is not None:
reqstring += "\n"
reqstring += self.buildJSObj(path, verb)
return reqstring
try:
FixBurpExceptions()
except:

6
__init__.py Normal file
View File

@ -0,0 +1,6 @@
#
# Part of the 5GC API Parse
# A 5GC NF OpenAPI parser - Burp Suite Extension
# Author: Sebastien Dudek (@FlUxIuS) at https://penthertz.com
#

154
utils/OpenAPI3GPP.py Normal file
View File

@ -0,0 +1,154 @@
#
# Part of the 5GC API Parse
# A 5GC NF OpenAPI parser - Burp Suite Extension
# Author: Sebastien Dudek (@FlUxIuS) at https://penthertz.com
#
import yaml
import json
class OpenAPI3GPP(object):
yamlinst = None
def __init__(self, yamlfile):
self.yamlinst = yaml.load(open(yamlfile), Loader=yaml.FullLoader)
def listpaths(self):
return [k for k, v in self.yamlinst['paths'].items()]
def getEndPointVerbs(self, path):
return [k for k, v in self.yamlinst['paths'][path].items()]
def getEndPointSummary(self, path, verb):
try:
return self.yamlinst['paths'][path][verb]['summary']
except:
return None
def getTags(self, path, verb):
try:
return ''.join(self.yamlinst['paths'][path][verb]['tags'])
except:
return None
def getParameters(self, path, verb):
try:
return self.yamlinst['paths'][path][verb]['parameters']
except:
return []
def getRequestBody(self, path, verb):
try:
return self.yamlinst['paths'][path][verb]['callbacks']
except:
return []
def dumpParameters(self, path, verb):
try:
return yaml.dump(self.getParameters(path, verb))
except:
return None
def dumpRequestBody(self, path, verb):
try:
return yaml.dump(self.getRequestBody(path, verb))
except:
return None
def getRequestJSObject(self, path, verb):
try:
return self.yamlinst['paths'][path][verb]['requestBody']['content']['application/json']['schema']['$ref']
except:
return None
def fetchObject(self, path):
prop = self.yamlinst
for k in path:
prop = prop[k]
return prop
def getPropJSObject(self, path, verb):
jsobj = self.getRequestJSObject(path, verb)
paths = jsobj.split('/')[1:]
obj = self.fetchObject(paths)
required = obj['required']
return (obj['properties'], required)
def buildJSObj(self, path, verb):
props, reqs = self.getPropJSObject(path, verb)
ojson = {}
for k, v in props.items():
if k in reqs:
ojson[k] = 'required_value'
else:
ojson[k] = 'value'
return repr(ojson)
# default field values
field_table = { 'Content-Encoding' : 'gzip, deflate',
'Accept-Encoding' : 'gzip, deflate',
'User-Agent' : 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:90.0) Gecko/20100101 Firefox/90.0',
'Accept' : 'application/json',
}
def buildDefaultHeaders(self, netloc):
string = ""
string += 'Host: ' + netloc + "\r\n"
string += 'User-Agent: ' + self.field_table['User-Agent'] + "\r\n"
string += 'Accept: ' + self.field_table['Accept'] + "\r\n"
return string
def buildRequest(self, path, verb, reqpath, netloc):
"""
Builds web request from YAML file
"""
parameters = self.getParameters(path, verb)
reqpath = self.yamlinst['servers'][0]['url'].format(apiRoot=reqpath) + path
extrpath = ""
headers = self.buildDefaultHeaders(netloc)
body = "\r\n"
reqstring = "{verb} {path} HTTP/1.1\r\n"
for param in parameters:
try:
value = "{value}"
if param['name'] in self.field_table:
value = self.field_table[param['name']]
if param['in'] == 'header':
headers += "%s: %s\r\n" % (param['name'], value)
elif param['in'] == 'query':
if extrpath == '':
extrpath += "?"
required = ''
if 'required' in param:
if param['required'] == True:
required = "required"
extrpath += "%s=%s&" % (param['name'], required)
except:
pass
reqpath += extrpath[:-1] # delete last and op
reqstring = reqstring.format(verb=verb.upper(), path=reqpath)
reqstring += headers
reqstring += "\n"
reqObj = self.getRequestJSObject(path, verb)
if reqObj is not None:
reqstring += "\n"
reqstring += self.buildJSObj(path, verb)
return reqstring

6
utils/__init__.py Normal file
View File

@ -0,0 +1,6 @@
#
# Part of the 5GC API Parse
# A 5GC NF OpenAPI parser - Burp Suite Extension
# Author: Sebastien Dudek (@FlUxIuS) at https://penthertz.com
#