目录

SmartBi 未授权远程代码执行

前言

今年七月份的时候SmartBi爆出来一个未授权rce,我就分析了一波,然后跟着继续挖了一下,挖到一些授权的xxe注入和后台rce,紧接着七月底又爆出一个获取token的洞,赶紧去分析了一波看看对补丁有没有办法绕过,果然找到了绕补丁的方法,结合着原来挖到的后台rce,两个洞结合可以未授权rce

漏洞分析

权限绕过

是在MonitorService类中

smartbix.datamining.service.MonitorService#setEngineAddress中,配置不需要权限,即可访问,在该函数中,可以设置Engine的地址

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/40104487-4107-0edc-1cfe-017da722fb19.png

然后在同一类中的getToken函数中,获取token放入请求包,向前面设置的Engine的地址发送请求包,请求路径为:/api/v1/configs/engine/smartbitoken,如果返回json格式的返回包,会返回true

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/3ae24be8-6d3f-23e6-8862-848d36c1221c.png

拿到token后在通过,同一类中的loginByToken函数即可获取JSESSIONID

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/8e100456-47ad-044a-4426-1b56a6699a7c.png

利用方式就是,构造一个假的Engine地址,可以接收token,再去获取JSESSIONID即可

具体怎么利用就不细说了,如下图所示,返回包返回true说明JSESSIONID可以拿着去登录后台了

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/3621fa84-cf11-5176-2b06-90dc82593569.png

官方的修复方式是:对setEngineAddress路由加了权限校验

另一个权限绕过

MonitorService类中,和上一个权限绕过类似使用了另一个路由,通过另一个路由触发setEngineAddress路由修改地址

smartbix.datamining.service.MonitorService#setAddress中,我们传进去的address为des加密过的json字符串,进入后先解密然后转换为ObjectNode可以理解为就是json字符串,接着往下走,判断obj中是否有c_address字段,然后判断type字段的值是否为experiment,最后取c_address字段的value,和EngineApi.address("engine-address")对比,一样的话进入到setEngineAddress路由中,且值为u_address字段的value,这里默认的engine-address地址为:http://localhost:8899

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/6307eee1-5b9c-5f6e-c3eb-20cc92d4f5c6.png

然后可以通过CommonUtil类进行des加密

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/34e80b01-48db-c5dc-c29b-cce2a134405d.png

设置成功,后续和上次权限绕过一致

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/d822d4be-9067-695e-c1a5-f1d65a3dfbaf.png

后台rce

该漏洞为JDBC 进行的JNDI注入 入口点是在SyncServlet中,type为sqldictsync时进入分支

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/6543e4f9-2761-cad4-1c27-11da10c10f22.png

进入smartbi.freequery.sync.SyncResources#synchronize()中,基本确定为JDBC连接,就看后续有没有对参数的限制和具体连接方式了

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/1ebe3334-8283-a3fb-d6e0-2d2455d09cb3.png

进入到smartbi.util.DbUtil#getConnection()中,url参数是从drvInfo中取值的,跟进到translateDriverInfo

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/ca6b0f1b-09a2-047b-4e30-0ee20c79bc17.png

smartbi.util.DbUtil#translateDriverInfo()中,就是通过传进来的dbType确定数据库和驱动,这里还是使用DB2进行JNDI注入,这里红框内是最终拼接而成的url,这里有个我踩坑的地方就是dbName后还拼接了:deferPrepares=false;(这个参数就是在执行sql语句前进行预编译),没注意到:,导致后面进行jndi注入时可以接收到jndi请求,但是一直没打成功,还是要细心点,直接在dbName最后加个;即可

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/f9284a40-3c71-a99d-55cf-e08af9b0f62d.png

最后进入到smartbi.connectionpool.ConnectionPool#getConnection()中,进行了对JNDI注入的验证,第一个if条件是判断url里是否有:clientrerouteserverlistjndiname=有的话就抛出异常,这里直接使用a:a=a;clientRerouteServerListJNDIName=绕过即可,第二个检验url开头是否为JNDI:还有java:的校验,不了解这是在防御什么,反正没什么影响,接着就直接进行连接了

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/f6b29d80-bd41-7e19-39c2-07d9e19d99dd.png

最后两个洞结合即可

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/e4a93050-6b84-f796-58a0-54570ce35eff.png