Apusic权限绕过浅析
真的是浅析
前几天去参加补天了,一直想写但是一直抽不出时间学习,由于漏洞比较简单这里也不过多篇幅的讲解,仅分享一些关键的点,在这里关于权限校验Apusic没有使用第三方框架(毕竟是迫真信创产品)
而是使用了自定义实现的安全性约束(关于什么安全性约束百度搜很多文章了不作搬运工)去实现了访问控制
1234567891011 |
<security-constraint> <display-name>test</display-name> <web-resource-collection> <web-resource-name>protect</web-resource-name> <url-pattern>/protect</url-pattern> <url-pattern>/protect/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> </auth-constraint></security-constraint> |
通过迫真的简单debug过后,发现在com.apusic.web.container.ServletInvocation#preRequest
对security-constraint做了检查
跳过垃圾时间(懒),代码如下很简单,在这里调用了checkResourcePermission
去做权限的检查
12345 |
public boolean checkResourcePermission(HttpServletRequest req) { WebResourcePermission perm = new WebResourcePermission(req); Principal principal = Security.getCurrentUser(); return this.checkPermission(perm, principal);} |
在这里,通过javax.security.jacc.WebResourcePermission去实现了权限的检查
首先在初始化WebResourcePermission中,先将request对象经过getUriMinusContextPath处理后传入父类,在这里我们可以看到uri是通过request.getRequestURI()
去获取之后去除了上下文路径
1234567891011121314151617181920212223242526 |
public WebResourcePermission(HttpServletRequest request) { super(getUriMinusContextPath(request)); this.urlPatternSpec = new URLPatternSpec(super.getName()); this.methodSpec = HttpMethodSpec.getSpec(request.getMethod());}private static String getUriMinusContextPath(HttpServletRequest request) { String uri = request.getRequestURI(); if (uri != null) { String contextPath = request.getContextPath(); int contextLength = contextPath == null ? 0 : contextPath.length(); if (contextLength > 0) { uri = uri.substring(contextLength); } if (uri.equals("/")) { uri = ""; } else { uri = uri.replaceAll(":", "%3A"); } } else { uri = ""; } return uri;} |
调用父类构造函数其实就是将处理好的uri赋值给name
回到WebResourcePermission的构造函数继续往下看,对上面这个name以及请求方法做了赋值,到此构造函数部分执行完毕
12 |
this.urlPatternSpec = new URLPatternSpec(super.getName());this.methodSpec = HttpMethodSpec.getSpec(request.getMethod()); |
接下来就是通过com.apusic.web.container.ConstraintMapper#checkPermission
去做权限校验了,最终会走到javax.security.jacc.WebResourcePermission#implies
处理
有兴趣的可以看看从checkResourcePermission
到implies:358
之间的调用栈
12345678910 |
implies:358, WebResourcePermission (javax.security.jacc)implies:525, PermissionsHash (java.security)implies:182, Permissions (java.security)implies:48, ApplicationPolicy (com.apusic.security.jacc)implies:58, ServerPolicy (com.apusic.security.jacc)checkPermission:272, ConstraintMapper (com.apusic.web.container)checkResourcePermission:255, ConstraintMapper (com.apusic.web.container)checkResourcePermission:1565, WebContainer (com.apusic.web.container)checkSecurityConstraint:310, ServletInvocation (com.apusic.web.container)preRequest:228, ServletInvocation (com.apusic.web.container) |
回到正题,在这里我们可以看到这部分的处理也很简单,一个是对方法的判断,一个是对url的判断,在这里我们看后者即可
继续往下走最终我们可以看到他的比对逻辑,也很简单以/protect/*
为例子
- pattern是否等于path
- patter如果以
/
开头/*
结尾,去除后两位后(/protect)如果长度为0也就是仅为/*
则返回true,反之判断path是否为/protect
或者以/protect
开头
1234567891011121314151617181920 |
if (pattern.equals(path)) { return true;} else { int slash; if (pattern.startsWith("/") && pattern.endsWith("/*")) { pattern = pattern.substring(0, pattern.length() - 2); slash = pattern.length(); if (slash == 0) { return true; } else { return path.startsWith(pattern) && (path.length() == slash || path.substring(slash).startsWith("/")); } } else if (pattern.startsWith("*.")) { slash = path.lastIndexOf(47); int period = path.lastIndexOf(46); return slash >= 0 && period > slash && path.endsWith(pattern.substring(1)); } else { return pattern.equals(DEFAULT_PATTERN); }} |
权限绕过构造
在这里我们关注几个细节一个是url的处理是通过request.getRequestURI()
处理那么在这里不会做url解码,在这里可以作为一个绕过的点
另一个细节从lib当中我们可以看到spring的版本是远古版本,因此alwaysUseFullPath
为false,因此是会做归一化再去做路由匹配
那么结合这两个点我们就可以构造出非常多的绕过姿势,在这里就不列举了
至于RCE的点那就更多了,可自行发现,源码很简单
- source:y4tacker
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论