cursor作为AI驱动的代码生成神奇,目前在国外已经是非常流行了。已经开始有一部分人开始尝试用cursor完全的接替原本的代码工作。
本文旨在介绍如果利用cursor去解读代码并分析漏洞。当我们更加深入了解cursor时,会被它强大的功能所震撼。
首先cursor无法直接帮我们去分析整个漏洞的漏洞原理和利用链,cursor的作用是辅助我们去分析这个漏洞。
我们需要自己很清楚要做什么,然后把一步步拆分得很细给到cursor去分析,借助cursor我们可以很快的分析并得到我们想要的答案。
以18.12.12为例,官方的漏洞修复补丁的文档在:
[OFBIZ-13006] [SECURITY] (CVE-2024-32113) Path traversal leading to RCE - ASF JIRA
补丁修复在这里:
提问:
- 为什么目录遍历能导致权限绕过
- 怎么触发代码执行
以下问题均是个人的提问 + cursor的回答。
0x01 提问一
主要目录结构
1、 framework/ - 框架核心目录
- **base/** - 基础组件
- **catalina/** - Tomcat/Catalina 相关配置
- **common/** - 通用功能组件
- **entity/** - 实体/数据模型相关
- **service/** - 服务层组件
- **webapp/** - Web应用组件
- **webtools/** - Web工具组件
- **start/** - 启动相关代码
关键配置文件
1、 组件配置
-
ofbiz-component.xml
- 每个组件的配置文件,定义了组件的基本信息、资源加载器等 -
component-load.xml
- 定义组件加载顺序
2、Web相关配置 -
web.xml
- Web应用配置文件 -
controller.xml
- Web控制器配置 -
entityengine.xml
- 实体引擎配置
特点分析
1、 模块化设计
- 每个功能模块都是独立的组件
- 通过 component-load.xml 控制加载顺序
<load-component component-location="base"/>
<load-component component-location="entity"/>
<load-component component-location="security"/>
<load-component component-location="datafile"/>
2、 MVC架构
- webapp 模块处理Web层
- entity 模块处理数据层
- service 模块处理业务逻辑层
3、 配置驱动 - 大量使用XML配置文件
- 支持热部署和动态加载
4、 扩展性 - 插件化架构
- 组件可以独立开发和部署
这是一个典型的Java企业级应用框架,采用组件化设计,具有良好的可扩展性和维护性。每个组件都有明确的职责划分,通过配置文件进行灵活组装。
0x02 提问二
让我帮你解析一下OFBiz中的Web路由控制机制:
1. 路由控制的核心组件
1、 ControlFilter - 请求过滤器
- 负责URL白名单过滤
- 处理重定向逻辑
- 安全检查
2、 ControlServlet - 主控制器 - 处理所有Web请求的核心Servlet
- 通过web.xml中的配置进行URL映射
2. 路由定义方式
1、 XML配置方式
<request-map uri="view">
<security https="true" auth="false"/>
<response name="success" type="view" value="main"/>
</request-map>
<request-map uri="chain">
<event type="java" path="org.apache.ofbiz.webapp.event.TestEvent" invoke="test"/>
<response name="success" type="request" value="/view"/>
<response name="error" type="view" value="error"/>
</request-map>
<!-- End Utility Requests -->
<request-map uri="main">
<security https="true" auth="false"/>
<response name="success" type="view" value="main"/>
</request-map>
这个示例展示了如何在controller.xml中定义请求映射:
-
request-map
定义URL路径 -
security
定义安全要求 -
response
定义响应行为
2、 URL路径控制
<servlet-mapping>
<servlet-name>ControlServlet</servlet-name>
<url-pattern>/control/*</url-pattern>
</servlet-mapping>
所有请求都通过ControlServlet进行处理,基本格式为:
-
/control/*
- 所有控制请求的基础路径
3、 组件挂载点定义
<webapp name="webtools"
title="WebTools"
position="12"
server="default-server"
location="webapp/webtools"
base-permission="OFBTOOLS,WEBTOOLS"
mount-point="/webtools"/>
每个webapp组件需要定义:
-
name
- 组件名称 -
mount-point
- URL挂载点 -
location
- 物理位置 -
base-permission
- 基础权限要求
3. 请求处理流程
1、 请求首先经过ControlFilter:
- 检查URL是否在允许列表中
- 处理重定向逻辑
- 执行安全检查
2、 然后由ControlServlet处理: - 解析请求URL
- 查找对应的request-map定义
- 执行相应的处理逻辑
- 返回定义的响应
4. 安全控制
路由定义中包含多层安全控制:
- URL白名单过滤
- 基于权限的访问控制
- HTTPS要求
- 认证要求
这种设计提供了:
- 集中式的请求处理
- 声明式的路由配置
- 灵活的安全控制
- 统一的请求处理流程
0x03 提问三
让我帮你分析这个请求的处理流程:
1. 请求入口
当访问 https://127.0.0.1:8443/webtools/control/main
时,请求会按以下顺序处理:
1、 首先经过 web.xml 中配置的过滤器链:
<filter>
<display-name>ControlFilter</display-name>
<filter-name>ControlFilter</filter-name>
<filter-class>org.apache.ofbiz.webapp.control.ControlFilter</filter-class>
<init-param>
<param-name>allowedPaths</param-name>
<param-value>/ping.txt:/error:/control:/select:/index.html:/index.jsp:/default.html:/default.jsp</param-value>
</init-param>
<init-param>
<param-name>redirectPath</param-name>
<param-value>/control/main</param-value>
</init-param>
</filter>
<filter>
<display-name>CacheFilter</display-name>
<filter-name>CacheFilter</filter-name>
<filter-class>org.apache.ofbiz.base.util.CacheFilter</filter-class>
</filter>
<filter>
<display-name>ContextFilter</display-name>
<filter-name>ContextFilter</filter-name>
<filter-class>org.apache.ofbiz.webapp.control.ContextFilter</filter-class>
</filter>
<filter>
<display-name>SameSiteFilter</display-name>
<filter-name>SameSiteFilter</filter-name>
<filter-class>org.apache.ofbiz.webapp.control.SameSiteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ControlFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CacheFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>ContextFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>SameSiteFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2、 然后到达 ControlServlet:
<servlet>
<description>Main Control Servlet</description>
<display-name>ControlServlet</display-name>
<servlet-name>ControlServlet</servlet-name>
<servlet-class>org.apache.ofbiz.webapp.control.ControlServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>ControlServlet</servlet-name>
<url-pattern>/control/*</url-pattern>
</servlet-mapping>
2. 权限验证流程
1、 ControlFilter 进行第一层验证:
- 检查请求路径是否在允许列表中
-
/control/main
在允许列表中:
<param-name>allowedPaths</param-name>
<param-value>/ping.txt:/error:/control:/select:/index.html:/index.jsp:/default.html:/default.jsp</param-value>
</init-param>
2、 检查登录状态和权限:
GenericValue userLogin = (GenericValue) httpRequest.getSession().getAttribute("userLogin");
if (!LoginWorker.hasBasePermission(userLogin, httpRequest)) { // Allows UEL and FlexibleString (OFBIZ-12602)
if (!GenericValue.getStackTraceAsString().contains("ControlFilterTests")
&& null == System.getProperty("SolrDispatchFilter") // Allows Solr tests
&& SecurityUtil.containsFreemarkerInterpolation(httpRequest, httpResponse, requestUri)) {
return;
}
}
3、 controller.xml 中的权限配置:
<request-map uri="main">
<security https="true" auth="false"/>
<response name="success" type="view" value="main"/>
</request-map>
3. 结论
对于 /webtools/control/main
这个接口:
1、 是否需要登录:
- 根据 controller.xml 中的配置
auth="false"
,这个接口不需要登录就可以访问
2、 安全要求: - 需要 HTTPS:
https="true"
- 在白名单中:
/control
在 allowedPaths 列表中
3、 处理流程: - 请求首先经过 ControlFilter 的路径验证
- 然后由 ControlServlet 处理具体的业务逻辑
- 最后根据 controller.xml 的配置返回 "main" 视图
所以这是一个公开的入口页面,不需要登录授权,但必须使用 HTTPS 访问。
0x04 提问四
让我分析一下这个安全漏洞(OFBIZ-13006):
漏洞描述
这是一个URL规范化绕过漏洞。在修复前的代码中:
// normalize to remove ".." special name usage to bypass webapp filter
try {
requestUri = new URI(requestUri).normalize().toString();
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
只对请求的URI进行了简单的normalize()处理,但没有对比规范化前后的URL是否一致。攻击者可以利用这个漏洞构造特殊的URL来绕过安全检查。
漏洞利用方式
攻击者可以通过以下方式绕过URL白名单检查:
1、 路径遍历攻击:
/webtools/control/../../../etc/passwd
2、 URL编码绕过:
/webtools/control/%2e%2e/%2e%2e/sensitive-data
3、 特殊字符组合:
/webtools/control/./././../../../confidential/
官方修复方案
新版本增加了额外的URL验证逻辑:
if (req.getRequestURL() != null) { // Allow tests with Mockito, ControlFilterTests
String url = new URI(req.getRequestURL().toString()).normalize().toString();
if (!req.getRequestURL().toString().equals(url)) {
throw new RuntimeException();
}
}
主要修复点:
- 获取完整的RequestURL而不仅仅是URI
- 对规范化前后的URL进行严格比对
- 如果不一致则直接抛出异常
安全建议
- 升级到最新版本
- 确保所有URL处理都经过严格的规范化和验证
- 实施严格的白名单机制
- 避免直接使用未经处理的用户输入作为文件路径
这是一个典型的输入验证不充分导致的安全漏洞,通过增加URL规范化验证得到了修复。
0x05 提问五
是的,这个漏洞确实可能被用来绕过登录授权。让我分析一下:
1、 漏洞存在的位置:
// normalize to remove ".." special name usage to bypass webapp filter
try {
requestUri = new URI(requestUri).normalize().toString();
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
这段代码只是简单地对URL进行normalize()处理,但没有验证normalize前后的URL是否一致。
2、 攻击场景分析:
- 假设存在一个需要授权的接口
/webtools/control/entitymaint
- 在controller.xml中的配置:
<event type="service-stream" invoke="serviceStreamTest"/>
<response name="success" type="none"/>
<response name="error" type="none"/>
</request-map>
要求auth="true"
,即需要登录。
3、 攻击路径:
- 攻击者可以构造URL:
/webtools/control/main/../entitymaint
-
main
接口配置为auth="false"
- 经过normalize()后变成
/webtools/control/entitymaint
- 由于normalize()前的URL以
/control/main
开头,会被allowedPaths白名单放行 - 最终可能访问到需要授权的entitymaint接口
4、 漏洞原因: - URL规范化检查不完整
- 白名单验证在规范化之后
- 没有验证规范化前后URL的一致性
5、 影响范围: - 可能绕过任何需要授权的接口
- 可能访问到受保护的资源
- 可能导致权限提升
这就是为什么官方在后续版本中增加了对比规范化前后URL是否一致的检查,以修复这个安全漏洞。
至此,我们解决了第一个问题,为什么目录遍历可以绕过登录授权
接下来我们引导cursor帮我们解决第二个问题,怎么让利用这个漏洞造成rce。
0x06 提问六
让我帮你分析一下ProgramExport这个接口的功能:
从controller.xml中可以看到,ProgramExport是一个视图映射(view-map),它指向了component://webtools/widget/EntityScreens.xml
中的ProgramExport屏幕定义。
通过查看相关代码,这个接口主要实现了以下功能:
1、 它是一个实体(Entity)导出工具,允许用户导出实体定义为EOModel格式
2、 从WebToolsServices.java中可以看到具体实现:
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "WebtoolsEomodelFullPathIsNotWriteable", UtilMisc.toMap("eomodeldFullPath", eomodeldFullPath), locale));
}
Set<String> entityNames = new TreeSet<String>();
if (UtilValidate.isNotEmpty(entityPackageNameOrig)) {
Set<String> entityPackageNameSet = new HashSet<String>();
entityPackageNameSet.addAll(StringUtil.split(entityPackageNameOrig, ","));
Debug.logInfo("Exporting with entityPackageNameSet: " + entityPackageNameSet, module);
Map<String, TreeSet<String>> entitiesByPackage = reader.getEntitiesByPackage(entityPackageNameSet, null);
for (Map.Entry<String, TreeSet<String>> entitiesByPackageMapEntry: entitiesByPackage.entrySet()) {
entityNames.addAll(entitiesByPackageMapEntry.getValue());
}
} else if (UtilValidate.isNotEmpty(entityGroupId)) {
Debug.logInfo("Exporting entites from the Group: " + entityGroupId, module);
entityNames.addAll(EntityGroupUtil.getEntityNamesByGroup(entityGroupId, dctx.getDelegator(), false));
} else {
entityNames.addAll(reader.getEntityNames());
}
Debug.logInfo("Exporting the following entities: " + entityNames, module);
// remove all view-entity
Iterator<String> filterEntityNameIter = entityNames.iterator();
while (filterEntityNameIter.hasNext()) {
String entityName = filterEntityNameIter.next();
ModelEntity modelEntity = reader.getModelEntity(entityName);
if (modelEntity instanceof ModelViewEntity) {
filterEntityNameIter.remove();
}
}
// write the index.eomodeld file
Map<String, Object> topLevelMap = new HashMap<String, Object>();
topLevelMap.put("EOModelVersion", "\"2.1\"");
List<Map<String, Object>> entitiesMapList = new LinkedList<Map<String,Object>>();
topLevelMap.put("entities", entitiesMapList);
for (String entityName: entityNames) {
Map<String, Object> entitiesMap = new HashMap<String, Object>();
entitiesMapList.add(entitiesMap);
entitiesMap.put("className", "EOGenericRecord");
entitiesMap.put("name", entityName);
}
UtilPlist.writePlistFile(topLevelMap, eomodeldFullPath, "index.eomodeld", true);
// write each <EntityName>.plist file
for (String curEntityName: entityNames) {
ModelEntity modelEntity = reader.getModelEntity(curEntityName);
UtilPlist.writePlistFile(modelEntity.createEoModelMap(entityNamePrefix, datasourceName, entityNames, reader), eomodeldFullPath, curEntityName +".plist", true);
}
Integer entityNamesSize = entityNames.size();
return ServiceUtil.returnSuccess(UtilProperties.getMessage(resource, "WebtoolsEomodelExported", UtilMisc.toMap("entityNamesSize", entityNamesSize.toString(), "eomodeldFullPath", eomodeldFullPath), locale));
} catch (UnsupportedEncodingException e) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "WebtoolsEomodelSavingFileError", UtilMisc.toMap("errorString", e.toString()), locale));
} catch (FileNotFoundException e) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "WebtoolsEomodelFileOrDirectoryNotFound", UtilMisc.toMap("errorString", e.toString()), locale));
} catch (GenericEntityException e) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "WebtoolsEomodelErrorGettingEntityNames", UtilMisc.toMap("errorString", e.toString()), locale));
}
}
主要功能包括:
- 允许用户指定导出路径(eomodeldFullPath)
- 可以选择性导出指定的实体包(entityPackageNameOrig)或实体组(entityGroupId)
- 会过滤掉视图实体(view-entity)
- 将实体定义导出为EOModel格式,包括:
- 生成index.eomodeld文件
- 为每个实体生成对应的.plist文件
- 包含实体的属性、关系等信息
3、 这个功能主要用于:
- 实体建模和设计
- 系统集成
- 数据结构文档化
4、 安全性:
从controller.xml中可以看到这个接口需要认证:
<security https="true" auth="true"/>
所以这是一个用于导出OFBiz实体定义的管理工具接口,主要面向开发和系统管理人员使用。
0x07 提问七
让我帮你分析一下ProgramExport接口的代码逻辑:
1、 路由配置:
在controller.xml中定义了ProgramExport的视图映射:
<view-map name="ProgramExport" type="screen" page="component://webtools/widget/EntityScreens.xml#ProgramExport"/>
2、 安全控制:
- 需要HTTPS访问
- 需要用户认证
- 通过web.xml中的过滤器进行安全控制:
- ControlFilter
- ContextFilter
- SameSiteFilter
3、 主要实现逻辑:
在ProgramExport.groovy中:
if (UtilValidate.isNotEmpty(groovyProgram)) {
try {
// Check if a webshell is not uploaded but allow "import"
if (!SecuredUpload.isValidText(groovyProgram, ["import"])) {
logError("================== Not executed for security reason ==================")
request.setAttribute("_ERROR_MESSAGE_", "Not executed for security reason")
return
}
shell.parse(groovyProgram)
shell.evaluate(groovyProgram)
recordValues = shell.getVariable("recordValues")
xmlDoc = GenericValue.makeXmlDocument(recordValues)
context.put("xmlDoc", xmlDoc)
} catch(MultipleCompilationErrorsException e) {
request.setAttribute("_ERROR_MESSAGE_", e)
return
} catch(groovy.lang.MissingPropertyException e) {
request.setAttribute("_ERROR_MESSAGE_", e)
return
} catch(IllegalArgumentException e) {
request.setAttribute("_ERROR_MESSAGE_", e)
return
} catch(NullPointerException e) {
request.setAttribute("_ERROR_MESSAGE_", e)
return
} catch(Exception e) {
request.setAttribute("_ERROR_MESSAGE_", e)
return
}
主要功能:
- 接收groovy程序代码作为输入
- 进行安全检查,防止webshell注入
- 执行groovy代码
- 获取recordValues变量
- 将结果转换为XML文档
4、 视图渲染:
使用ProgramExport.ftl模板渲染结果:
<div class="screenlet">
<div class="screenlet">
<div class="screenlet-title-bar">
<h3>${uiLabelMap.WebtoolsEntityXMLRepresentation}</h3>
</div>
<div class="screenlet-body">
<p>
<#if xmlDoc?has_content>
${Static["org.apache.ofbiz.base.util.UtilXml"].writeXmlDocument(xmlDoc)?replace("\n", "<br />")?replace(" ", " ")}
</#if>
</p>
</div>
</div>
主要功能:
- 显示XML格式的实体数据
- 使用screenlet布局
- 格式化XML输出
5、 错误处理: - 捕获各种异常并返回错误信息
- 包括编译错误、属性缺失、参数错误等
- 通过request.setAttribute设置错误消息
6、 安全特性: - 要求HTTPS
- 需要认证
- 输入验证
- 防止webshell注入
- Session超时控制(60分钟)
这是一个用于导出实体数据的管理工具接口,主要面向开发和系统管理人员使用,具有较完善的安全控制机制。
0x08 提问八
我来说明如何调用ProgramExport接口执行groovy代码:
1、 首先这是一个需要认证的HTTPS接口,所以需要先登录系统获取认证。
2、 接口调用方式:
- HTTP Method: POST
- URL:
https://<host>/webtools/control/ProgramExport
- Content-Type: application/x-www-form-urlencoded
3、 请求参数:
从MiscForms.xml可以看到表单定义:
<form name="ProgramExport" type="single" target="ProgramExport" default-map-name="parameters">
<field name="groovyProgram" required-field="true">
<textarea cols="120" rows="20" />
</field>
<field name="submitButton" title="${uiLabelMap.CommonRun}"><submit button-type="button"/></field>
</form>
需要提交的参数:
- groovyProgram: 要执行的groovy代码(必填)
4、 示例代码:
// 查询Product实体的前3条记录
import org.apache.ofbiz.entity.util.EntityFindOptions
EntityFindOptions findOptions = new EntityFindOptions()
findOptions.setMaxRows(3)
List products = delegator.findList("Product", null, null, null, findOptions, false)
if (products != null) {
recordValues.addAll(products)
}
5、 响应结果:
- 成功: 返回XML格式的查询结果
- 失败: 返回错误信息
6、 安全限制:
// Check if a webshell is not uploaded but allow "import"
if (!SecuredUpload.isValidText(groovyProgram, ["import"])) {
logError("================== Not executed for security reason ==================")
request.setAttribute("_ERROR_MESSAGE_", "Not executed for security reason")
return
}
代码会经过安全检查,防止webshell注入。
7、 curl调用示例:
curl -X POST \
https://localhost:8443/webtools/control/ProgramExport \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'groovyProgram=import org.apache.ofbiz.entity.util.EntityFindOptions;
EntityFindOptions findOptions = new EntityFindOptions();
findOptions.setMaxRows(3);
List products = delegator.findList("Product", null, null, null, findOptions, false);
if (products != null) {
recordValues.addAll(products);
}'
注意:
- 需要HTTPS访问
- 需要先登录获取认证
- groovy代码要符合安全规范
- 建议在测试环境使用,生产环境谨慎使用
0x09 提问九
我理解您是出于学习目的分析漏洞。从代码分析来看:
1、 ControlFilter.java中的路径处理:
if (httpRequest.getAttribute(FORWARDED_FROM_SERVLET) == null && !allowedPaths.isEmpty()) {
// check to make sure the requested url is allowed
// get the request URI without the webapp mount point
String requestUri = httpRequest.getRequestURI().substring(httpRequest.getContextPath().length());
// normalize to remove ".." special name usage to bypass webapp filter
try {
requestUri = new URI(requestUri).normalize().toString();
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
这段代码在处理路径规范化时存在问题。
2、 ProgramExport的定义:
<request-map uri="ProgramExport">
<security https="true" auth="true"/>
<response name="success" type="view" value="ProgramExport"/>
<response name="error" type="view" value="ProgramExport"/>
</request-map>
3、 根据上述分析,理论上可以通过以下方式访问:
https://127.0.0.1:8443/webtools/control/main/../ProgramExport
4、 需要注意的安全限制:
// Check if a webshell is not uploaded but allow "import"
if (!SecuredUpload.isValidText(groovyProgram, ["import"])) {
logError("================== Not executed for security reason ==================")
request.setAttribute("_ERROR_MESSAGE_", "Not executed for security reason")
return
}
这里有安全检查机制。
建议:
- 及时修复此漏洞
- 加强路径验证
- 实施多层安全防护
- 监控异常访问
作为安全研究人员,我们应该负责任地分析和报告漏洞。
利用cursor我们可以很轻易的分析一个漏洞。甚至这个分析过程,我们不用一个小时就能完成。但由于涉及合规问题,cursor无法直接给我们完整的poc。
没有评论