sanderv32 / ngx_http_auth_yubikey_module

Yubikey basic authentication for NGINX
Other
36 stars 2 forks source link

auth bypass #6

Open silverskin opened 10 years ago

silverskin commented 10 years ago

Hi, it is possible to bypass the yubikey authentication even though the API and users file is present in the configuration file: auth_yubikey_client_id "xxxxx"; auth_yubikey_secret_key "THIS_IS_THE_KEY"; auth_yubikey_api_url "https://api.yubico.com/wsapi/2.0/verify?id=%d&otp=%s"; auth_yubikey_file "/usr/local/nginx/yubikey.conf"; auth_yubikey_ttl "43200";

This can be done by providing invalid parameter values for the authentication window. I can send the more specific explanation by email.

sanderv32 commented 9 years ago

Hi, can you send me a detailed explanation so I can fix the problem?

silverskin commented 9 years ago

Can you give me your email address, where to send the explanation or do I just paste it here?

sanderv32 commented 9 years ago

You can copy and paste it in here.

silverskin commented 9 years ago

what I found out was that, if a user submits an arbitrary username and arbitrary password (at least 32 characters, like 32 times a) the authentication module first presents the user with a 403 Forbidden page. After this if the user refreshes the page enough times, best I've got was with refreshing it three times the user is allowed access.

The error log is as follows with the requests:

1st request with arbitrary username and password: 2014/11/19 00:40:53 [notice] 1199#0: 42 User: "testing" Password: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", client: 213.243.162.99, server: localhost, request: "GET /test/ HTTP/1.1", host: "80.69.173.183" 2014/11/19 00:40:53 [notice] 1199#0: 42 alcf : default ttl -> 3600 ttl->1416354053 md5->088e398b1b95d6adbee9e29674801439 testing:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, client: 213.243.162.99, server: localhost, request: "GET /test/ HTTP/1.1", host: "80.69.173.183" 2014/11/19 00:40:53 [notice] 1199#0: *42 WSApi url "https://api.yubico.com/wsapi/2.0/verify?id=%d&otp=%s", client: 213.243.162.99, server: localhost, request: "GET /test/ HTTP/1.1", host: "80.69.173.183"

2nd request and user is still presented with 403. 2014/11/19 00:40:55 [notice] 1199#0: 42 User: "testing" Password: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", client: 213.243.162.99, server: localhost, request: "GET /test/ HTTP/1.1", host: "80.69.173.183" 2014/11/19 00:40:55 [notice] 1199#0: 42 alcf : default ttl -> 3600 ttl->1416354055 md5->3e27a0775fe2aac49ae0aba7b63a7bd3 testing:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, client: 213.243.162.99, server: localhost, request: "GET /test/ HTTP/1.1", host: "80.69.173.183" 2014/11/19 00:40:55 [notice] 1199#0: *42 WSApi url "https://api.yubico.com/wsapi/2.0/verify?id=%d&otp=%s", client: 213.243.162.99, server: localhost, request: "GET /test/ HTTP/1.1", host: "80.69.173.183"

3rd request, here it gets interesting, now the logs show that "Found user" and refreshing the page after this user is allowed access to the restricted page. 2014/11/19 00:40:57 [notice] 1199#0: 42 User: "testing" Password: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", client: 213.243.162.99, server: localhost, request: "GET /test/ HTTP/1.1", host: "80.69.173.183" 2014/11/19 00:40:57 [notice] 1199#0: 42 Found user testing. Cache (ttl 1416354055) valid, client: 213.243.162.99, server: localhost, request: "GET /test/ HTTP/1.1", host: "80.69.173.183" 2014/11/19 00:40:57 [notice] 1199#0: 42 User: "testing" Password: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", client: 213.243.162.99, server: localhost, request: "GET /test/ HTTP/1.1", host: "80.69.173.183" 2014/11/19 00:40:57 [notice] 1199#0: 42 alcf : default ttl -> 3600 ttl->1416354057 md5->4e4b547c8c27dd3a5ac7cc91bee88182 testing:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, client: 213.243.162.99, server: localhost, request: "GET /test/ HTTP/1.1", host: "80.69.173.183" 2014/11/19 00:40:57 [notice] 1199#0: *42 WSApi url "https://api.yubico.com/wsapi/2.0/verify?id=%d&otp=%s", client: 213.243.162.99, server: localhost, request: "GET /test/ HTTP/1.1", host: "80.69.173.183"

4th request, which presents the restricted page in the end. 2014/11/19 00:40:59 [notice] 1199#0: 42 User: "testing" Password: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", client: 213.243.162.99, server: localhost, request: "GET /test/ HTTP/1.1", host: "80.69.173.183" 2014/11/19 00:40:59 [notice] 1199#0: 42 Found user testing. Cache (ttl 1416354055) valid, client: 213.243.162.99, server: localhost, request: "GET /test/ HTTP/1.1", host: "80.69.173.183" 2014/11/19 00:40:59 [notice] 1199#0: 42 User: "testing" Password: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", client: 213.243.162.99, server: localhost, request: "GET /test/ HTTP/1.1", host: "80.69.173.183" 2014/11/19 00:40:59 [notice] 1199#0: 42 Found user testing. Cache (ttl 1416354057) valid, client: 213.243.162.99, server: localhost, request: "GET /test/ HTTP/1.1", host: "80.69.173.183"

Somehow the module starts to think that the user is cached and valid. Checking the source code, it should present the unauthorized but somehow it jumps to the else statement and allows access.

                            /* User is already cached, check ttl */
                            /* timeout with time now             */
                            if (alcf->cached_cred[i].ttl < time(NULL)) {
                                    if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) {
                                            ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
                                                                    "Found user %s. Cache (ttl %ud) expired", user.data, alcf->cached_cred$
                                    }
                                    return NGX_HTTP_UNAUTHORIZED;
                            } else
                                    if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) {
                                            ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
                                                                    "Found user %s. Cache (ttl %ud) valid", user.data, alcf->cached_cred[i$
                                    }
                                    return NGX_OK;
Icebird2000 commented 9 years ago

Hello, I have the same Problem. I think it is wrong to cache credentials bevor they are checked. Another problem is then that the user-check is not working anymore. so you can take all usernames and every 32 characters log password to get in.

sanderv32 commented 9 years ago

I'm aware of the implications of caching the username and OTP, but as HTTP is a stateless protocol the other way to solve this problem is setting a cookie (something i already started working on). Because of the nature of OTP there is no other way to solve this as the OTP can only be used one time only. Caching OTP shouldn't be any problem as you can't replay it.