dlenski / gp-saml-gui

Interactively authenticate to GlobalProtect VPNs that require SAML
GNU General Public License v3.0
293 stars 66 forks source link

Microsoft Authentication: SAML Issue #31

Closed Hugobsb closed 2 years ago

Hugobsb commented 3 years ago

Any idea of what is going on? I login successfully with my credentials, and after I get this error.

Got SAML result headers: {'saml-auth-status': '1'}

dlenski commented 3 years ago

Any idea of what is going on?

Not without a whole lot more details, nope. Run with gp-saml-gui -vv, study the step-by-step explanation of what it's doing, and share as much of the log as you can.

… and after I get this error.

Got SAML result headers: {'saml-auth-status': '1'}

This is not an error.

Hugobsb commented 3 years ago

Any idea of what is going on?

Not without a whole lot more details, nope. Run with gp-saml-gui -vv, study the step-by-step explanation of what it's doing, and share as much of the log as you can.

… and after I get this error. Got SAML result headers: {'saml-auth-status': '1'}

This is not an error.

Sorry for that, I was far from the PC I used.

Here it is.

➜  ~ gp-saml-gui -vv --gateway --clientos=Windows company.vpn.com

Looking for SAML auth tags in response to https://company.vpn.com/ssl-vpn/prelogin.esp...
Got SAML REDIRECT, opening browser...

[REQUEST] GET for resource https://login.microsoftonline.com/censoredCompanyId/saml2?SAMLRequest=censoredCompanyId

[PAGE ] Finished loading page https://login.microsoftonline.com/censoredCompanyId/saml2SAMLRequest=censoredCompanyId

[RECEIVE] 3995 bytes of text/html; charset=utf-8 for resource GET https://login.microsoftonline.com/censoredCompanyId/saml2?AMLRequest=censoredCompanyId

[REQUEST] POST for resource https://company.vpn.com/SAML20/SP/ACS

[PAGE] Finished loading page https://company.vpn.com/SAML20/SP/ACS

[SAML] Got SAML result headers: {'saml-auth-status': '1'}

[RECEIVE] 1792 bytes of text/html; charset=UTF-8 for resource POST https://company.vpn.com/SAML20/SP/ACS

[DATA] 1792 bytes of text/html; charset=UTF-8 for resource https://company.vpn.com/SAML20/SP/ACS

Date: Thu, 27 May 2021 16:52:44 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 1792
Connection: keep-alive
ETag: "censoredTagId"
Pragma: no-cache
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Expires: Thu, 19 Nov 1981 08:52:00 GMT
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block;
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; img-src * data:; style-src 'self' 'unsafe-inline';
saml-auth-status: 1
Strict-Transport-Security: max-age=31536000;

<html>
<head>
    <title>Login Error</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <META HTTP-EQUIV="PRAGMA" CONTENT="NO-CACHE">
    <meta name="viewport" content="initial-scale=1.0">
    <style>
        #content {
            border:3px solid#aaa;
            background-color:#fff;
            margin:1.2em;
            padding:1.2em;
            font-family:Tahoma,Helvetica,Arial,sans-serif;
            font-size:1em;
        }
        h1 {
            font-size:1.2em;
            font-weight:bold;
            color:#196390;
        }
        b {
            font-weight:normal;
            color:#196390;
        }
        form td, form input {
            font-size: 1em;
            font-weight: bold;
        }
        #formtable {
            height: 100%;
            width: 100%;
        }
        #formtd {
            vertical-align: middle;
        }
        #formdiv {
            margin-left: auto;
            margin-right: auto;
        }
    </style>
</head>
<body bgcolor="#e7e8e9">
<div id="content">
    <div id="formdiv">

<style>
    #dError1 {
        color: #E10000;
        margin-top: 10px;
        padding-top: 10px;
        padding-bottom: 10px;
        margin-left: 10px;
        margin-right: 10px;
        padding-left: 10px;
        padding-right: 10px;
        font-weight: bold;
        overflow: auto;
    }
</style>

<form name="login" id="login_form" >
    <h3><font color='red'><b>Authentication Failed</b></font></h3>
    <div id="dError1" class="dError1">Please contact the administrator for further assistance</div>
</form>

Error code: <saml-auth-status>-1</saml-auth-status>
    </div>
</div>
<a href="#" onclick="window.top.location='/php/login.php';return false;">Login Retry</a>
</body>
</html>

Login window closed by user.
dlenski commented 3 years ago

If you instead do gp-saml-gui -vv --external --gateway --clientos=Windows company.vpn.com, it will pop up your "regular" browser.

With the external browser, the script won't be able to automatically extract the SAML results, but you can try to follow the login procedure (you may also need to spoof the User-Agent to pretend that it's Windows, but first try without).

If you do it in the regular browser… does the login succeed? Can you extract the portal-userauthcookie or prelogin-cookie from the HTML or headers of the final page?

Assuming the login in the regular browser succeeds, then we need to figure out what's different between the "regular browser" vs. the browser window that gp-saml-gui pops up.

