Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2FA: Failed to parse server response #109

Closed
antt1v opened this issue May 22, 2018 · 10 comments
Closed

2FA: Failed to parse server response #109

antt1v opened this issue May 22, 2018 · 10 comments

Comments

@antt1v
Copy link

antt1v commented May 22, 2018

Problem description

I stumbled on a problem with a GlobalProtect VPN server using 2FA where openconnect doesn't understand the challenge response. Produced as follows:

  1. I ran openconnect-gp: ./openconnect --protocol=gp --usergroup=portal vpn.server.com --user user.name --dump -vvv
  2. Authentication is successful and I recieve the 2FA token via SMS.
  3. Openconnect gets confused with the server response and doesn't prompt for the challenge:
POST https://vpn.domain.com/global-protect/getconfig.esp
Attempting to connect to server 
Connected to 
SSL negotiation with vpn.domain.com
Connected to HTTPS on vpn.domain.com
> POST /global-protect/getconfig.esp HTTP/1.1
> Host: vpn.domain.com
> User-Agent: PAN GlobalProtect
> X-Pad: 00000000000000000000000
> Content-Type: application/x-www-form-urlencoded
> Content-Length: 169
> 
> jnlpReady=jnlpReady&ok=Login&direct=yes&clientVer=4100&prot=https:&clientos=linux-64&server=vpn.domain.com&computer=G0015&user=user.name&passwd=password
Got HTTP response: HTTP/1.1 200 OK
Server: 
Date: Tue, 22 May 2018 10:49:38 GMT
Content-Type: application/xml; charset=UTF-8
Content-Length: 168
Connection: keep-alive
ETag: "573dc-5ef-5a0a2e6e"
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
Set-Cookie: PHPSESSID=bbaaf9b3131a8f943ef4675172f18b9c; secure; HttpOnly
Set-Cookie: PHPSESSID=bbaaf9b3131a8f943ef4675172f18b9c; secure; HttpOnly
HTTP body length:  (168)
< 		<challenge>
< 		<user>user.name</user>
< 		<inputstr>e708</inputstr>
< 		<respmsg>  Wait for token to change,  then enter the new tokencode:</respmsg>
< 		</challenge>
Failed to parse server response
Response was:		<challenge>
		<user>user.name</user>
		<inputstr>e708</inputstr>
		<respmsg>  Wait for token to change,  then enter the new tokencode:</respmsg>
		</challenge>
Failed to obtain WebVPN cookie

Operating system and openconnect-gp version

openconnect-gp version:

OpenConnect version v7.08-310-gf873d5b
Using GnuTLS. Features present: PKCS#11, HOTP software token, TOTP software token, System keys, DTLS, ESP
Supported protocols: anyconnect (default), nc, gp

operating system:

Linux G0015 4.13.0-41-generic #46~16.04.1-Ubuntu SMP Thu May 3 10:06:43 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

Thanks in advance!

@dlenski
Copy link
Owner

dlenski commented May 22, 2018

Thanks, @antt1v. Lord, I hate GlobalProtect.

All the GlobalProtect VPNs I've seen until now deliver their challenge prompts in the form of a JavaScript blob (yes, JavaScript): https://github.com/dlenski/openconnect/blob/HEAD/gpst.c#L129-L200

var respStatus = "Challenge";
var respMsg = "I herd u liek parsing JavaScript so I sent u sum. PLEEZ ENTER UR CHALLENJ CODE";
thisForm.inputStr.value = "TopSecretRandomString";

… but now you're telling me that there are some GlobalProtect VPNs which respond in a (relatively) saner format with XML:

		<challenge>
		<user>user.name</user>
		<inputstr>e708</inputstr>
		<respmsg>  Wait for token to change,  then enter the new tokencode:</respmsg>
		</challenge>

What can you tell me about the GlobalProtect server? What version of GP is the server running?

dlenski added a commit that referenced this issue May 22, 2018
@antt1v
Copy link
Author

antt1v commented May 23, 2018

