记一次执行顺序问题导致的SQL注入绕过

拦截器(Interceptor)和过滤器(Filter)在Java Web应用程序中都是用于处理HTTP请求和响应的组件,但它们属于不同的层次,并且具有不同的执行顺序和作用域。正确理解它们之间的区别和执行顺序对于确保应用程序的安全性至关重要。

0x00 背景

在Java Web开发中,SQL注入是一种常见的安全漏洞,它允许攻击者通过构造恶意的SQL查询语句来操纵数据库。在实际业务中发现一处SQL注入的绕过case,当前漏洞已经修复完毕 。提取关键的的漏洞代码做下复盘。

目标应用使用mybatis进行SQL交互,部分业务接口通过orderby实现了排序的功能。因为动态SQL没办法进行预编译处理,若缺少对应的安全措施,会因为存在SQL直接拼接而引入SQL注入风险的:

 order by ${_parameter} desc

应用是通过过滤器Filter的方式对用户传递的参数进行检查,来防御SQL注入风险的。关键代码如下,大致思路是首先获取当前请求的参数以及对应的值,然后调用checkSqlInject方法进行对应的安全检查:

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) servletRequest;

        MyRequestWrapper requestWrapper = new MyRequestWrapper(req);

        // 获取请求参数
        Map<String, Object> paramsMaps = new TreeMap<>();
        if ("POST".equals(req.getMethod().toUpperCase())) {
            String body = requestWrapper.getBody();
            paramsMaps = JSONObject.parseObject(body, TreeMap.class);
        } else {
            Map<String, String[]> parameterMap = requestWrapper.getParameterMap();
            Set<Map.Entry<String, String[]>> entries = parameterMap.entrySet();
            for (Map.Entry<String, String[]> next : entries) {
                paramsMaps.put(next.getKey(), next.getValue()[0]);
            }
        }

        // 校验SQL注入
        for (Object o : paramsMaps.entrySet()) {
            Map.Entry entry = (Map.Entry) o;
            Object value = entry.getValue();
            if (value != null) {
                boolean isValid = checkSqlInject(value.toString(), servletResponse);
                if (!isValid) {
                    return;
                }
            }
        }

        chain.doFilter(requestWrapper, servletResponse);
    }

checkSqlInject方法具体实现如下,通过正则匹配的方式如果检查到当前参数内容存在非法字符,会进行拦截:

private static final String SQL_REGX = ".*(\\b(select|update|and|or|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|drop|execute)\\b).*";

/**
     * 检查SQL注入
     *
     * @param value           参数值
     * @param servletResponse 相应实例
     * @throws IOException      IO异常
     */
    private boolean checkSqlInject(String value, ServletResponse servletResponse) throws IOException {
        if (null != value && value.matches(SQL_REGX)) {
            log.error("您输入的参数有非法字符,请输入正确的参数");
            HttpServletResponse response = (HttpServletResponse) servletResponse;

            Map<String, String> rsp = new HashMap<>();
            rsp.put("code", HttpStatus.BAD_REQUEST.value() + "");
            rsp.put("message", "您输入的参数有非法字符,请输入正确的参数!");

            response.setStatus(HttpStatus.OK.value());
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(objectMapper.writeValueAsString(rsp));
            response.getWriter().flush();
            response.getWriter().close();
            return false;
        }
        return true;
    }

这里过滤的规则比较粗糙,倒是也限制了类似select等关键字,防止进一步的数据获取,从某种意义上也防止了SQL注入的进一步利用。那么有没有办法可以绕过当前的关键字检测呢?从代码上看,这里没有考虑当JSON请求时,过滤器跟Controller JSON请求方式不一致可能导致潜在的参数走私问题。也没有考虑GET请求在特定注解的情况下可以转换成POST进行请求的情况。

image.png

抛开前面提到的思路,还有没有更多的缺陷需要进一步修复呢?下面是具体的分析过程。

0x01 绕过分析

在代码审计时筛选和整理当前应用使用的安全措施是一个非常好的习惯。能更直观的感知整个参数的调用过程。除了SQL注入过滤器以外,应用还存在另外一个拦截器Interceptor。在其preHandle方法中,会使用Jsoup对所有用户输入进行HTML净化,移除潜在的恶意脚本。

@Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 对请求参数进行HTML净化
        for (String key : request.getParameterMap().keySet()) {
            String value = request.getParameter(key);
            value = sanitizeInput(value); 
            request.getParameterMap().replace(key, value);
        }
        return true;
    }

