华夏ERP-代码审计
2023-03-09 21:09:36

[TOC]

0x01环境搭建

Release 华夏ERP_v2.3 · jishenghua/jshERP · GitHub

连数据库,改端口,直接run

0x02代码审计

Filter过滤器

image-20230512154239040

  • ignoredUrl:表示被忽略的 URL 的模式,它使用 # 分隔了一些后缀名,如 .css、.js、.jpg、.png、.gif、.ico,这些后缀名的 URL 将不会被 LogCostFilter 过滤。
  • filterPath:表示需要过滤的 URL 的模式,它使用 # 分隔了一些需要过滤的 URL,如 /user/login、/user/registerUser、/v2/api-docs 等。

接着是初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);
}
}
}

获取两个参数·*FILTER_PATH**IGNORED_PATH*,检索其是否有#,并分离然后存入数组

再来看下doFilter函数,这也是来看Filter`过滤器实际的作用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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 userInfo = servletRequest.getSession().getAttribute("user");
if(userInfo!=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");
}

这里理解起来也比较简单

首先是初始化的两个变量,一个请求一个相应,requestUrl获取请求的Url

第一个if

1
2
然后读取`Session`获取用户信息
如果是登录状态则放行

第二个if

1
2
如果Url不为空就判断是否包含"/doc.html","/register.html"和"/login.html"
如果包含就放行

第三个if

1
2
调用`verify`函数,实际上就是来检测`Url`是否是可忽视`Url`
如果是则放行

第四个if

1
2
如果允许Url数组不为空,则检索Url是否在允许列表
如果是则放行

否则跳转到登录界面

可直接访问忽略的urlpath

image-20230512170907517

那么这里考虑两个问题,实际是一个问题

也就是Filter放行的两个数组名单,一个忽略的一个白名单

第一

其中对*ignoredList*的判断是通过一个verify函数,

相当于只匹配regex而不顾及前后缀

image-20230512173140932

这里应该使用endsWith()来判断资源的后缀为js、css等

第二

白名单的判断通过startsWith()的话

是可以通过目录穿越来饶过认证

image-20230512173314096

另外如果Url中存在/doc.html,/register.html,/login.html同样也会放行,也就是可以进行绕过

鉴权绕过

前面我们说了Filter拦截器的处理规则

Url中存在/doc.html,/register.html,/login.html的时候就会直接放行

image-20230519163611007

这里没有session,就会重定向到login.html界面

SQL注入

全局搜索${

来到UserMaooerEx.xml

image-20230517195937230

全局搜索selectByConditionUser

UserMapperEx的接口

image-20230517200021735

接着全局搜索或者查找引用

UsserService的java文件

image-20230517200133414

来到UserComponent

接着找哪里调用了UserService.select方法

image-20230517200820484

这里通过search字段获取userNameloginName并且可控

CommonQueryManager下调用

image-20230518224148428

接着来到ResourceController

这里到了contoller层就可以看前端获取数据了

image-20230518224236337

到用户管理界面进行验证

image-20230517173521606

image-20230517173309096