Thanks for taking the time to look into this! Yeah, GlobalProtect is... quite terrible.

Unfortunately I don't have access to the server itself so I don't know all the details. I assume the server version is one of these two as they're inside the server response after a successful authentication:

< 	<portal-config-version>4100</portal-config-version>
< 	<version>3.0.3-7</version>

I tried the fix in 077c420 and now the challenge response is parsed succesfully and I receive the portal config after enterting the challenge token.

However, after that when trying to contact the gateway, it reports authentication failure and goes right back to the challenge prompt:

HTTP body length:  (132)
< 
< var respStatus = "Error";
< var respMsg = "Authentication failure: Invalid username or password";
< thisForm.inputStr.value = "e8aa";
< 
Unexpected 512 result from server
Invalid username or password.
  Wait for token to change,  then enter the new tokencode:
Challenge: 

Re-entering the token pin (or the password) seems to do nothing.

Here's the full output:

./openconnect --protocol=gp --usergroup=portal vpn.domain.com --dump -vvv
Please enter your username and password
Username: user.name
Password: 
POST https://vpn.domain.com/global-protect/getconfig.esp
Attempting to connect to server ip:port
Connected to ip:port
SSL negotiation with vpn.domain.com
Connected to HTTPS on vpn.domain.com
> POST /global-protect/getconfig.esp HTTP/1.1
> Host: vpn.domain.com
> User-Agent: PAN GlobalProtect
> X-Pad: 00000000000000000000000
> Content-Type: application/x-www-form-urlencoded
> Content-Length: 169
> 
> jnlpReady=jnlpReady&ok=Login&direct=yes&clientVer=4100&prot=https:&clientos=linux-64&server=vpn.domain.com&computer=G0015&user=user.name&passwd=password
Got HTTP response: HTTP/1.1 200 OK
Server: 
Date: Wed, 23 May 2018 07:07:05 GMT
Content-Type: application/xml; charset=UTF-8
Content-Length: 168
Connection: keep-alive
ETag: "573dc-5ef-5a0a2e6e"
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
Set-Cookie: PHPSESSID=04b1ddc8b0194f2ae293ca2541698931; secure; HttpOnly
Set-Cookie: PHPSESSID=04b1ddc8b0194f2ae293ca2541698931; secure; HttpOnly
HTTP body length:  (168)
< 		<challenge>
< 		<user>user.name</user>
< 		<inputstr>e8aa</inputstr>
< 		<respmsg>  Wait for token to change,  then enter the new tokencode:</respmsg>
< 		</challenge>
  Wait for token to change,  then enter the new tokencode:
