Last active
July 16, 2021 23:19
-
-
Save retpolanne/41e8a3da92adf05fa31d47aa54032403 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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