CVE-2014-0074

https://github.com/apache/shiro/commit/f988846207f98c98ff24213ee9063798ea5d9b6c

https://xz.aliyun.com/t/11633#toc-6

https://su18.org/post/shiro-1/#cve-2014-0074

0x00 概述

当程序使用LDAP服务器并启用非身份验证绑定时,远程攻击者可借助空的用户名或密码利用该漏洞绕过身份验证。

影响范围:

  • shiro 1.x < 1.2.3

0x01 漏洞分析

  • ldap unauthenticated bind enabled 的情况下,可以使用空用户名+任意密码进行认证。
  • ldap allow anonymous 的情况下,可以空用户名+空密码的匿名访问进行认证。

0x02 修复分析

漏洞修复增加了对用户名存在而密码为空的校验,根据 su18 师傅的文章描述,该项漏洞实际上并不能算作是 shiro 的漏洞,而是 ldap 和 shiro 之间的联动配置问题,但是 shiro 发放了 cve,并进行了修复,但是修复的代码和提交的漏洞场景没有什么关系,只是禁止了用户名存在而密码为空的情况。

 /**
     * Validates the configuration in the JNDI <code>environment</code> settings and throws an exception if a problem
     * exists.
     * <p/>
     * This implementation will throw a {@link AuthenticationException} if the authentication mechanism is set to
     * 'simple', the principal is non-empty, and the credentials are empty (as per
     * <a href="http://tools.ietf.org/html/rfc4513#section-5.1.2">rfc4513 section-5.1.2</a>).
     *
     * @param environment the JNDI environment settings to be validated
     * @throws AuthenticationException if a configuration problem is detected
     */
    private void validateAuthenticationInfo(Hashtable<String, Object> environment)
        throws AuthenticationException
    {
        // validate when using Simple auth both principal and credentials are set
        if(SIMPLE_AUTHENTICATION_MECHANISM_NAME.equals(environment.get(Context.SECURITY_AUTHENTICATION))) {

            // only validate credentials if we have a non-empty principal
            if( environment.get(Context.SECURITY_PRINCIPAL) != null &&
                StringUtils.hasText( String.valueOf( environment.get(Context.SECURITY_PRINCIPAL) ))) {

                Object credentials = environment.get(Context.SECURITY_CREDENTIALS);

                // from the FAQ, we need to check for empty credentials:
                // http://docs.oracle.com/javase/tutorial/jndi/ldap/faq.html
                if( credentials == null ||
                    (credentials instanceof byte[] && ((byte[])credentials).length <= 0) || // empty byte[]
                    (credentials instanceof char[] && ((char[])credentials).length <= 0) || // empty char[]
                    (String.class.isInstance(credentials) && !StringUtils.hasText(String.valueOf(credentials)))) {

                    throw new javax.naming.AuthenticationException("LDAP Simple authentication requires both a "
                                                                       + "principal and credentials.");
                }
            }
        }
    }