Challenge: 
POST https://vpn.domain.com/global-protect/getconfig.esp
> POST /global-protect/getconfig.esp HTTP/1.1
> Host: vpn.domain.com
> User-Agent: PAN GlobalProtect
> Cookie: PHPSESSID=04b1ddc8b0194f2ae293ca2541698931
> X-Pad: 0000000
> Content-Type: application/x-www-form-urlencoded
> Content-Length: 185
> 
> jnlpReady=jnlpReady&ok=Login&direct=yes&clientVer=4100&prot=https:&clientos=linux-64&server=vpn.domain.com&computer=G0015&inputStr=e8aa&user=user.name&passwd=token_pin
Got HTTP response: HTTP/1.1 200 OK
Server: 
Date: Wed, 23 May 2018 07:07:18 GMT
Content-Type: application/xml; charset=UTF-8
Content-Length: 10486
Connection: keep-alive
ETag: "573dc-5ef-5a0a2e6e"
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
Set-Cookie: PHPSESSID=04b1ddc8b0194f2ae293ca2541698931; secure; HttpOnly
Set-Cookie: PHPSESSID=04b1ddc8b0194f2ae293ca2541698931; secure; HttpOnly
Set-Cookie: PHPSESSID=04b1ddc8b0194f2ae293ca2541698931; secure; HttpOnly
Set-Cookie: PHPSESSID=04b1ddc8b0194f2ae293ca2541698931; secure; HttpOnly
Set-Cookie: PHPSESSID=04b1ddc8b0194f2ae293ca2541698931; secure; HttpOnly
Set-Cookie: PHPSESSID=04b1ddc8b0194f2ae293ca2541698931; secure; HttpOnly
HTTP body length:  (10486)
< <?xml version="1.0" encoding="UTF-8" ?>
< <policy>
< 	<portal-name>VPN</portal-name>
< 	<portal-config-version>4100</portal-config-version>
< 	<version>3.0.3-7                                                         </version>
< 	<client-role>global-protect-full</client-role>
< 	<agent-user-override-key>****</agent-user-override-key>
< 	<root-ca>
< 		<entry name="CA Chain">
< 			<cert>
< -----BEGIN CERTIFICATE-----
< ****
< -----END CERTIFICATE-----
< -----BEGIN CERTIFICATE-----
< ****
< -----END CERTIFICATE-----
< 			</cert>
< 			<install-in-cert-store>no</install-in-cert-store>
< 		</entry>
< 	</root-ca>
< 	<connect-method>on-demand</connect-method>
< 	<on-demand>yes</on-demand>
< 	<refresh-config>yes</refresh-config>
< 	<refresh-config-interval>24</refresh-config-interval>
< 	<authentication-modifier>
< 		<none/>
< 	</authentication-modifier>
< 	<authentication-override>
< 		<accept-cookie>no</accept-cookie>
< 		<generate-cookie>yes</generate-cookie>
< 		<cookie-lifetime><lifetime-in-hours>24</lifetime-in-hours></cookie-lifetime>
< 		<cookie-encrypt-decrypt-cert>GP-Cookie- certificate</cookie-encrypt-decrypt-cert>
< 	</authentication-override>
< 	<use-sso>yes</use-sso>
< 	<gateways>
< 		<cutoff-time>5</cutoff-time>
< 		<external>
< 			<list>
< 				<entry name="gateway1.vpn.domain.com">
< 					<priority>1</priority>
< 					<description>gateway1</description>
< 				</entry>
< 			</list>
< 		</external>
< 	</gateways>
< 	<agent-ui>
< 		<can-save-password>yes</can-save-password>
< 		<passcode></passcode>
< 		<agent-user-override-timeout>0</agent-user-override-timeout>
< 		<max-agent-user-overrides>0</max-agent-user-overrides>
< 		<help-page></help-page>
< 		<welcome-page>
< 			<display>no</display>
< 			<page></page>
< 		</welcome-page>
< <agent-user-override>with-comment</agent-user-override>
< <enable-advanced-view>yes</enable-advanced-view>
< <enable-do-not-display-this-welcome-page-again>yes</enable-do-not-display-this-welcome-page-again>
< <can-change-portal>yes</can-change-portal>
< <show-agent-icon>yes</show-agent-icon>
< <password-expiry-message></password-expiry-message>
< 
< 	</agent-ui>
< 	<hip-collection>
< 		<hip-report-interval>3600</hip-report-interval>
< 		<max-wait-time>20</max-wait-time>
< 		<collect-hip-data>yes</collect-hip-data>
< 		<default>
< 			<category>
< 				<member>host-info</member>
< 				<member>data-loss-prevention</member>
< 				<member>patch-management</member>
< 				<member>firewall</member>
< 				<member>antivirus</member>
< 				<member>anti-spyware</member>
< 				<member>disk-backup</member>
< 				<member>disk-encryption</member>
< 			</category>
< 		</default>
< 	</hip-collection>
< 	<agent-config>
< 	<save-user-credentials>1</save-user-credentials>
< 	<portal-2fa>no</portal-2fa>
< 	<internal-gateway-2fa>no</internal-gateway-2fa>
< 	<auto-discovery-external-gateway-2fa>no</auto-discovery-external-gateway-2fa>
< 	<manual-only-gateway-2fa>no</manual-only-gateway-2fa>
< <client-upgrade>prompt</client-upgrade>
< <logout-remove-sso>yes</logout-remove-sso>
< <krb-auth-fail-fallback>yes</krb-auth-fail-fallback>
< <retry-tunnel>30</retry-tunnel>
< <retry-timeout>5</retry-timeout>
< <enforce-globalprotect>no</enforce-globalprotect>
< <captive-portal-exception-timeout>0</captive-portal-exception-timeout>
< <traffic-blocking-notification-delay>15</traffic-blocking-notification-delay>
< <display-traffic-blocking-notification-msg>yes</display-traffic-blocking-notification-msg>
< <traffic-blocking-notification-msg>&lt;div style=&quot;font-family:'Helvetica Neue';&quot;&gt;&lt;h1 style=&quot;color:red;text-align:center; margin: 0; font-size: 30px;&quot;&gt;Notice&lt;/h1&gt;&lt;p style=&quot;margin: 0;font-size: 15px; line-height: 1.2em;&quot;&gt;To access the network, you must first connect to GlobalProtect.&lt;/p&gt;&lt;/div&gt;</traffic-blocking-notification-msg>
< <allow-traffic-blocking-notification-dismissal>yes</allow-traffic-blocking-notification-dismissal>
< <display-captive-portal-detection-msg>no</display-captive-portal-detection-msg>
< <captive-portal-detection-msg>&lt;div style=&quot;font-family:'Helvetica Neue';&quot;&gt;&lt;h1 style=&quot;color:red;text-align:center; margin: 0; font-size: 30px;&quot;&gt;Captive Portal Detected&lt;/h1&gt;&lt;p style=&quot;margin: 0; font-size: 15px; line-height: 1.2em;&quot;&gt;GlobalProtect has temporarily permitted network access for you to connect to the Internet. Follow instructions from your internet provider.&lt;/p&gt;&lt;p style=&quot;margin: 0; font-size: 15px; line-height: 1.2em;&quot;&gt;If you let the connection time out, open GlobalProtect and click Connect to try again.&lt;/p&gt;&lt;/div&gt;</captive-portal-detection-msg>
< <certificate-store-lookup>user-and-machine</certificate-store-lookup>
< <scep-certificate-renewal-period>7</scep-certificate-renewal-period>
< <ext-key-usage-oid-for-client-cert></ext-key-usage-oid-for-client-cert>
< <retain-connection-smartcard-removal>yes</retain-connection-smartcard-removal>
< <rediscover-network>yes</rediscover-network>
< <resubmit-host-info>yes</resubmit-host-info>
< <can-continue-if-portal-cert-invalid>yes</can-continue-if-portal-cert-invalid>
< <user-switch-tunnel-rename-timeout>0</user-switch-tunnel-rename-timeout>
< <pre-logon-tunnel-rename-timeout>-1</pre-logon-tunnel-rename-timeout>
< <show-system-tray-notifications>no</show-system-tray-notifications>
< <max-internal-gateway-connection-attempts>0</max-internal-gateway-connection-attempts>
< <portal-timeout>30</portal-timeout>
< <connect-timeout>60</connect-timeout>
< <receive-timeout>30</receive-timeout>
< <flush-dns>no</flush-dns>
< <proxy-multiple-autodetect>no</proxy-multiple-autodetect>
< <wsc-autodetect>yes</wsc-autodetect>
< <ipv6-preferred>yes</ipv6-preferred>
< 
< 	</agent-config>
< <user-email>[email protected]</user-email>
< <portal-userauthcookie>****</portal-userauthcookie>
< <portal-prelogonuserauthcookie>empty</portal-prelogonuserauthcookie>
< <scep-cert-auth-cookie>****</scep-cert-auth-cookie>
< </policy>
1 gateway servers available:
  gateway1(gateway1.vpn.domain.com)
