OpenSSH 6.9p1 Authentication Bypass / Use-After-Free

2015-08-15 / 2015-08-17
Credit: Blue Frost
Risk: High
Local: No
Remote: Yes

Blue Frost Security GmbH research(at) BFS-SA-2015-002 13-August-2015 ________________________________________________________________________________ Affected Product: OpenSSH ( Affected Version: Portable versions <= 6.9p1 Vulnerability: Vulnerabilities in PAM Privilege Separation Code ________________________________________________________________________________ I. Impact Two vulnerabilities were identified in the PAM privilege separation code. One of them (III) allows remote attackers who previously achieved remote code execution within the unprivileged pre-auth sandbox process to perform a successful authentication as an arbitrary user (e.g. root) and thus impersonate other users. The only additional prerequisite is any valid (possibly low-privileged) user account which can be used to login into the system via SSH. ________________________________________________________________________________ II. Background OpenSSH implements privilege separation which was introduced with version 5.9. Privilege separation is a generic approach which splits the code into two processes: An unprivileged child process and a privileged monitor process. The unprivileged child does most of the work and in particular processes all the network data. The monitor process communicates with the unprivileged child process and performs all the operations which require higher privileges. The idea of this design is to prevent programming errors in the unprivileged parts from compromising the whole application and thus prevent a full system compromise. A good technical overview can be found in the paper "Preventing Privilege Escalation" by Niels Provos et al. ( The unprivileged child process and privileged monitor process communicate via a socketpair. Several different monitor request and answer types are defined which can be used to exchange messages between the two processes. The complete list can be found in the mon_dispatch_proto{15,20} and mon_dispatch_postauth{15,20} structures defined in monitor.c. Monitor requests have certain flags assigned which can restrict when and how requests are accepted by the monitor. E.g. the flag MON_ONCE determines that a request can only be sent once and is disabled after it was received for the first time in the monitor. The MON_AUTH flag determines that a request is related to the authentication process. The complete list of flags can be found in the monitor.c file as well. Not all defined requests are permitted in every state of the SSH protocol. In order to control which requests are permitted, the functions monitor_permit() and monitor_permit_authentications() are used. The function monitor_permit() can be used to enable or disable a certain message while the function monitor_permit_authentications() enables or disables all authentication related messages which have the MON_AUTH flag set. When a request is received by the monitor which is currently not allowed the monitor process terminates by calling the fatal() function. ________________________________________________________________________________ III. PAM Authentication Bypass in Privilege Separation When PAM support is enabled in the portable version of OpenSSH, a few additonal monitor requests are enabled which can be found in the monitor.c file: #ifdef USE_PAM {MONITOR_REQ_PAM_START, MON_ONCE, mm_answer_pam_start}, {MONITOR_REQ_PAM_ACCOUNT, 0, mm_answer_pam_account}, {MONITOR_REQ_PAM_INIT_CTX, MON_ISAUTH, mm_answer_pam_init_ctx}, {MONITOR_REQ_PAM_QUERY, MON_ISAUTH, mm_answer_pam_query}, {MONITOR_REQ_PAM_RESPOND, MON_ISAUTH, mm_answer_pam_respond}, {MONITOR_REQ_PAM_FREE_CTX, MON_ONCE|MON_AUTHDECIDE, mm_answer_pam_free_ctx}, #endif Before any PAM-related monitor requests are sent, the unprivileged child process sends the MONITOR_REQ_PWNAM request to verify that the username received from the network represents a valid user. The monitor responds with the corresponding passwd struct entry if the user exists and additionally caches the username and passwd struct entry in the current monitor authentication context (struct Authctxt *authctxt). PAM authentication then starts with the unprivileged child process sending the MONITOR_REQ_PAM_START request which tells the monitor to open a new authentication transaction for the current user by calling the PAM API function pam_start(). The next PAM-related monitor request sent by the child process is MONITOR_REQ_PAM_INIT_CTX which initializes [2] the current PAM authentication context in the monitor. int mm_answer_pam_init_ctx(int sock, Buffer *m) { debug3("%s", __func__); authctxt->user = buffer_get_string(m, NULL); [1] sshpam_ctxt = (sshpam_device.init_ctx)(authctxt); [2] sshpam_authok = NULL; buffer_clear(m); [...] Interestingly the child process sends the current username as part of this request once again to the monitor and the monitor overwrites the previously stored username in the monitor authentication context with the received username [1]. During a normal PAM authentication the same username is sent, so this doesn't represent a problem. However an attacker who already compromised the unprivileged pre-auth child process could send a different username in this case. This could lead to a situation where the username stored in the monitor authentication context would not match the stored passwd struct entry. The remaining PAM authentication only relies on the stored username. The user which is actually logged in after a successful authentication is on the contrary only identified by the stored passwd struct entry. This allows an attacker who compromised the unprivileged child process to first send any desired username (e.g. root) with the MONITOR_REQ_PWNAM request which would store the corresponding passwd struct entry in the authentication context. Subsequently he would send a different username for whom he's in posession of the password and perform a valid PAM authentication with that user. Once the authentication was successful, the attacker is not logged in with the user he used for the authentication, but with the username he specified in the initial MONITOR_REQ_PWNAM request. ________________________________________________________________________________ IV. Use-After-Free in PAM Privilege Separation The monitor requests for PAM authentication which can be send by the unprivileged child process represent additional attack surface. But not only implementation flaws in the handlers have to be considered. Also the order in which requests are send could result in unexpected program states and thus lead to interesting vulnerabilities. One such vulnerability can be found in the handler for the MONITOR_REQ_PAM_FREE_CTX request: int mm_answer_pam_free_ctx(int sock, Buffer *m) { debug3("%s", __func__); (sshpam_device.free_ctx)(sshpam_ctxt); [3] buffer_clear(m); mm_request_send(sock, MONITOR_ANS_PAM_FREE_CTX, m); auth_method = "keyboard-interactive"; auth_submethod = "pam"; return (sshpam_authok == sshpam_ctxt); } At [3] the free_ctx function pointer of the KbdintDevice structure is called which is set to sshpam_free_ctx(). That function can be found in auth-pam.c: static void sshpam_free_ctx(void *ctxtp) { struct pam_ctxt *ctxt = ctxtp; debug3("PAM: %s entering", __func__); sshpam_thread_cleanup(); free(ctxt); [4] /* * We don't call sshpam_cleanup() here because we may need the PAM * handle at a later stage, e.g. when setting up a session. It's * still on the cleanup list, so pam_end() *will* be called before * the server process terminates. */ } As can be seen this frees the current PAM context memory pointed to by the passed sshpam_ctxt pointer [4]. The flag MON_ONCE is set for the MONITOR_REQ_PAM_FREE_CTX monitor request which prevents a malicious child process from calling it multiple times which would otherwise lead to an obvious double free vulnerability. However it is still possible for a compromised child process to first send the MONITOR_REQ_PAM_FREE_CTX request which will free the PAM context and then use any of the other PAM monitor requests which leads to a use-after-free condition. The freed structure is defined in auth-pam.c as follows: struct pam_ctxt { sp_pthread_t pam_thread; int pam_psock; int pam_csock; int pam_done; }; As can be seen the structure does not contain any pointers or length fields which would be ideal candidates for the exploitation of this use-after-free bug. However it stores two other interesting values, which are the pam_psock and pam_csock file descriptors. These file descriptors identify the receiving and sending socket to the PAM authentication system respectively. In order to exploit the use-after-free condition, you could try to replace the freed memory in the monitor process with a fake structure which would have pam_psocks and pam_csock being set to the file descriptors used for the communication with the unprivileged child process. This way the monitor would send all PAM authentication requests directly to the unprivileged child process which could then impersonate the PAM subsystem and e.g. allow authentication for any user without a password. The problem which most probably prevents the described way of exploitation is that only monitor requests which have the MON_AUTHDECIDE flag set are allowed to make an authentication decision: if (!(ent->flags & MON_AUTHDECIDE)) fatal("%s: unexpected authentication from %d", __func__, ent->type); For PAM only the MONITOR_REQ_PAM_FREE_CTX request has this flag set. Additionally the request has the MON_ONCE flag set which means we can send it to the monitor only a single time. Since we initially need to send this request to trigger the freeing of the PAM context, followed by other monitor requests for the actual authentication, we are running into the described MON_AUTHDECIDE check which prevents a successful authentication. We didn't find a way to successfully exploit this use-after-free condition for anything interesting. But a creative mind might eventually find a useful scenario. ________________________________________________________________________________ V. Mitigation Both vulnerabilities were fixed in OpenSSH 7.0 which should be installed to resolve the issues. ________________________________________________________________________________ VI. Disclosure Timeline - 2015-08-10 Vulnerabilities reported to - 2015-08-11 OpenSSH 7.0 is released which fixes the issues ________________________________________________________________________________ Credit: Bugs found by Moritz Jodeit of Blue Frost Security GmbH. ________________________________________________________________________________ Unaltered electronic reproduction of this advisory is permitted. For all other reproduction or publication, in printing or otherwise, contact for permission. Use of the advisory constitutes acceptance for use in an "as is" condition. All warranties are excluded. In no event shall Blue Frost Security be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages, even if Blue Frost Security has been advised of the possibility of such damages. Copyright 2015 Blue Frost Security GmbH. All rights reserved. Terms of use apply.

Vote for this issue:


Thanks for you vote!


Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.

(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2023,


Back to Top