Skip to content

Commit

Permalink
[v15] Always verify the old password when changing it (#38962)
Browse files Browse the repository at this point in the history
* Always verify the old password when changing it

* review
  • Loading branch information
bl-nero committed Mar 6, 2024
1 parent e14c2b1 commit 89a425c
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 4 deletions.
5 changes: 5 additions & 0 deletions lib/auth/methods.go
Expand Up @@ -371,6 +371,11 @@ func (a *Server) authenticateUserInternal(ctx context.Context, req AuthenticateU
authErr = authenticateHeadlessError
case req.Webauthn != nil:
authenticateFn = func() (*types.MFADevice, error) {
if req.Pass != nil {
if err = a.checkPasswordWOToken(user, req.Pass.Password); err != nil {
return nil, trace.Wrap(err)
}
}
mfaResponse := &proto.MFAAuthenticateResponse{
Response: &proto.MFAAuthenticateResponse_Webauthn{
Webauthn: wantypes.CredentialAssertionResponseToProto(req.Webauthn),
Expand Down
6 changes: 2 additions & 4 deletions lib/auth/password.go
Expand Up @@ -129,10 +129,8 @@ func (a *Server) ChangePassword(ctx context.Context, req *proto.ChangePasswordRe
Username: user,
Webauthn: wantypes.CredentialAssertionResponseFromProto(req.Webauthn),
}
if len(req.OldPassword) > 0 {
authReq.Pass = &PassCreds{
Password: req.OldPassword,
}
authReq.Pass = &PassCreds{
Password: req.OldPassword,
}
if req.SecondFactorToken != "" {
authReq.OTP = &OTPCreds{
Expand Down
51 changes: 51 additions & 0 deletions lib/auth/password_test.go
Expand Up @@ -29,12 +29,14 @@ import (
"github.com/gravitational/trace"
"github.com/jonboulle/clockwork"
"github.com/pquerna/otp/totp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/bcrypt"

"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/client/proto"
"github.com/gravitational/teleport/api/constants"
mfav1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/mfa/v1"
"github.com/gravitational/teleport/api/types"
apievents "github.com/gravitational/teleport/api/types/events"
wanpb "github.com/gravitational/teleport/api/types/webauthn"
Expand Down Expand Up @@ -294,6 +296,55 @@ func TestServer_ChangePassword(t *testing.T) {
}
}

// This test asserts that an attacker is unable to change password without
// providing the old one if they take over a user's web session and use a
// different type of WebAuthn challenge that would be normally requested by the
// Web UI. This is a regression test for
// https://github.com/gravitational/teleport-private/issues/1369.
func TestServer_ChangePassword_FailsWithoutOldPassword(t *testing.T) {
t.Parallel()

server := newTestTLSServer(t)
mfa := configureForMFA(t, server)
authServer := server.Auth()
ctx := context.Background()

username := mfa.User
newPass := []byte("capybarasarecool123")

userClient, err := server.NewClient(TestUser(username))
require.NoError(t, err)
defer userClient.Close()

// Acquire and solve an MFA challenge.
mfaChallenge, err := userClient.CreateAuthenticateChallenge(ctx, &proto.CreateAuthenticateChallengeRequest{
Request: &proto.CreateAuthenticateChallengeRequest_ContextUser{
ContextUser: &proto.ContextUser{},
},
ChallengeExtensions: &mfav1.ChallengeExtensions{
AllowReuse: mfav1.ChallengeAllowReuse_CHALLENGE_ALLOW_REUSE_NO,
},
})
require.NoError(t, err, "creating challenge")
mfaResp, err := mfa.WebDev.SolveAuthn(mfaChallenge)
require.NoError(t, err, "solving challenge with device")

// Change password.
req := &proto.ChangePasswordRequest{
User: username,
NewPassword: newPass,
Webauthn: mfaResp.GetWebauthn(),
}
err = authServer.ChangePassword(ctx, req)
assert.True(t,
trace.IsAccessDenied(err),
"ChangePassword error mismatch, want=AccessDenied, got=%v (%T)",
err, trace.Unwrap(err))

// Did the password change take effect?
assert.Error(t, authServer.checkPasswordWOToken(username, newPass), "password was changed")
}

func TestChangeUserAuthentication(t *testing.T) {
t.Parallel()

Expand Down

0 comments on commit 89a425c

Please sign in to comment.