Apusic权限绕过浅析 真的是浅析 前几天去参加补天了,一直想写但是一直抽不出时间学习,由于漏洞比较简单这里也不过多篇幅的讲解,仅分享一些关键的点,在这里关于权限校验Apusic没有使用第三方框架(毕竟是迫真信创产品)
而是使用了自定义实现的安全性约束(关于什么安全性约束百度搜很多文章了不作搬运工)去实现了访问控制
1 2 3 4 5 6 7 8 9 10 11 <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
去做权限的检查
1 2 3 4 5 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()
去获取之后去除了上下文路径
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 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以及请求方法做了赋值,到此构造函数部分执行完毕
1 2 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
之间的调用栈
1 2 3 4 5 6 7 8 9 10 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
开头 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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的点那就更多了,可自行发现,源码很简单