Please select GlobalProtect gateway.
GATEWAY: [gateway1]:gateway1
POST https://gateway1.vpn.domain.com/ssl-vpn/login.esp
Attempting to connect to server ip:port
Connected to ip:port
SSL negotiation with gateway1.vpn.domain.com
Connected to HTTPS on gateway1.vpn.domain.com
> POST /ssl-vpn/login.esp HTTP/1.1
> Host: gateway1.vpn.domain.com
> User-Agent: PAN GlobalProtect
> X-Pad: 00000000000000000000000000000000000000000000000000000000000
> Content-Type: application/x-www-form-urlencoded
> Content-Length: 197
> 
> jnlpReady=jnlpReady&ok=Login&direct=yes&clientVer=4100&prot=https:&clientos=linux-64&server=gateway1.vpn.domain.com&computer=G0015&inputStr=e8aa&user=user.name&passwd=token_pin
Got HTTP response: HTTP/1.1 512 Custom error
Server: 
Date: Wed, 23 May 2018 07:07:18 GMT
Content-Type: text/html
Content-Length: 132
Connection: keep-alive
ETag: "573f3-1e6f-5a0a2e6e"
Pragma: no-cache
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
x-private-pan-sslvpn: auth-failed
Expires: Thu, 19 Nov 1981 08:52:00 GMT
X-FRAME-OPTIONS: DENY
Set-Cookie: PHPSESSID=d234a7495584b0cc6fef18741041a0c0; secure; HttpOnly
Set-Cookie: PHPSESSID=d234a7495584b0cc6fef18741041a0c0; secure; HttpOnly
Set-Cookie: PHPSESSID=d234a7495584b0cc6fef18741041a0c0; secure; HttpOnly
HTTP body length:  (132)
< 
< var respStatus = "Error";
< var respMsg = "Authentication failure: Invalid username or password";
< thisForm.inputStr.value = "e8aa";
< 
Unexpected 512 result from server
Invalid username or password.
  Wait for token to change,  then enter the new tokencode:
Challenge: 

@dlenski
Copy link
Owner

dlenski commented May 23, 2018

However, after that when trying to contact the gateway, it reports authentication failure and goes right back to the challenge prompt:

When logging in via the portal interface, the current behavior is (a) do the portal login and (b) if the portal login succeeds, reuse the same credentials from the portal form to attempt to login to the gateway.

The problem here, I think, is that the credentials being reused are those of the 2FA challenge login (username, token code, inputStr) rather than the "original" login (username, password).

Is that a correct statement?

What happens if you skip the portal entirely, and just connect to https://gateway1.vpn.domain.com/gateway? There are very few cases I've seen where the portal login is actually necessary or desirable… and they mainly involve weird external web-based authentication mechanisms.

Re-entering the token pin (or the password) seems to do nothing.

Yeah …

  • re-entering the token pin probably doesn't work because the gateway isn't expecting the token login.
  • re-entering the password doesn't work because the hidden inputStr field is still set, causing the gateway to interpret it as a response to a different login form.

@dlenski
Copy link
Owner

dlenski commented May 23, 2018

27c5568 should fix this, though it will require you to re-enter the original password again for the gateway.

I really need to clean up the GP form handling, but the problem is that I don't know all the possible weird things that can happen, such as this one :(

@antt1v
Copy link
Author

antt1v commented May 23, 2018

The problem here, I think, is that the credentials being reused are those of the 2FA challenge login (username, token code, inputStr) rather than the "original" login (username, password).
Is that a correct statement?

Ah, that makes perfect sense. You are correct.

What happens if you skip the portal entirely, and just connect to https://gateway1.vpn.domain.com/gateway?

Great idea, that worked perfectly! I have some trouble with the DNS server behind the VPN not being used, but I'm guessing that's more likely due to my local setup rather than skipping the portal. I'll work it out. Other than that, no issues.

For the record, the challenge prompt from the gateway was in JavaScript, not XML like from the portal.

27c5568 should fix this, though it will require you to re-enter the original password again for the gateway.

I gave this a try but no luck. The inputStr got screwed up and it goes back to the challenge prompt. Here's the result:

1 gateway servers available:
  gateway1 (gateway1.vpn.domain.com)