Hugobsb commented 3 years ago

If you instead do gp-saml-gui -vv --external --gateway --clientos=Windows company.vpn.com, it will pop up your "regular" browser.

With the external browser, the script won't be able to automatically extract the SAML results, but you can try to follow the login procedure (you may also need to spoof the User-Agent to pretend that it's Windows, but first try without).

If you do it in the regular browser… does the login succeed? Can you extract the portal-userauthcookie or prelogin-cookie from the HTML or headers of the final page?

Assuming the login in the regular browser succeeds, then we need to figure out what's different between the "regular browser" vs. the browser window that gp-saml-gui pops up.

Excellent point. I'm checking this right now.

Hugobsb commented 3 years ago

Same issue.

image

Also nothing interesting in the scripts src, apparently:

image

Logs:

➜ ~ gp-saml-gui -vv --external --gateway --clientos=Windows company.vpn.com

Looking for SAML auth tags in response to https://company.vpn.com/ssl-vpn/prelogin.esp... Got SAML REDIRECT, opening external browser for debugging...

➜ ~ Abrindo em uma sessão de navegador existente. (translating, opening a session in an existing browser)

dlenski commented 3 years ago

What happens if you do SAML to the portal interface instead of the gateway? Yes, it's a pain, because you may have to do it again on the gateway (but see https://gitlab.com/openconnect/openconnect/-/issues/147, https://gitlab.com/openconnect/openconnect/-/merge_requests/199 for work to alleviate this in OpenConnect).

Ideas, in rough order that they should be considered…

  1. It may be that your VPN's SAML configuration is broken unless going through the portal. (Because that's what the official PAN/GP clients do, so it's probably all that it was tested against).
  2. Some PAN GP VPNs send a field called <saml-request-id> in their SAML prelogin. Does your VPN contain this in /ssl-vpn/prelogin.esp? I don't know what to do with this field in gp-saml-gui or OpenConnect, because I haven't seen logs from the official client where it's used, but it might be needed somewhere/somehow for SAML to succeed on some VPNs.
  3. Run mitmproxy to capture the requests from the official client, and figure out what is different and significant.
Hugobsb commented 3 years ago

Thanks for sending me these links, it seems to be helpful, I will read them for further understanding.

I took a look at the logs, and I think this may be helpful here.

It sends:

`

(came empty) Seems to be an ID(two letters) ` It also sends a ``
dlenski commented 3 years ago

Thanks for sending me these links, it seems to be helpful, I will read them for further understanding.

If you want to dive into the deep end, we have a vigorously ongoing gripe-session/discussion about the many issues with sanely (re)implementing GP SAML over at https://gitlab.com/openconnect/openconnect/-/issues/147#note_587223494

I took a look at the logs, and I think this may be helpful here.

It sends:

<saml-request-id>(came empty)</saml-request-id> <saml-request>Seems to be an ID</saml-request><region>(two letters)</region>

Yep, that's the "normal" case, where <saml-request-id> is empty or absent, and <saml-request> contains a big base64-encoded blob of a URL or a complete HTML page, from which the SAML starts.

This is the case gp-saml-gui knows how to handle so far.

It also sends a <prelogin-cookie></prelogin-cookie>

With a non-empty value? :raised_eyebrow:

That's very interesting if so. Unfortunately, I've never seen this before and have no idea how the value might need to be injected into the SAML flow, without having much more access to its contents. A MITMproxy capture is probably the sane way to handle this.

Hugobsb commented 3 years ago

I don't have much knowledge about VPNs and Networks. Is there any command I can do to authenticate using the portal?

I also found your explanation, my problem here is right about cookies, and SAML tags in general which are passed on the authentication isn't it? A friend of mine updated the GP a little ago, and the VPN service stopped working, they ask us to use the version 5.0.6-29.

image

Hugobsb commented 3 years ago

Thanks for sending me these links, it seems to be helpful, I will read them for further understanding.

If you want to dive into the deep end, we have a vigorously ongoing gripe-session/discussion about the many issues with sanely (re)implementing GP SAML over at https://gitlab.com/openconnect/openconnect/-/issues/147#note_587223494

I took a look at the logs, and I think this may be helpful here. It sends: <saml-request-id>(came empty)</saml-request-id> <saml-request>Seems to be an ID</saml-request><region>(two letters)</region>

Yep, that's the "normal" case, where <saml-request-id> is empty or absent, and <saml-request> contains a big base64-encoded blob of a URL or a complete HTML page, from which the SAML starts.

This is the case gp-saml-gui knows how to handle so far.

It also sends a <prelogin-cookie></prelogin-cookie>

With a non-empty value? 🤨

That's very interesting if so. Unfortunately, I've never seen this before and have no idea how the value might need to be injected into the SAML flow, without having much more access to its contents. A MITMproxy capture is probably the sane way to handle this.

Yes, it is not empty.

I am going to grab some useful information using MITM.

dlenski commented 2 years ago

Any updates @Hugobsb? Did you ever get this working? Please reopen or comment if any further updates.