在sanitizeInput中,主要是通过Jsoup的clean方法对用户输入进行处理,clean() 方法可以接收一个HTML字符串,并对其进行清理,移除任何潜在的恶意脚本,只保留安全的HTML标签和属性:

public static String sanitizeInput(String strHtml) {
        String cleaned = "";
        if (StringUtil.isNotBlank(strHtml)){
            cleaned = Jsoup.clean(strHtml, whitelist);
            return cleaned;
        }
        return cleaned;
    }

这里针对SQL和XSS分别使用了Filter和interceptor进行处理。那么有没有可能因为两者的解析顺序不同,可能导致了潜在的绕过风险呢?下面对具体的执行顺序进行简单的分析:

  • 过滤器Filter

过滤器位于请求处理链的最外层,可以拦截请求并进行对应的处理。如果某资源已经配置对应filter进行处理的话,那么每次访问这个资源都会执行doFilter()方法,该方法也是过滤器的核心方法。例如上面SQL注入的风险识别就是基于该方法实现的。

Spring Boot默认内嵌Tomcat作为Web服务器。简单查看Filter的具体调用过程。

Filter调用时会在org.apache.catalina.cor.StandardWrapperValve#invoke()方法中被创建。会通过ApplicationFilterFactory.createFilterChain创建FilterChain:

image.png

查看createFilterChain方法的具体实现,首先检查 servlet 是否为 null,若为 null,表示没有指定Servlet,就没有需要创建的过滤器链。否则根据实际的情况创建一个 ApplicationFilterChain 对象,或者获取已存在的过滤器链对象。而过滤器链对象会负责对一系列的过滤器进行管理:

image.png

接着获取所有的filter的映射对象,在filterMaps中保存的是各个filter的元数据信息,若filterMaps不为null且length不为0,则对前面创建的filterChain进一步的封装,这里首先会获取与当前请求相关的标识信息,例如请求的调度类型(dispatcher)和请求的路径(requestPath):

image.png

然后遍历所有过滤器映射,根据一定的条件判断将匹配的过滤器添加到过滤器链中。条件包括与调度类型的匹配和与请求路径或Servlet名称的匹配:

image.png

最后,返回创建的过滤器链,该过滤器链包含了所有匹配的过滤器。如果没有找到匹配的过滤器,则返回一个空的过滤器链。创建了filterChain之后,就开始执行ApplicationFilterChain的doFilter进行请求的链式处理:

image.png

具体的逻辑在org.apache.catalina.core.ApplicationFilterChain#internalDoFilter方法,这里会通过pos索引判断是否执行完了所有的filter,如果没有,取出当前待执行的索引filter,调用其doFilter方法:

image.png

当所有的filter执行完后,会释放掉过滤器链及其相关资源。然后执行servlet具体的业务模块servlet.service(request, response);

image.png

以上是tomcat中整个Filter的调用过程。

也就是说,过滤器主要在Servlet容器级别处理请求的,会在Spring的其他组件之前执行。在Spring中,DispatcherServlet是前端控制器设计模式的实现,提供Spring Web MVC的集中访问点,而且负责职责的分派。其也是在这个环节中进行解析处理的。业务场景中Controller 中收到的请求,都是经过 Tomcat 容器解析后交给 DispatcherServlet,再由其转交给对应 Controller 的。

  • 拦截器Interceptor(preHandle)

拦截器(Interceptor)是一个设计用于在请求处理流程之前或之后执行的组件。它们可以用于多种目的,包括日志记录、安全控制、事务管理、错误处理等。其可以拦截进入Controller之前的请求,也可以拦截Controller处理完请求之后的响应。

image.png
这里只讨论preHandle方法,其在请求进入Controller之前执行,可以返回一个布尔值,决定是否继续执行后续的Interceptor或Controller。看看具体的调用过程。在DispatcherServlet的解析过程中,找到了拦截器的解析逻辑。

Spring MVC在接收到请求时,会调用DispatcherServlet的service方法进行处理。主要是调用doDispatch方法来获取对应的mappedHandler:

image.png

在getHandler方法中,顺序循环调用HandlerMapping的getHandler方法进行解析:

image.png

这里首先会通过RequestMappingHandlerMapping,在其getHandler方法中通过getHandlerInternal获取handler构建HandlerExecutionChain并返回,这里会添加当前请求相关的所有Interceptor:

image.png

在getHandlerExecutionChain方法中,一开始会创建一个HandlerExecutionChain对象,用于存储处理器和拦截器。然后遍历 adaptedInterceptors 的拦截器集合,如果拦截器是 MappedInterceptor 的实例,并且它的 matches(request) 方法返回 true(表示请求的URL路径匹配该拦截器),则将该拦截器中的实际拦截器添加到 chain 中。否则直接将它添加到 chain 中,无需进行路径匹配:

image.png

最后返回构建好的 HandlerExecutionChain 对象 chain,其中包含了处理程序和相应的拦截器,以便在处理HTTP请求时按照一定的顺序执行这些拦截器操作。处理完后会获取处理器适配器,然后调用applyPreHandle方法进行处理:

image.png

实际就到了执行拦截器前置处理preHandle方法的时候了:

image.png

以上是拦截器Interceptor的大致执行流程。

拦截器的preHandle方法是在DispatcherServlet中进行处理,并且在调用Controller方法之前进行拦截。

也就是说,过滤器Filter的执行顺序要在拦截器Interceptor之前

结合前面的分析,SQL注入主要是对内容进行检查,而XSS则是对恶意的内容进行移除处理。若Filter的解析顺序在Interceptor之前,精简下对应的代码逻辑大致如下:

private static final String SQL_REGX = ".*(\\b(select|update|and|or|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|drop|execute)\\b).*";

public static void main(String[] args) throws Exception {
    String value = "用户输入的内容";
    System.out.println("用户输入:"+value);
    if (null != value && value.matches(SQL_REGX)) {
        throw new Exception("您输入的参数有非法字符,请输入正确的参数");
    }

    String cleaned = Jsoup.clean(value, whitelist);

    System.out.println("最终处理后的内容并交给Controler进行处理:"+cleaned);

}

通过类似selec</script>t的输入即可绕过当前的注入防护,可以看到成功绕过了对应的SQL检测逻辑,并且最终经过处理后成功获取到了理想状态的字符串select:

image.png

0x02 其他

除此之外,很多安全措施还可以通过切面或者直接在Service 层进行实现。相比Filter和Interceptor,切面在方法级别执行。而Service的调用一般都是通过在Controller调用的。

结合上面的分析,可以大概知道,当一个请求到达时,执行顺序是大致如下:

image.png

在实际代码审计过程中,可以结合实际的业务场景,关注对应措施的解析顺序问题(也包括interceptor和filter自身的解析顺序)。可能会有意想不到的惊喜。

今天只要你给我的文章点赞,我私藏的网安学习资料一样免费共享给你们,来看看有哪些东西。

网络安全学习资源分享:

给大家分享我自己学习的一份全套的网络安全学习资料,希望对想学习 网络安全的小伙伴们有帮助!

零基础入门

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

【点击免费领取】CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》

1.学习路线图

在这里插入图片描述

攻击和防守要学的东西也不少,具体要学的东西我都写在了上面的路线图,如果你能学完它们,你去接私活完全没有问题。

2.视频教程

网上虽然也有很多的学习资源,但基本上都残缺不全的,这是我自己录的网安视频教程,上面路线图的每一个知识点,我都有配套的视频讲解。【点击领取视频教程】

在这里插入图片描述

技术文档也是我自己整理的,包括我参加大型网安行动、CTF和挖SRC漏洞的经验和技术要点,电子书也有200多本【点击领取技术文档】

在这里插入图片描述

(都打包成一块的了,不能一一展开,总共300多集)

3.技术文档和电子书

技术文档也是我自己整理的,包括我参加大型网安行动、CTF和挖SRC漏洞的经验和技术要点,电子书也有200多本【点击领取书籍】

在这里插入图片描述

4.工具包、面试题和源码

“工欲善其事必先利其器”我为大家总结出了最受欢迎的几十款款黑客工具。涉及范围主要集中在 信息收集、Android黑客工具、自动化工具、网络钓鱼等,感兴趣的同学不容错过。

在这里插入图片描述

最后就是我这几年整理的网安方面的面试题,如果你是要找网安方面的工作,它们绝对能帮你大忙。

这些题目都是大家在面试深信服、奇安信、腾讯或者其它大厂面试时经常遇到的,如果大家有好的题目或者好的见解欢迎分享。

参考解析:深信服官网、奇安信官网、Freebuf、csdn等

内容特点:条理清晰,含图像化表示更加易懂。

内容概要:包括 内网、操作系统、协议、渗透测试、安服、漏洞、注入、XSS、CSRF、SSRF、文件上传、文件下载、文件包含、XXE、逻辑漏洞、工具、SQLmap、NMAP、BP、MSF…

在这里插入图片描述

👋全套《黑客&网络安全入门&进阶学习资源包》👇👇👇

这份完整版的学习资料已经上传CSDN,也可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

img

  • 17
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值