Please select GlobalProtect gateway.
GATEWAY: [gateway1]:gateway1
0L��
Challenge: 
POST https://gateway1.vpn.domain.com/ssl-vpn/login.esp
Attempting to connect to server ip:port
Connected to ip:port
SSL negotiation with gateway1.vpn.domain.com
Connected to HTTPS on gateway1.vpn.domain.com
> POST /ssl-vpn/login.esp HTTP/1.1
> Host: gateway1.vpn.domain.com
> User-Agent: PAN GlobalProtect
> X-Pad: 0000000000000000000000000000000000000000000000000000000
> Content-Type: application/x-www-form-urlencoded
> Content-Length: 201
> 
> jnlpReady=jnlpReady&ok=Login&direct=yes&clientVer=4100&prot=https:&clientos=linux-64&server=gateway1.vpn.domain.com&computer=G0015&inputStr=p%c3%9a%01&user=user.name&passwd=password
Got HTTP response: HTTP/1.1 512 Custom error
Server: 
Date: Wed, 23 May 2018 09:37:29 GMT
Content-Type: text/html
Content-Length: 139
Connection: keep-alive
ETag: "573f3-1e6f-5a0a2e6e"
Pragma: no-cache
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
x-private-pan-sslvpn: auth-failed
Expires: Thu, 19 Nov 1981 08:52:00 GMT
X-FRAME-OPTIONS: DENY
Set-Cookie: PHPSESSID=e12dc946f6174c97661bb3ecc4e61748; secure; HttpOnly
Set-Cookie: PHPSESSID=e12dc946f6174c97661bb3ecc4e61748; secure; HttpOnly
Set-Cookie: PHPSESSID=e12dc946f6174c97661bb3ecc4e61748; secure; HttpOnly
HTTP body length:  (139)
< 
< var respStatus = "Error";
< var respMsg = "Authentication failure: Invalid username or password";
< thisForm.inputStr.value = "p&Atilde;��";
< 
Unexpected 512 result from server
Invalid username or password.

Challenge: 

In any case I'm personally fine skipping the portal and using the gateway. Thanks for the help! :)

If you do choose to continue improving the form handling I'd be happy to test any changes.

@dlenski
Copy link
Owner

dlenski commented May 23, 2018

For the record, the challenge prompt from the gateway was in JavaScript, not XML like from the portal.

🤦‍♂️

Internal consistency is not one of the strong points of the GP authentication flow. And that's the nicest possible thing I could say about it.

If you do choose to continue improving the form handling I'd be happy to test any changes.

I see what I screwed up in my haste. Will fix tomorrow.

@dlenski
Copy link
Owner

dlenski commented May 29, 2018

@antt1v, please let me know if 49a7074 fixed the problem with the portal. It'll be good to know that this case works, even if it's not necessary for you to use it.

@antt1v
Copy link
Author

antt1v commented May 29, 2018

I can confirm that the portal authentication now works at least for me. Basically everything is done twice, first with the portal and then with the gateway. The authentication prompts are:

  1. Portal username
  2. Portal password
  3. Portal token challenge
  4. GW username
  5. GW password
  6. GW token challenge (a new one)

Not the prettiest, but it works! Thanks! :)

@dlenski
Copy link
Owner

dlenski commented May 29, 2018

Great, thanks for filling me in!

I can confirm that the portal authentication now works at least for me. Basically everything is done twice, first with the portal and then with the gateway.

Yeah, that's what I expected 🙁. I assume that when you do this with the official client, the portal passes off some kind of "portal authentication cookie" to prevent the gateway from having to redo the login and challenge.

Except for (4): it re-prompts you for the username? That's not supposed to happen. I wrote a quick-and-dirty GlobalProtect server "simulator" and used it to test this scenario, and at least with the simulator it does not reprompt for username…

screenshot

@dlenski dlenski closed this as completed May 29, 2018
@antt1v
Copy link
Author

antt1v commented May 29, 2018

Ah, my bad! Step 4 doesn't actually happen, just the passwords.

Repository owner locked and limited conversation to collaborators Oct 10, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants