
Java Web安全之代码审计信息安全的75%发生在Web应用而非网络层。本文内容主要以Java Web安全-代码审计为中心展开。一、JavaWeb 安全基础1. 何为代码审计?通俗的说Java代码审计就是通过审计Java代码来发现Java应用程序自身中存在的安全问题由于Java本身是编译型语言所以即便只有class文件的情况下我们依然可以对Java代码进行审计。对于未编译的Java源代码文件我们可以直接阅读其源码而对于已编译的class或者jar文件我们就需要进行反编译了。Java代码审计其本身并无多大难度只要熟练掌握审计流程和常见的漏洞审计技巧就可比较轻松的完成代码审计工作了。但是Java代码审计的方式绝不仅仅是使用某款审计工具扫描一下整个Java项目代码就可以完事了一些业务逻辑和程序架构复杂的系统代码审计就非常需要审计者掌握一定的Java基础并具有具有一定的审计经验、技巧甚至是对Java架构有较深入的理解和实践才能更加深入的发现安全问题。本文将分为多章节来讲述Java代码审计需要掌握的前置知识以及Java代码审计的流程、技巧。2. 准备环境和辅助工具在开始Java代码审计前请自行安装好Java开发环境建议使用MacOS、Ubuntu操作系统。所谓“工欲善其事必先利其器”合理的使用一些辅助工具可以极大的提供我们的代码审计的效率和质量强烈推荐下列辅助工具1.Jetbrains IDEA(IDE)2.Sublime text(文本编辑器)3.JD-GUI(反编译)4.Fernflower(反编译)5.Bytecode-Viewer6.Eclipse(IDE)7.NetBeans(IDE)二、反编译技巧在渗透测试的时候需要审计的代码通常是class文件或者jar包那么我们应该如何审计呢让我们先来学习一下什么是Java源码和字节码。1. Java类编译与反编译基础简单的说Java源码就是未经编译的.java文件我们可以很轻松的阅读其中的代码逻辑而字节码.class文件则是.java文件经过编译之后产生的字节码文件因为.class文件是编译后的二进制文件所以我们是无法直接阅读的只能通过反编译工具将二进制文件转换成java代码或者ASM代码。示例代码Test.java:/** * author yz */ public class Test { public static void hello() { System.out.println(Hello~); } public void world() { System.out.println(World!); } public static void main(String[] args) { hello(); } }Test.java编译执行流程:Test.java 源码、字节码由于class文件的可读性较差通常我们需要使用Java反编译工具来反编译代码。我们通常会使用到JD-GUI、IDEA Fernflower插件、Bytecode-Viewer、Fernflower、JAD、JBE、JEB 等工具来反编译class。其中JD-GUI可能是目前反编译中使用的最多的工具了但是个人觉得JD-GUI的反编译能力远不如经过IDEA(IDEA应该是使用的改版后的Fernflower)因为IDEA默认支持对jar和class的反编译所以我个人强烈推荐使用IDEA来反编译class代码。当然反编译工具很多时候也不是万能的JD-GUI经常遇到无法反编译或反编译过程中程序直接崩溃的情况遇到这类情况我们通常可以使用IDEA反编译试试如果IDEA也无法反编译可以使用JBE来加载class文件读取程序的字节码如果JBE仍无法读取类信息还可以使用JDK自带的javap命令来读取class类字节码如果上诉所有的方法都无法反编译那么恐怕是这个类本身就存在无法编译问题要么可能就是类文件被加密处理过。可能你会说java编译的class不是说不可以加密吗没错这里所说的加密其实是为了保护编译后的class代码不可反编译通过实现自定义ClassLoader来loadClass加密后的类方式而已,这种加密方式曾在实战中也有遇到。2. 反编译整个Jar技巧通常我们在某些特殊的场景下拿到的只是jar文件那么我们应该如何反编译整个jar包的class文件呢2.1. FernflowerFernflower可以很轻松的实现jar的完整反编译执行如下命令即可 java -jar fernflower.jar jarToDecompile.jar decomp/ 其中jarToDecompile.jar是需要反编译的jar文件decomp是反编译后的class文件所存放的目录。需要注意的是Fernflower如遇无法反编译的情况可能会生成空的java文件2.2. JD-GUIJD-GUI是一个带GUI的反编译工具在JD-GUI的菜单中点击File–Save All Sources即可反编译jar。2.3. IDEAIDEA默认就支持jar包反编译同时还支持class文件名(⇧⌘F)、类方法名称(⇧⌘O)搜索。2.4. Bytecode-ViewerFernFlower提供了GUI版本Bytecode-Viewer,Bytecode-Viewer提供了直接反编译的class、jar、zip、apk、dex功能直接拖拽jar就可以直接对整个jar进行反编译了。2.5. Find命令find命令并不能支持Java反编译但是find命令可以非常方便的搜索经过编译后的二进制文件中的内容,所以有的时候使用find命令通常是最简单实用的直接解压jar包然后使用find命令搜索: find ./ -type f -name “/*.class” |xargs grep XXXX 即可搞定。2.6 使用Find命令和Fernflower实现批量反编译jar当我们只有项目war包且源码经过打包后发布到WEB-INF/lib的情况下我们不得不去找出待审计源码的具体jar文件并反编译。遇到这种情况我们可以巧妙的使用find命令来反编译所有目标的jar包。这里以jcms的一个非常老版本为例,jcms最终给客户部署的war包中源码并不是在WEB-INF/classes目录下而是将整个jcms系统按模块打包成了多个jar包放在了WEB-INF/lib目录下。我们可以通过搜索com.hanweb包名称来找出所有jar中包含了jcms的文件并通过Fernflower来反编译。java -jar /Users/yz/Desktop/javaweb-decomplier/javaweb-decomplier.jar -dgs1 $(find /Users/yz/Desktop/jcms/WEB-INF/lib/ -type f -name *.jar |xargs grep com.hanweb |awk {print $3}) /Users/yz/jcms-decomplier依赖的jar: javaweb-decomplier、Intellij java-decompiler。执行上面的命令后会在jcms-decomplier目录下看到所有的jar已经被Fernflower反编译了。3. IntelliJ IDEA 推荐IntelliJ IDEA是Jetbrains出品的一款非常强大的Java IDEIDEA提供了强大的代码搜索、近乎完美的反编译、动态调试等功能可以最大程度的辅助我们代码审计。不可以否认与IDEA相比虽然Eclipse和Netbeans也有与之类似的功能但是在真正的实战体验中个人更倾向于使用IDEA虽然曾经的我也是一个重度Eclipse开发者。三、IDEA代码搜索技巧IDEA的搜索快捷键是:⇧⌘F,使用IDEA提供的搜索功能可以非常快速的定位漏洞点信息。IDEA可以通过自定义搜索范围来精确查找我们需要审计的代码。默认搜索的是所有的位置不过我们可以点击红色箭头指向的…按钮来细化我们的搜索范围。1. 自定义范围搜索自定义搜索范围示例:自定义搜索范围后就可以在搜索时使用自定义的配置进行范围搜索了有助于我们在挖漏洞的时候缩小代码定位范围。2. 标记搜索搜索快捷键: ⌘O标记搜索支持类名、方法名搜索包括class或jar文件中的方法也支持搜索。3. Java调用链搜索当我们审计代码的时候发现某个方法或类有漏洞时我们需要定位到漏洞的请求地址(触发点)复杂业务系统往往会让我们很难定位到漏洞的触发点。借助IDEA的方法调用链搜索功能就可以很轻松的找出方法的调用链和触发点。选择类或者方法名–右键–Find Useages或者使用快捷键⌥F7四、Java Web基础1. Java分层思想为了更好的管理项目我们通常会采用分层架构的方式来开发Java Web项目分层设计的好处在于可以非常方便的分清楚包之间的业务逻辑关系。常见的JavaWeb项目分层视图层View 视图)控制层Controller、Action 控制层服务层Service业务逻辑层BO(business object)实体层entity 实体对象、VO(value object) 值对象 、模型层bean。持久层dao- Data Access Object 数据访问层、PO(persistant object) 持久对象基于Java分层架构的示例项目:3. 什么是Servlet?Servlet是在Java Web容器上运行的小程序,通常我们用Servlet来处理一些较为复杂的服务器端的业务逻辑。值得注意的是在Servlet3.0之后(Tomcat7)可以使用注解方式配置Servlet了。基于注解的ServletServlet3.0之前的版本都需要在web.xml中配置Servlet是两对标签由和组成Spring MVC框架就是基于Servlet技术实现的。基于配置实现的ServletHttpServlet类实现一个Servlet很简单只需要继承javax.servlet.http.HttpServlet类并重写doXXX方法或者service方法就可以了其中需要注意的是重写HttpServlet类的service方法可以获取到上述七种Http请求方法的请求。4. JSP、Servlet之间的关系JSP、JSPX文件是可以直接被Java容器直接解析的动态脚本jsp和其他脚本语言无异不但可以用于页面数据展示也可以用来处理后端业务逻辑。从本质上说JSP就是一个Servlet因为jsp文件最终会被编译成class文件而这个Class文件实际上就是一个特殊的Servlet。JSP文件会被编译成一个java类文件如index.jsp在Tomcat中Jasper编译后会生成index/_jsp.java和index/_jsp.class两个文件。而index/jsp.java 继承于HttpJspBase类HttpJspBase是一个实现了HttpJspPage接口并继承了HttpServlet的标准的Servlet//jspService方法其实是HttpJspPage接口方法类似于Servlet中的service方法这里的//_jspService方法其实就是HttpJspBase的service方法调用。5. 什么是FilterFilter是JavaWeb中的过滤器,用于过滤URL请求。通过Filter我们可以实现URL请求资源权限验证、用户登陆检测等功能。Filter是一个接口实现一个Filter只需要重写init、doFilter、destroy方法即可其中过滤逻辑都在doFilter方法中实现。Filter和Servlet一样是Java Web中最为核心的部分使用Servlet和Filter可以实现后端接口开发和权限控制当然使用Filter机制也可以实现MVC框架Struts2实现机制就是使用的Filter。Filter的配置类似于Servlet由和两组标签组成如果Servlet版本大于3.0同样可以使用注解的方式配置Filter。6. Filter和Servlet的总结对于基于Filter和Servlet实现的简单架构项目代码审计的重心集中于找出所有的Filter分析其过滤规则找出是否有做全局的安全过滤、敏感的URL地址是否有做权限校验并尝试绕过Filter过滤。第二点则是找出所有的Servlet分析Servlet的业务是否存在安全问题,如果存在安全问题是否可以利用是否有权限访问利用时是否被Filter过滤等问题切勿看到Servlet、JSP中的漏洞点就妄下定论不要忘了Servlet前面很有可能存在一个全局安全过滤的Filter。Filter和Servlet都是Java Web提供的API简单的总结了下有如下共同点。1.Filter和Servlet都需要在web.xml或注解(WebFilter、WebServlet)中配置而且配置方式是非常的相似的2.Filter和Servlet都可以处理来自Http请求的请求两者都有request、response对象3.Filter和Servlet基础概念不一样Servlet定义是容器端小程序用于直接处理后端业务逻辑而Filter的思想则是实现对Java Web请求资源的拦截过滤4.Filter和Servlet虽然概念上不太一样但都可以处理Http请求都可以用来实现MVC控制器(Struts2和Spring框架分别基于Filter和Servlet技术实现的)5.一般来说Filter通常配置在MVC、Servlet和JSP请求前面常用于后端权限控制、统一的Http请求参数过滤(统一的XSS、SQL注入、Struts2命令执行等攻击检测处理)处理其核心主要体现在请求过滤上而Servlet更多的是用来处理后端业务请求上7. 初识JavaWeb MVC框架传统的开发存在结构混乱易用性差耦合度高可维护性差等多种问题为了解决这些毛病分层思想和MVC框架就出现了。MVC即模型(Model)、视图(View)、控制器(Controller) MVC模式的目的就是实现Web系统的职能分工。截至2018年底绝大多数的新项目都已然改为了基于Spring Boot的Spring MVC实现也就是说曾经站在JavaWeb MVC最巅峰的Struts2框架已经逐渐陨落。7.1 Spring MVC 控制器在Spring进入了3.0时代,使用Java注解的方式也逐渐的流行了起来曾经写一个Spring的控制器我们通常要在xml中声明Spring bean并配置处理的URL而在新时代的Spring项目中我们通常用Spring MVC注解就可以轻松完成Spring MVC的配置了。一个基于Spring 注解配置的控制器:package org.javaweb.codereview.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.GetMapping;Controllerpublic class IndexController {RequestMapping(“/index.php”)public String index() {return “/index.html”;}}Spring Controller注解:ControllerRestControllerRepositoryRestControllerSpring MVC请求配置注解:RequestMappingGetMappingPostMappingPutMappingDeleteMappingPatchMappingSpring MVC除了上述6种Http请求处理注解以外还有Spring Data JPA Rest提供的特殊的RepositoryRestResource注解RepositoryRestResource是基于Spring Data JPA REST库实现的Spring Data JPA REST提供的API可支持通过JPA查询数据并处理Http请求服务。基于XML配置的Spring MVC对于一些老旧的项目可能还保留了一些基于xml配置的方式Spring MVC项目这里只简单的介绍下如何配置不做过多的描述。基于配置方式的控制器一般是在Controller类中实现了Spring的org.springframework.web.servlet.mvc.Controller接口的handleRequest方法(当然还有其他途径如:AbstractCommandController和SimpleFormController但都已经过时了)。TestController.java示例代码:package org.javaweb.codereview.controller; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * author yz */ public class TestController implements Controller { Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { ModelAndView mv new ModelAndView(); mv.setViewName(index); return mv; } }XML配置具体的beanbean name/test.do classorg.javaweb.codereview.controller.TestController/7.2 Struts2控制器Struts2主要的开发模式是基于xml配置在struts.xml中配置Action地址和对应的处理类。不过Struts2(2.1.6版本开始)也可以使用struts2-convention-plugin插件来实现基于注解方式的配置。需要注意的是Struts2的参数是可以通过get/set方法传入的如上图TestActionAnnotation类的username变量是可以直接在Http请求中的URL传入的。7.3 快速找出Http请求请求URL代码审计中我们可以选择优先从Controller、Servlet和JSP中入手也可以选择从漏洞点反向推出Http请求的入口地址这里将讲解下如何快速找到这些请求入口因为Struts2和Spring MVC的原理比较接近所以本节只以Spring MVC为例。7.3.1 查找Spring MVC所有的控制器如果有源码的情况下可以使用find命令或者IDEA的全局搜索功能即可快速搜索到所有的控制器如果只有class文件的情况下可以使用find命令:find ~/cms/ -type f -name *.class |xargs grep -E Controller|RestController|RepositoryRestController7.3.2 查找所有的请求处理URL查找请求处理URL的方式同理使用如下find命令查找所有class中的请求处理注解:find ~/cms/ -type f -name *.class |xargs grep -E RequestMapping|GetMapping|PostMapping|PutMapping|DeleteMapping|PatchMapping|RepositoryRestResource7.4 Spring MVC和Struts2控制器小结这一小节我们只是简单的介绍下Spring MVC和Struts2的控制器在后面的框架服务章节将会详细介绍。至于如何去快速定位Struts2的action请自行参考Spring MVC的Controller查找方式这里不再讲解。五、Java语言的动态性Java语言动态性一直以来都比较差并不像PHP那样灵活。在Java中的动态性往往需要使用一些曲折的方式来实现.这里简单列举了Java十余种动态性相关技术并总结部分技术实现安全问题。1.Java反射机制2.MethodHandle3.JDK动态代理4.使用JVM上的动态语言(如:Groovy、JRuby、Jython)5.表达式库(如:OGNL、MVEL、SpEL、EL)6.JSP、JSPX、Quercus(Resin容器提供了PHP5支持)7.字节码库(如:Asm、Javassist、Cglib、BCEL)8.ScriptEngineManager(脚本引擎)。9.动态编译(如:JDT、JavaCompiler)10.ClassLoader、URLClassLoader11.模版引擎(如:Freemarker、Velocity)12.序列化、反序列化(包含Java 对象序列化、XML、JSON等)13.JNI、JNA(Java调用C/C)14.OSGi(Open Service Gateway Initiative)15.RMI(Java远程方法调用,基于对象序列化机制实现)16.WebService17.JDWP(Java Platform Debugger Architecture Java调试协议)18.JMX(Java Management Extensions)1. Java反射机制特性Java反射机制可以无视类方法、变量访问权限修饰符可以调用任何类的任意方法、访问并修改成员变量值。也就是说只要发现一处Java反射调用漏洞几乎就可以为所欲为了。当然前提可能需要你能控制反射的类名、方法名和参数。一行代码即可实现反射调用Runtime执行本地命令:Runtime.class.getMethod(exec, String.class).invoke(Runtime.class.getMethod(getRuntime).invoke(null), whoami)获取一个类的对象(如Runtime类)我们一般会采用如下几种方式:1.Class.forName(“java.lang.Runtime”)、”.getClass().forName(“java.lang.Runtime”)2.Runtime.class3.ClassLoader.getSystemClassLoader().loadClass(“java.lang.Runtime”)Java反射获取类方法有两种方式:1.getMethod(xxx),getMethods()2.getDeclaredMethod(xxx)、getDeclaredMethods()。区别在于getMethod会返回当前类和父类的所有public方法而getDeclaredMethod返回的是当前的所有方法。Java反射获取类成员变量有两种方式:1.getField(xxx)、getFields()2.getDeclaredField(xxx)、getDeclaredFields()getField和getDeclaredField区别同上如果想要调用private修饰的Field或者Method只需要设置下setAccessible为true就可以了如:xxxMethod.setAccessible(true)。Java的大部分框架都是采用了反射机制来实现的(如:Spring MVC、ORM框架等)所以我们不得不掌握Java反射机制来提升我们的代码审计能力。Java反射机制实现无关键字执行命令import java.io.InputStream; import java.lang.reflect.Method; import java.util.Scanner; /** * author yz */ public class ReflectionTest { public static void exec() { try { System.out.println(Runtime.class.getMethod(exec, String.class).invoke(Runtime.class.getMethod(getRuntime).invoke(null), curl -i localhost:8000)); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { try { String str whoami; // java.lang.Runtime String runtime new String(new byte[]{106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101}); // Runtime.class Class? c Class.forName(runtime); // 获取getRuntime方法Runtime.getRuntime() Method m1 c.getMethod(new String(new byte[]{103, 101, 116, 82, 117, 110, 116, 105, 109, 101})); // 获取Runtime的exec方法rt.exec(xxx) Method m2 c.getMethod(new String(new byte[]{101, 120, 101, 99}), String.class); // Runtime.getRuntime().exec(str) Object obj2 m2.invoke(m1.invoke(null), str); // 获取命令执行结果Process类的getInputStream()方法 Method m obj2.getClass().getMethod(new String(new byte[]{103, 101, 116, 73, 110, 112, 117, 116, 83, 116, 114, 101, 97, 109})); m.setAccessible(true); // process.getInputStream() InputStream in (InputStream) m.invoke(obj2, new Object[]{}); // 输出InputStream内容到 Scanner scanner new Scanner(in).useDelimiter(//A); System.out.println(scanner.hasNext() ? scanner.next() : ); } catch (Throwable t) { t.printStackTrace(); } } }2. JDK7 MethodHandleJDK7开始Java提供了MethodHandle可以非常方便的访问和调用类方法MethodHandle的能力和Java反射机制相似但效率却远高出Java反射机制但MethodHandle也并不是那么完美的缺点是MethodHandle必须要求JDK版本大于等于1.7MethodHandle也无法像反射那样调用私有方法和变量。参考:通过代码简单介绍JDK 7的MethodHandle并与.NET的委托对比。基于MethodHandle实现的调用Runtime执行系统命令import java.io.InputStream; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.util.Scanner; /** * author yz */ public class MethodHandlesTest { public static void main(String[] args) { try { String str ping p2j.cn -c 1; Class runtimeClass Runtime.class; MethodHandles.Lookup lookup MethodHandles.lookup(); // Runtime rt Runtime.getRuntime() MethodHandle methodHandle lookup.findStatic( runtimeClass, getRuntime, MethodType.methodType(runtimeClass) ); // 获取Runtime的exec方法 MethodHandle execMethod lookup.findVirtual( runtimeClass, exec, MethodType.methodType(Process.class, new Class[]{ String.class }) ); // 获取Process的getInputStream方法 MethodHandle inputStreamMethod lookup.findVirtual( Process.class, getInputStream, MethodType.methodType(InputStream.class) ); // 调用Runtime.getRuntime().exec(xxx).getInputStream() InputStream in (InputStream) inputStreamMethod.invoke( execMethod.invoke(methodHandle.invoke(), str) ); // 输出InputStream内容到 Scanner scanner new Scanner(in).useDelimiter(//A); System.out.println(scanner.hasNext() ? scanner.next() : ); } catch (Throwable t) { t.printStackTrace(); } } }六、Java代码审计-Checklist通常我喜欢把代码审计的方向分为业务层安全问题、代码实现和服务架构安全问题,。1. 业务层安全常见问题业务层的安全问题集中在业务逻辑和越权问题上我们在代码审计的过程中尽可能的去理解系统的业务流程以便于发现隐藏在业务中的安全问题。1.1 业务层中常见的安全问题Checklist1.用户登陆、用户注册、找回密码等功能中密码信息未采用加密算法。2.用户登陆、用户注册、找回密码等功能中未采用验证码或验证码未做安全刷新(未刷新Session中验证码的值)导致的撞库、密码爆破漏洞。3.找回密码逻辑问题(如:可直接跳过验证逻辑直接发包修改)。4.手机、邮箱验证、找回密码等涉及到动态验证码等功能未限制验证码失败次数、验证码有效期、验证码长度过短导致的验证码爆破问题。5.充值、付款等功能调用了第三方支付系统未正确校验接口(如:1分钱买IPhone X)。6.后端采用了ORM框架更新操作时因处理不当导致可以更新用户表任意字段(如:用户注册、用户个人资料修改时可以直接创建管理员账号或其他越权修改操作)。7.后端采用了ORM框架查询数据时因处理不当导致可以接收任何参数导致的越权查询、敏感信息查询等安全问题。8.用户中心转账、修改个人资料、密码、退出登陆等功能未采用验证码或Token机制导致存在CSRF漏洞。9.后端服务过于信任前端重要的参数和业务逻辑只做了前端验证(如:文件上传功能的文件类型只在JS中验证、后端不从Session中获取用户ID、用户名而是直接接收客户端请求的参数导致的越权问题)。10.用户身份信息认证逻辑问题(如:后台系统自动登陆时直接读取Cookie中的用户名、用户权限不做验证)。11.重要接口采用ID自增、ID可预测并且云端未验证参数有效性导致的越权访问、信息泄漏问题(如:任意用户订单越权访问)。12.条件竞争问题某些关键业务(如:用户转账)不支持并发、分布式部署时不支持锁的操作等。13.重要接口未限制请求频率导致短信、邮件、电话、私信等信息轰炸。14.敏感信息未保护如Cookie中直接存储用户密码等重要信息。15.弱加密算法、弱密钥如勿把Base64当成数据加密方式、重要算法密钥采用弱口令如123456。16.后端无异常处理机制、未自定义50X错误页面,服务器异常导致敏感信息泄漏(如:数据库信息、网站绝对路径等)。17.使用DWR框架开发时前后端不分漏洞(如:DWR直接调用数据库信息把用户登陆逻辑直接放到了前端来做)。2. 代码实现常见问题代码审计的核心是寻找代码中程序实现的安全问题通常我们会把代码审计的重心放在SQL注入、文件上传、命令执行、任意文件读写等直接威胁到服务器安全的漏洞上因为这一类的漏洞杀伤力极大也是最为致命的。###2.1 代码实现中常见的安全问题Checklist1.任意文件读写(文件上传、文件下载)、文件遍历、文件删除、文件重命名等漏洞2.SQL注入漏洞3.XXE(XML实体注入攻击)4.表达式执行(SpEL、OGNL、MVEL2、EL等)5.系统命令执行漏洞(ProcessBuilder)6.反序列化攻击(ObjectInputStream、JSON、XML等)7.Java反射攻击8.SSRF攻击2.1.1 Java 文件名空字节截断漏洞(%00 Null Bytes)空字节截断漏洞漏洞在诸多编程语言中都存在究其根本是Java在调用文件系统(C实现)读写文件时导致的漏洞并不是Java本身的安全问题。不过好在高版本的JDK在处理文件时已经把空字节文件名进行了安全检测处理。2013年9月10日发布的Java SE 7 Update 40修复了空字节截断这个历史遗留问题。此次更新在java.io.File类中添加了一个isInvalid方法专门检测文件名中是否包含了空字节。修复的JDK版本所有跟文件名相关的操作都调用了isInvalid方法检测防止空字节截断。修复前(Java SE 7 Update 25)和修复后(Java SE 7 Update 40)的对比会发现Java SE 7 Update 25中的java.io.File类中并未添加/u0000的检测。受空字节截断影响的JDK版本范围:JDK1.7.40,单是JDK7于2011年07月28日发布至2013年09月10日发表Java SE 7 Update 40这两年多期间受影响的就有16个版本值得注意的是JDK1.6虽然JDK7修复之后发布了数十个版本但是并没有任何一个版本修复过这个问题而JDK8发布时间在JDK7修复以后所以并不受此漏洞影响。参考:JDK-8014846 : File and other classes in java.io do not handle embedded nulls properly。维基百科-Java版本歷史Oracle Java 历史版本下载2.1.2 测试Java写文件截断测试测试类FileNullBytes.java:import java.io.File; import java.io.FileOutputStream; import java.io.IOException; /** * author yz */ public class FileNullBytes { public static void main(String[] args) { try { String fileName /tmp/null-bytes.txt/u0000.jpg; FileOutputStream fos new FileOutputStream(new File(fileName)); fos.write(Test.getBytes()); fos.flush(); fos.close(); } catch (IOException e) { e.printStackTrace(); } } }使用JDK1.7.0.25测试成功截断文件名使用JDK1.7.0.80测试写文件截断时抛出java.io.FileNotFoundException: Invalid file path异常:空字节截断利用场景Java空字节截断利用场景最常见的利用场景就是文件上传时后端使用了endWith、正则使用如:.(jpg|png|gif)$验证文件名后缀且文件名最终原样保存,同理文件删除(delete)、获取文件路径(getCanonicalPath)、创建文件(createNewFile)、文件重命名(renameTo)等方法也可适用。空字节截断修复方案最简单直接的方式就是升级JDK如果担心升级JDK出现兼容性问题可在文件操作时检测下文件名中是否包含空字节如JDK的修复方式:fileName.indexOf(‘/u0000′)即可。2.1.2 任意文件读取漏洞任意文件读取漏洞即因为没有验证请求的资源文件是否合法导致的此类漏洞在Java中有着较高的几率出现任意文件读取漏洞看似很简单但是在这个问题上翻车的有不乏一些知名的中间件:Weblogic、Tomcat、Resin又或者是主流MVC框架:Spring MVC、Struts2。所以在审计文件读取功能的时候要非常仔细或许很容易就会有意想不到的收获任意文件读取示例代码file-read.jsp:% page contentTypetext/html;charsetUTF-8 languagejava % % page importjava.io.ByteArrayOutputStream % % page importjava.io.File % % page importjava.io.FileInputStream % % File file new File(request.getParameter(path)); FileInputStream fis new FileInputStream(file); ByteArrayOutputStream baos new ByteArrayOutputStream(); byte[] b new byte[1024]; int a -1; while ((a fis.read(b)) ! -1) { baos.write(b, 0, a); } out.write(pre new String(baos.toByteArray()) /pre); fis.close(); %访问file-read.jsp文件即可读取任意文件:http://localhost:8080/file/file-read.jsp?path/etc/passwd快速发现这类漏洞得方式其实也是非常简单的在IDEA中的项目中重点搜下如下文件读取的类。1.JDK原始的java.io.FileInputStream类2.JDK原始的java.io.RandomAccessFile类3.Apache Commons IO提供的org.apache.commons.io.FileUtils类4.JDK1.7新增的基于NIO非阻塞异步读取文件的java.nio.channels.AsynchronousFileChannel类。5.JDK1.7新增的基于NIO读取文件的java.nio.file.Files类。常用方法如:Files.readAllBytes、Files.readAllLines如果仍没有什么发现可以搜索一下FileUtil很有可能用户会封装文件操作的工具类。Java WebSeviceWeb Service是一种基于SOAP协议实现的跨语言Web服务调用在Java中Web Service有如下技术实现:Oracle JWS、Apache Axis1、2、XFire、Apache CXF、JBossWS。Axis1.4 配置web.xml配置Axis1.4配置server-config.wsdd文件注册Web Service服务类和方法:FileService类提供了文件读写接口:使用IDEA创建Web Service项目默认会创建管理Web Service的API:/servlet/AxisServlet、/services、SOAPMonitor、/servlet/AdminServlet/*.jws以及用监控Web Service的端口5001或5101。访问Web Service的FileService服务加上?wsdl参数可以看到FileService提供的服务方法和具体的参数信息。使用SOAP-UI调用Web Service接口示例:需要注意的是Web Service也是可以设置授权认证的,如实现了WS-Security的WSS4J。使用IDEA根据wsdl生成Web Service客户端代码设置wsdl地址、包名:新建FileServiceTest类测试接口调用:package org.javaweb.codereview.axis.client; import java.net.URL; /** * 文件Web Service服务测试 * * author yz */ public class FileServiceTest { public static void main(String[] args) { try { FileServiceService fileService new FileServiceServiceLocator(); URL webServiceUrl new URL(http://localhost:8080/services/FileService); FileServiceSoapBindingStub soapService new FileServiceSoapBindingStub(webServiceUrl, fileService); String content soapService.readFile(/etc/passwd); System.out.println(content); } catch (Exception e) { e.printStackTrace(); } } }参考:Axis1.4框架 实现webservice服务器和客户端使用IDEA根据wsdl生成WebServices客户端代码-Javaaxis2 利用小工具cat.aar转自freebuf《网络安全从零到精通全套学习大礼包》96节从入门到精通的全套视频教程免费领取如果你也想通过学网络安全技术去帮助就业和转行我可以把我自己亲自录制的96节 从零基础到精通的视频教程以及配套学习资料无偿分享给你。网络安全学习路线图想要学习 网络安全作为新手一定要先按照路线图学习方向不对努力白费。对于从来没有接触过网络安全的同学我帮大家准备了从零基础到精通学习成长路线图以及学习规划。可以说是最科学最系统的学习路线大家跟着这个路线图学习准没错。配套实战项目/源码所有视频教程所涉及的实战项目和项目源码学习电子书籍学习网络安全必看的书籍和文章的PDF市面上网络安全书籍确实太多了这些是我精选出来的面试真题/经验以上资料如何领取ttps://i-blog.csdnimg.cn/direct/6d41d41d77204bfc9459b7a3d079371f.png)配套实战项目/源码所有视频教程所涉及的实战项目和项目源码学习电子书籍学习网络安全必看的书籍和文章的PDF市面上网络安全书籍确实太多了这些是我精选出来的面试真题/经验以上资料如何领取文章来自网上侵权请联系博主