Skip to content

Instantly share code, notes, and snippets.

@retpolanne
Last active July 16, 2021 23:19
Show Gist options
  • Save retpolanne/41e8a3da92adf05fa31d47aa54032403 to your computer and use it in GitHub Desktop.
Save retpolanne/41e8a3da92adf05fa31d47aa54032403 to your computer and use it in GitHub Desktop.
import argparse
import base64
from bs4 import BeautifulSoup
from http import cookies
from http.server import HTTPServer, BaseHTTPRequestHandler
import json
import requests
import os
import socketserver
from urllib.parse import unquote
import webbrowser
from xml.etree import ElementTree
PORTAL = None
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description='''
python browser_flow.py portal-address 2>/dev/null
Requirements:
python 3
beautifulsoup4
'''
)
parser.add_argument(
'portal',
type=str,
help='The GlobalProtect portal address'
)
class AuthServer(HTTPServer):
def serve_forever(self):
self.stop = False
while not self.stop:
self.handle_request()
class AuthHandler(BaseHTTPRequestHandler):
def do_POST(self):
global PORTAL
self.send_response(200)
content_len = int(self.headers.get('content-length'))
post_body = self.rfile.read(content_len)
saml_response_list = parse_saml_response(post_body.decode('utf-8'))
get_acs_prelogin_cookie(PORTAL, saml_response_list)
self.server.stop = True
def log_message(self, format, *args):
return
def start_server(
port,
server_class=AuthServer,
handler_class=AuthHandler
):
server_address = ('', port)
httpd = server_class(server_address, handler_class)
httpd.serve_forever()
def firewall_request(portal, endpoint, form):
url = 'https://{portal}/{endpoint}'.format(
portal=portal,
endpoint=endpoint
)
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'PAN GlobalProtect/4.1.3-8 (Apple Mac OS X 10.14.0)'
}
res = requests.post(url, headers=headers, data=form, verify=False)
return res
def parse_xml(xml):
parsed_xml = ElementTree.fromstring(xml)
tree = ElementTree.ElementTree(parsed_xml)
return tree
def get_prelogin_saml_request(xml):
tree = parse_xml(xml)
root = tree.getroot()
saml_request = root.findall('saml-request')[0]
return base64.b64decode(saml_request.text)
def modify_saml_request(saml_request):
ElementTree.register_namespace(
'samlp',
'urn:oasis:names:tc:SAML:2.0:protocol'
)
tree = parse_xml(saml_request)
root = tree.getroot()
root.set(
'AssertionConsumerServiceURL',
'http://localhost:8080'
)
items = [e for e in tree.iter()]
items[1].text = 'http://localhost:8080'
return ElementTree.tostring(tree.getroot())
def parse_saml_response(saml_response):
saml_response = unquote(saml_response)
saml_response_list = saml_response.split('&')
return saml_response_list
def modify_prelogin_html(prelogin_html):
soup = BeautifulSoup(prelogin_html, 'html.parser')
saml_request_item = soup.find('input', {'name': 'SAMLRequest'})
saml_request = base64.b64decode(saml_request_item.get('value'))
saml_request = modify_saml_request(saml_request)
saml_request = base64.b64encode(saml_request)
saml_request_item['value'] = saml_request.decode('utf-8')
return soup.renderContents()
def open_new_prelogin_html(prelogin_html):
fd = open('./prelogin.html', 'wb')
fd.write(prelogin_html)
fd.close()
uri = 'file://{}/prelogin.html'.format(
os.getcwd()
)
webbrowser.open_new_tab(uri)
def get_acs_prelogin_cookie(portal, saml_response_list):
relay_state = saml_response_list[0].replace('RelayState=', '')
saml_response = saml_response_list[1].replace('SAMLResponse=', '')
form = {
'RelayState': relay_state,
'SAMLResponse': saml_response
}
response = firewall_request(portal, 'SAML20/SP/ACS', form)
print(response.headers.get('prelogin-cookie'))
def prelogin(portal):
tmp_prelogin_form = {
'tmp': 'tmp',
'clientVer': 4100,
'clientos': 'Mac',
'os-version': 'Apple Mac OS X 10.14.0',
'ipv6-support': 'yes'
}
prelogin_res = firewall_request(
portal,
'global-protect/prelogin.esp',
tmp_prelogin_form
)
prelogin_html = get_prelogin_saml_request(prelogin_res.text)
new_prelogin_html = modify_prelogin_html(prelogin_html)
open_new_prelogin_html(new_prelogin_html)
start_server(8080)
def main(args):
global PORTAL
PORTAL = args.portal
prelogin(PORTAL)
if __name__ == '__main__':
args = parser.parse_args()
main(args)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment