《Java代码审计》——“OWASP Top 10 2017”漏洞的代码审计
本文最后更新于 37 天前,其中的信息可能已经有所发展或是发生改变。

注入

SQL注入

JDBC拼接不当造成SQL注入

JDBC有两种方法执行SQL语句,分别为PrepareStatement和Statement。

Statement对象的SQL注入

        String sql = "select * from user where id = " + req.getParameter("id");
        System.out.println(sql);
        try {
            java.sql.Statement st = con.createStatement();
            ResultSet re = st.executeQuery(sql);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }

PrepareStament对象的SQL注入

        String sql = "select * from user where id = " + req.getParameter("id");
        System.out.println(sql);
        try {
            java.sql.PreparedStatement psst = con.prepareStament(sql);
            ResultSet re = st.executeQuery(sql);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }

安全的SQL语句

        PrintWriter out = resp.getWriter();
        String sql = "select * from user where id = ?";
        System.out.println(sql);
        try {
            java.sql.PreparedStatement psst = con.prepareStament(sql);
            psst.setInt(1, Integer.parseInt(res.getParameter("id")));
            ResultSet re = psst.executeQuery();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }

如果使用字符串拼接执行,可能会造成SQL注入。

正确的PrepareStatement使用?进行占位,可以有效避免SQL注入的产生。

框架使用不当造成SQL注入

Java项目会对JDBC进行更抽象封装的持久化框架,通常在底层实现对SQL注入的防御。但如果研发人员未能恰当使用框架的情况下,仍可能存在SQL注入的风险。

(1) MyBatis

MyBatis的思想是将SQL语句编入配置文件,避免SQL语句在Java程序中大量出现,方便后续对SQL语句的修改和配置。

MyBatis中使用parameterType向SQL语句传参,在SQL引用传参可以使用#{Parameter}和${Parameter}两种方式。#采用预编译的方式构造SQL,$采用拼接的方式构造SQL。

(2)Hibernate框架

Hibernate是Java持久化API规范的一种实现方式。将Java类映射到数据表中,从Java数据类型映射到SQL数据类型。是目前主流的Java数据库持久化框架,采用Hibernate查询语言。(HQL)

语法与SQL类似,但有所不同,在漏洞上具有一定的限制。HQL查询语句是对持久化类的对象进行操作而不是直接对数据库进行操作,因此使用Hibernate引擎进行解析。

正确使用参数绑定的方式可以避免注入的产生。

  1. 位置参数
String parameter = "admin";
Query<User> query = session.createQuery("from com.admin.bean.Userwhere name = ?1", User.class);
query.setParameter(1, parameter);

2. 命名参数

Query<User> query = session.createQuery("from com.admin.bean.Userwhere name = ?1", User.class);
String parameter = "admin";
Query<User> query = session.createQuery("from com.admin.bean.Userwhere name = :name", User.class);
query.setParameter("name", parameter);

3. 命名参数列表

List<String> names = Arrays.asList("admin", "guest");
Query<User> query = session.createQuery("from com.admin.bean.User where name in (:name)", User.class);
query.setParameter("name", names);

4. 类实例(JavaBean)

user1.setName("admin");
Query<User> query = session.createQuery("from com.admin.bean.User where name = :name", User.class);
query.setProperties(user1);

使用拼接参数的方式构造SQL语句可能会造成SQL注入。

命令注入

        Process process = Runtime.getRuntime().exec(cmd);

命令注入的局限性:

  • | 前面命令输出结果作为后面命令的输入内容
  • || 前面命令执行失败才执行后面的命令
  • & 前面命令执行后继续执行后面的命令
  • && 前面命令执行成功后才执行后面的命令

在Java中连接符的使用存在一定局限性,会被当做一个完整的字符串而非两条命令。传入的字符串会优先经过StringTokenizer的处理,后续会分割出cmdarray来保存分割后的命令参数,而经过处理后参数会被当做被执行的参数,所以命令分割符不生效。

代码注入

代码注入漏洞的前提条件时将用户输入的数据作为Java代码执行。通过Java的反射,根据传入的不同类名、方法名和参数执行不同的功能。

String ClassName = req.getParameter("ClassName");
String MethodName = req.getParameter("Method");
String[] Args = new String[]{req.getParameter("Args").toString()};
try {
    Class clazz = Class.forName(ClassName);
    Constructor constructor = clazz.getConstructor(String[].class);
    Object obj = constructor.newInstance(new Object[]{Args});
    Method method = clazz.getMethod(MethodName);
    method.invoke(obj);
} catch (ClassNotFoundException e) {
    ...
}

表达式注入

表达式语言(Expression Language),又称EL表达式,是一种在JSP中内置的语言,用于作用于用户访问页面的上下文以及不同作用域的对象,取得对象属性值或执行简单的运算或判断操作。

JSP四大作用域

  • page:只在一个页面保存数据
  • request:只在一个请求中保存数据
  • session:在一次会话中保存数据,单用户使用
  • application:在整个服务器中保存数据,全用户共享

可以使用作用域范围作为前缀来指定在某个作用域查找,例如${requestScope.name}表示在request作用域中获取name变量,与${requestScope["name"]}同理。

EL也可以实例化Java的内置类${Runtime.getRuntime().exec("calc")}

模板注入

FreeMarker引擎进行解析并动态替换模板中的内容进行渲染。

可以通过New函数实例化一个Execute对象并执行exec()方法造成任意代码执行。

<#assign value="freemarker.template.utility.Execute"?new()>${value("calc.exe")}

api函数可以用来访问Java的API,使用方法为value?api.someJavaMethod(),相当于value.someJavaMethod(),因此可以利用API函数通过getClassLoader来获取一个类加载器,进而加载任意类,也可以通过getResource来读取服务器上的资源文件。

失效的身份验证

错误地使用应用程序的身份认证和会话管理功能,使攻击者能够破译密码,密钥或会话令牌,或者利用其他开发漏洞暂时或长久地冒充其他用户的身份,导致攻击者可以执行受害者用户的任何操作。

案例:WebGoat8 JWT Token 猜解实验

敏感信息泄露

敏感信息包括系统敏感信息以及应用敏感信息。

系统敏感信息指业务系统本身的基础环境信息,例如系统信息、中间件版本、代码信息等

应用信息包括个人的身份证、姓名、电话号码、邮箱等。

案例:TurboMail 5.2.0

XML外部实体注入(XXE)

攻击者可以利用XXE实现任意文件读取、内网端口探测、命令执行、拒绝服务攻击等方面的攻击。

读取系统文件

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<foo>&xxe;</foo>

实体指的是DTD实体,定义引用普通文本或特殊字符的快捷方式的变量。

实体可以分成内部内部声明实体和引用外部实体,引用外部实体可以支持http、file、ftp、jar、netdoc、gopher等协议。

DoS攻击

通过多个引用指数级扩展XML大小。

Blind XXE

盲注XXE,可以操作数据外带,以及引用外部dtd等。

修复

设置属性,禁止使用外部实体

失效的访问控制

失效的访问控制是指未对通过身份验证的用户实施恰当的访问控制,可以利用缺陷访问未授权的功能或数据,例如访问其他用户的账户,查看敏感文件,修改其他用户的数据,更改访问权限等。

横向越权:Cookie、GET、遍历,测试功能点如重置密码、敏感数据访问等

纵向越权:Cookie、JWT,单个功能点、API漏洞做权限验证

安全配置错误

最小权限

Tomcat任意文件写入:/1.jsp/调用DefaultServlet进行处理,调用doPut写入文件,File对象实例化过程中处理掉多余的/。

XSS

  • 收集输入、输出点
  • 查看输入、输出点的上下文环境
  • 判断WEB应用是否对输入、输出防御

不安全的反序列化

Java RMI(Java Remote Method Invocation,Java远程方法调用)是允许运行在一个Java虚拟机的对象调用另一个Java虚拟机对象的方法,通过反序列化的方式传输对象,利用RMI作为反序列化的触发点。

JNDI(Java Naming and Directory Interface, Java命令和目录接口)是一组应用程序接口,目的是方便查找远程或本地对象。通过JNDI获取远程对象过程中,如果访问的服务地址可控,加载并实例化恶意对象,造成JNDI注入。

JEP290:内置过滤器,只允许特定的类进行反序列化。

案例:Apache Commons Collections 反序列化漏洞:transform的input变量可控时调用任意类的任意方法、Templateslmpl

FastJson反序列化漏洞:1. 绕过黑名单 2.寻找绕过AutoType的方式

使用含有已知漏洞的组件

Weblogic cc t3

不足的日志记录和监控

CRLF:HTTP请求走私、HTTP响应拆分 伪造日志

未经记录可审计事件

暂无评论

发送评论 编辑评论


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