Java 代码审计笔记

常见漏洞审计

1.SQL注入漏洞

Java执行SQL语句常见方式:

  • 使用JDBC的java.sql.Statement执行SQL语句
  • 使用JDBC的java.sql.PreparedStatement执行SQL语句
  • 使用Hibernate执行SQL语句
  • 使用MyBatis执行SQL语句

其中Statement需要通过拼接来执行语句且每次都要进行编译,若不对输入做过滤会导致SQL注入漏洞;而PrepareStatement进行预编译参数化查询能够有效防止SQL注入。通过搜索这两个类的调用并进行审计,从而检查出漏洞是否存在。

PreparedStatement是使用占位符传入参数,因此在执行order by时会出错(具体原因不详细说明,自行百度),只能使用字符串拼接的方式,因此只能使用Statement。

Java预编译查询中不会对%和_进行转义处理,而%和_是like查询的通配符,不做相关过滤可能导致恶意模糊查询。

MyBatis中的#{}在底层上使用“?”作为占位符来生成PreparedStatement,是参数化查询预编译的机制,较安全,也因此在使用order by、like、in查询时会报错,需要转用${}进行查询;${}将传入的数据直接显示生成在SQL语句中,类似字符串拼接,因此不对输入进行检查、过滤将可能导致SQL注入。

审计总结:(留意getParameter函数来找到可控参数)

  • Statement
  • createStatement
  • PreparedStatement
  • like ‘%${
  • in (${
  • select
  • update
  • insert

2.任意文件上传漏洞

常见文件上传方式:

public String fileUpload(@RequestParam("file") CommonsMulpartFile file) throws IOException{
    long startTime = System.currentTimeMillis();
    System.out.println("fileName: "+file.getOriginalFilename());
try{
        OutputStream os = new FileOutputStream("/tmp"+newDate().getTime()+file.getOriginalFilename());
        InputStream is = file.getInputStream();
        int temp;
        while((temp = is.read())!=(-1))
        {
            os.write(temp);
        }
        os.flush();
        os.close();
        is.close();
    } catch(FileNotFoundException e){
        e.printStackTrace();
    }
    return "/success";
}
String realPath = this.getServletContext().getRealPath("/upload");
String tempPath = "/tmp";
File f = new File(realPath);
if(!f.exists()&&!f.isDirectory()){
    f.mkdir();
}
File f1 = new File(tempPath);
if(!f1.isDirectory)){
    f1.mkdir();
}
DiskFileUploadItemFactory factory = new DiskFileItemFactory();
factory.setRepository(f1);
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setHeaderEncoding("UTF-8");
if(!ServletFileUpload.isMultipartContent(req)){ return; }
List<FileItem> items = upload.parseRequest(req);
for(FileItem item:items){
    if(item.isFormField()){
        String filedName = item.getFieldName();
        String filedValue = item.getString("UTF-8");
    }
    else{
        String filedName = item.getName();
        if(fileName == null || "".equals(fileName.trim())){ continue; }
        fileName = fileName.substring(fileName.lastIndexOf("/")+1);
        String filePath = realPath+"/"+fileName;
        InputStream in = item.getInputStream();
        OutputStream out = new FileOutputStream(filePath);

byte b[] = new byte[1024];
int len = -1;
while((len=in.read(b)))!=-1){
    out.write(b,0,len);
}
out.close();
in.close();
try{
    Thread.sleep(3000);
} catch(InterruptedException e){
    e.printStackTrace();
}
item.delete();
public String handleFileUpload(@RequestParam("file") MultipartFile file){
    if(file.isEmpty()){
        return "请上传文件";
    }
    //获取文件名
    String fileName = file.getOriginalFilename();
    String suffixName = fileName.substring(fileName.indexOf("."));
    String filePath = "/tmp";
    File dest = new File(filePath+fileName);
    if(!dest.getParentFile().exists()){
        dest.getParentFile().mkdirs();
    }
    try{
        file.transferTo(dest);
        return "上传成功";
    } catch(IllegalStateException e){
        e.printStackTrace();
    } catch(IOException e){
        e.printStackTrace();
    }
    return "上传失败";
}

审计总结:(检查前后端是否存在后缀过滤不全和读取后缀方式错误等)

  • org.apache.commons.fileupload
  • java.io.File
  • MultipartFile
  • RequestMethod
  • MultipartHttpServletRequest
  • CommonsMultipartResolver

3.XSS漏洞

XXS漏洞的产生必然存在相关的输入/输出:Java输入通常使用request.getParameter(param)或${param}输入信息。输出表现为前端的渲染,可通过定位前端中一些标识来找。

XSS常见触发位置

JSP表达式

  • <%=变量 %>
  • <% out.println(变量); %>
  • <% String msg = request.getParameter(‘变量’);%>

EL(Expression Language,表达式语言)

  • <c:out>标签:显示一个表达式的结果
  • <c:if>标签:判断表达式的值
  • <c:forEach>标签:迭代输出标签内部的内容

ModelAndView类用来存储处理完成后的结果数据,以及显示该数据的视图,其前端JSP页面可以使用${参数}的方法来获取值:

package com.dgr.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@RequestMapping("mvc")
@Controller
public class TestRequestMapping{
    @RequestMapping(value="/getMessage")
    public ModelAndView getMessage(){
        ModelAndView modelandview = new ModelAndView();
        modelandview.setViewName("message");
        modelandview.addObject("message","Hello World");
        return modelandview;
    }
}

ModelMap类的使用

//Spring也提供了ModelMap类,此处用java.util.Map实现,可根据模型属性的具体类型自动生成模型属性的名称
public String testmethod(String someparam,ModelMap model){
    //省略方法处理逻辑
    //将数据放置到ModelMap类的model对象中,第二个参数可以是任何Java类型
    Model.addAttribute("key",someparam);
    return "success";
}

Model类的使用(Model类是一个接口类,通过attribute()添加数据,存储的数据范围是requestScope)

public String index1(Model model){
    model.addAttribute("result","后台返回");
    return "result";
}

审计总结:

  • <%=
  • ${
  • <c:out
  • <c:if
  • <c:forEach
  • ModelAndView
  • ModelMap
  • Model
  • request.gerParameter
  • request.setAttribute
  • response.getWriter().print()
  • response.getWriter().writer()

4.目录穿越漏洞

目录穿越的本质是路径可控,一旦涉及文件的读取问题便会涉及java.io.File类,审计时优先查找java.io.File类的调用,判断Paths、path、System.getProperty(“user.dir”)等可能会用来构造路径的关键字

5.URL跳转漏洞

检查用于URL重定向的方法:

  • 通过ModelAndView
  • 通过返回String
  • 使用sendRedirect
  • 使用RedirectAttributes
  • 通过设置Header

6.命令执行漏洞

Java.lang.ProcessBuilder类用于创建操作系统进程,每个ProcessBuilder实例管理一个进程属性集。start()方法利用这些属性创建一个新的Process进程实例,从而利用ProcessBuilder执行命令:

ProcessBuilder pb = new ProcessBuilder("mycommand","myArg");
Process process = pb.start();

java.lang.Runtime公共类中的exec()方法同样也可以执行系统命令:

  • public Process exec(String command)
  • public Process exec(String[] cmdarray)
  • public Process exec(String[] cmdarray,String[] envp)
  • public Process exec(String[] cmdarray,String[] envp,File dir)
  • public Process exec(String command,String[] envp)
  • public Process exec(String command,String[] envp,File dir)

7.XXE漏洞

搜索常见的能够解析XML的方法:

  • XMLReader
  • SAXBuilder
  • SAXReader
  • SAXParserFactory
  • Digester //通常没有回显
  • DocumentBuilderFactory

8.SSRF漏洞

与PHP不同,在Java中SSRF仅支持sun.net.www.protocol下所有的协议:http、https、file、ftp、mailto、jar及netdoc协议,因此不能像PHP一样使用gopher协议来扩展攻击面

在Java中可以通过file或netdoc协议进行列目录操作以读取更多敏感信息,对于无回显的文件读取可以利用ftp协议进行外带攻击(部分版本的Java即使使用ftp协议也无法读取多行文件

注意能够发起HTTP请求的类及函数:

  • HttpURLConnection.getInputStream
  • URLConnection.getInputStream
  • HttpClient.execute
  • OkHttpClient.newCall.execute
  • Request.Get.execute
  • Request.Post.execute
  • [url].openStream()方法
  • ImageIO.read

若想支持sun.net.www.protocol中的所有协议则只能使用方法:URLConnection、URL

若发起的网络请求是带HTTP的,那么其将只支持HTTP、HTTPS协议:HttpURLConnection、HttpClient、OkHttpClient.newCall.execute

9.SpEL表达式注入漏洞

SpEL表达式语法:

  • 使用量表达式语法:”#{‘Hello World’}”
  • 使用Java代码new/instanceof:Expression exp = parser.parseExpression(“new Spring(‘Hello World’)”);
  • 使用T(Type)来表示java.lang.Class实例:parser.parseExpression(“T(Integer).MAX_VALUE”);

SpEL中可以使用#bean_id来获取容器内的变量。同时,存在两个特殊的变量 #this、#root,分别用来表示使用当前的上下文和引用容器的root对象

SpEL可以使用T()操作符声明特定的Java类型,一般用来访问Java类型中的静态属性或静态方法。括号中需要包含类名的全限定名,也就是包名加上类名。唯一例外的是SpEL内置了java.lang包下的类声明,例如java.lang.String可以通过T(String)访问而不需要使用全限定名

使用new可以直接在SpEL中创建实例,创建实例的类要通过全限定名进行访问:

ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("new.java.util.Date()");
Date value = (Date)exp.getValue();
System.out.println(value);
  • org.springframework.expression.spel.standard
  • SpelExpressionParser
  • parseExpression
  • expression.getValue()
  • expression.setValue()

SpEL漏洞成因:Spring为解析SpEL提供了两套接口,分别是SimpleEvaluationContext和StandardEvaluationContext。SimpleEvaluationContext仅支持SpEL语法的子集,抛弃了Java类型引用、构造函数及beam引用相对安全。而StandardEvaluationContext包含了SpEL所有功能,且在不指定EvaluationContext情况下默认采用StandardEvaluationContext;很大一部分开发人员未对用户输入进行处理就直接通过解析引擎对SpEL继续解析

通过插入以下POC检查是否存在SpEL表达式注入:

  • ${4*4}
  • T(Thread).sleep(10000)
  • T(java.lang.Runtime).getRuntime().exec(‘command’)
  • T(java.lang.Runtime).getRuntime().exec(“nslookup baidu.com”)
  • new java.lang.ProcessBuilder(“command”).start()
  • new java.lang.ProcessBuilder({‘nslookup baidu.com’}).start()
  • #this.getClass().forName(‘java.lang.Runtime’).getRuntime().exec(‘nslookup baidu.com’)

对于常见黑名单过滤可以利用两种方式构造payload:利用反射与拆分关键字构造、利用ScriptEngineManager构造

10.Java反序列化

审计关键字:

  • ObjectInputStream.readObject
  • ObjectInputStream.readUnshared
  • XMLDecoder.readObject
  • Yaml.load
  • XStream.fromXML
  • ObjectMapper.readValue
  • JSON.parseObject

搜索关键字找到存在反序列化操作的文件时,考虑参数是否可控,以及引用的Class Path是否包含Apache Commons Collections等危险库(ysoserial所支持的其他库亦可)。同时满足以上条件则通过ysoserial工具生成payload直接打。不支持危险库则尝试通过构造利用链接实现任意代码执行

11.SSTI模板注入漏洞

Java中常见模板引擎:XMLTemplate、Velocity、CommonTemplate、FreeMarker、Smarty4j、TemplateEngine等,Velocity使用较多

在Velocity中以#来标识Velocity的脚本语句例如#set、#if、#else、#恩典、#foreach、#iinclude、#parse等

#if($msg.img)
<img src="$msg.imgs" border=0>
#else
<img src="qccp.jpg">
#end

$用来标识一个对象,一旦可以调用对象则有办法构造命令执行语句:$e.getClass().forName(“java.lang.Runtime”).getMethod(“getRuntime”,null).invoke(null,null).exec()

在Velocity模板注入中,如果无法进行命令执行,往往可以通过修改Cookie来进行特权升级,例如:$session.setAttribute(“IS_ADMIN”,”1″)

在漏洞不存在回显的情况且容器为Tomcat7时,可通过以下方法构造一个有回显的命令执行:

#set($str=$class.inspect("java.lang.String").type)
#set($chr=$class.inspect("java.lang.Character").type)
#set($ex=$class.inspect("java.lang.Runtime").type.getRuntime().exec("whoami"))
$ex.waitFor()
#set($out=$ex.getInputStream())
#foreach($i in [1..$out.available()])
$str.valueOf($chr.toChars($out.read()))
#end

审计关键字:搜索对于模板引擎的关键字,例如velocity对应的org.apache.velocity

12.整数溢出漏洞

java中的int是32位有符号整数类型,其最大值是0x7fffffff,最小值是0x80000000,即-2147483648~2147483647之间。当运算结果超出这个范围则发生了溢出,而且不会有任何异常抛出

13.硬编码密码漏洞

硬编码密码指在系统中采用明文的形式存储密码(密码直接存储在源代码甚至是前端代码中),通常会导致严重的身份验证失败。

14.不安全的随机数生成器

计算机产生的随机数具有周期性、可预测性,例如java.util.Random工具类没带参数构造函数生成的Random对象的种子默认是当前系统时间的毫秒数,故进入到Random类中查看其种子默认是当前的系统时间,只要种子一样,其输出的随机序列也是一样的

欢迎评论区中交流
No Comments

Send Comment Edit Comment


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
Previous