jira环境搭建及受限文件读取原理和深思CVE-2021-26086
s3gundo 技术文章 8278浏览 · 2022-03-01 15:54

jira环境搭建及受限文件读取原理和深思CVE-2021-26086

一、环境搭建踩坑

坑太多了 装了差不多七八个小时才装好 也是给自己找一些经验。不过不得不说在装软件的同时也学到了非常多的东西,实战的项目感觉就是会有不一样的感觉。多动手总是会有好处的。

我先发几个步骤 然后罗列一下我猜到的一些坑的点,算是把这个环境给装好了。因为我是mac系统,参照着一个步骤来的,但是中间夹着一些另外的,使用通用的破解插件。

一、安装步骤

https://blog.csdn.net/pang787559613/article/details/101269073

https://www.jianshu.com/p/da0ddd124be8

前半部分基本按照第一个链接,后半部分按照第二个链接进行配置:

二、坑点

其实为了兼容后面的软件 mysql5.7的安装是比较好的,既可以兼容前面老版本的软件,后面的新版也会兼容这个。感觉相当于java8一样地位的存在。在这里我选取的是5.7.31 并且不是用homebrew安装的(感觉坑还挺多的)

一、mysql我在安装的过程中其实会遇到经典问题,就是第一次登录会拒绝登录,所以一般先安全模式启动,然后修改密码并flush privilege就可以了。

二、mac上面mysql的安装默认不会生成/etc/my.cnf的配置文件,需要自己touch一个并自己写一个默认的配置。 配置上面jira默认需要的字符集和ssl的问题。

因为emoji等表情符号的出现,更广泛的编码集需要拥抱时代的变化,所以我们尽可能的再去抛弃utf8转向utf8mb4,就像jira那样(utf8mb4是utf8的超集,理论上由utf8升级到utf8mb4字符编码没有任何兼容问题)

https://confluence.atlassian.com/adminjiraserver/connecting-jira-applications-to-mysql-5-7-966063305.html
这是字符集的解决办法 注意mysqld和client下面对应的配置别写反了。

  [client]
  #jira config
  default-character-set = utf8mb4


  default-character-set=utf8mb4
  #password   = your_password  
  port        = 3306  
  socket      = /tmp/mysql.sock   
  # Here follows entries for some specific programs  
  # The MySQL server  
  [mysqld]
  character-set-client-handshake = FALSE
  character-set-server = utf8mb4
  collation-server = utf8mb4_unicode_ci
  default-storage-engine=INNODB
  character_set_server=utf8mb4
  innodb_default_row_format=DYNAMIC
  innodb_large_prefix=ON
  innodb_file_format=Barracuda
  innodb_log_file_size=2G
  skip_ssl #这里是忽略ssl安全连接的问题

这是创建jira对应数据库时 添加所需要的数据集
CREATE DATABASE Jira CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
ALTER DATABASE Jira DEFAULT CHARACTER SET = utf8mb4 DEFAULT COLLATE = utf8mb4_bin;

检查修改
mysql> SHOW VARIABLES WHERE Variable_name LIKE 'character\_set\_%' OR Variable_name LIKE 'collation%';
修复&优化所有数据表
> mysqlcheck -u root -p --auto-repair --optimize --all-databases

mac上面mysql的重启命令
> sudo /usr/local/mysql/support-files/mysql.server restart

三、之前一直按照前面的步骤使用试用版的密钥,后来我去官网上面查看,使用密钥是不需要联网的,所以不是网址被ban的问题(况且我还挂了全局代理)。之后我将问题翻译为英文,去jira的社区看看:

We're unable to confirm that Jira license
https://community.atlassian.com/t5/Jira-Software-questions/We-re-unable-to-confirm-that-Jira-license/qaq-p/1211749
https://community.atlassian.com/t5/Jira-Software-questions/why-I-have-got-unconfirm-the-JIRA-license-message-even-I-just/qaq-p/638673 时区的问题

得到的答案就是 应该不太存在这种情况,建议看看日志
You can find these in $JIRAHOME/log/atlassian-jira.log 
 $JIRAINSTALL/logs/catalina.out file. 

 然后我就去翻看了这两个日志,发现没有激活相关的错误,但是我看到了其他的错误,我之前设置了mysql不需要验证ssl,但是jira去链接mysql的时候还是建立了安全的连接。是在自己的jirahome下的dbconfig.xml,并且注意使用&连接必须要编码
 <url>jdbc:mysql://address=(protocol=tcp)(host=localhost)(port=3306)/jira?useUnicode=true&useSSL=false&characterEncoding=UTF8&sessionVariables=default_storage_engine=InnoDB</url>

然后去找了一下插件的gitee,发现这个插件自带了kengen的功能,这个是插件里面的破解步骤:

https://www.cnblogs.com/sanduzxcvbnm/p/13809276.html

我输入的:

java -jar atlassian-agent.jar -d -m 76xxxxx77@qq.com -n s3gundo -p jira -o http://127.0.0.1:8080 -s BRX3-TPH5-YVOW-XXXX 
之后会得到

综上,一些jira社区帮助我解决问题的url

https://community.atlassian.com/t5/Jira-Software-questions/The-database-setup-is-not-supporting-utf8mb4/qaq-p/1012877
https://community.atlassian.com/t5/Jira-Software-questions/why-I-have-got-unconfirm-the-JIRA-license-message-even-I-just/qaq-p/638673
https://community.atlassian.com/t5/Jira-Software-questions/We-re-unable-to-confirm-that-Jira-license/qaq-p/1211749
https://community.atlassian.com/t5/Jira-Software-questions/WARN-Establishing-SSL-connection-without-server-s-identity/qaq-p/1015860
https://community.atlassian.com/t5/Confluence-questions/SSL-errors-with-confluence-and-MySQL/qaq-p/578128
https://community.atlassian.com/t5/Jira-Software-questions/why-I-have-got-unconfirm-the-JIRA-license-message-even-I-just/qaq-p/638673
https://community.atlassian.com/t5/Jira-Software-questions/We-re-unable-to-confirm-that-Jira-license/qaq-p/1211749
https://community.atlassian.com/t5/Jira-Software-questions/The-database-setup-is-not-supporting-utf8mb4/qaq-p/1012877

Atlassian家族插件

https://gitee.com/pengzhile/atlassian-agent
请支持正版

二、漏洞复现调试

这里的jira home是我们之前设置过了的,然后把web-inf下面的lib添加到库就可以了,我一般是整个文件夹直接导入。

windows配置
set JAVA_OPTS=%JAVA_OPTS% -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=0.0.0.0:5005 

catlina.sh linux配置
CATALINA_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,address=60222,suspend=n,server=y"

CVE-2021-26086 受限文件读取挖掘分析

参考文章: https://tttang.com/archive/1323/ 梅子酒师傅的
师傅的文章中可能有两处笔误。1、是对url路径的解析2、jiraloginfilter的问题

https://xz.aliyun.com/t/10444 藏青师傅的

先放poc

/s/xx/_/;/WEB-INF/web.xml 
/s/xx/_/;/WEB-INF/decorators.xml 
/s/xx/_/;/WEB-INF/classes/seraph-config.xml 
/s/xx/_/;/META-INF/maven/com.atlassian.jira/jira-webapp-dist/pom.properties 
/s/xx/_/;/META-INF/maven/com.atlassian.jira/jira-webapp-dist/pom.xml 
/s/xx/_/;/META-INF/maven/com.atlassian.jira/atlassian-jira-webapp/pom.xml 
/s/xx/_/;/META-INF/maven/com.atlassian.jira/atlassian-jira-webapp/pom.properties

稍加改造/s/everything/_/;anythingulike/WEB-INF/web.xml

因为burp抓不到localhost和127.0.0.1的的包,我们得先抓自己本地ip的包,但是我们之前设置jira的时候,ip是设置成localhost的,我们在burp上面的右上角把ip地址更改一下,然后host的值也改一下。就可以读取到文件了。

这里的payload我放为/s/s3gundo/_/;anythingulike/WEB-INF/web.xml,具体的分析可以看下面。

1、 filter的初始化

复习一下filter的初始化

org.apache.catalina.Valve#invoke ->StandardWrapperValve.invoke
    StandardWrapperValve ->> + ApplicationFilterFactory : 1、createFilterChain()创建FilterChain
    ApplicationFilterFactory ->> ApplicationFilterFactory : 1.1、创建FilterChain并初始化(servlet设置到FilterChain当中)从配置文件中读取的filtermap并匹配查找后返回
    ApplicationFilterFactory -->> - StandardWrapperValve : 1.2、返回FilterChain对象

filterchain的初始化,跟进ApplicationFilterFactory.createFilterChain方法,可以看到从wrapper中获取的http请求方法和路径,并将filtermap中匹配得到的路径与请求方法,加入到filterChain中

可以看到urlpattern是/*是肯定会被匹配上的。org.apache.catalina.core.ApplicationFilterChain#doFilter匹配得到这些filters

可以看到序号九,第十个filter就是后面的重点。

2、 Jira的正常访问/WEB-INF/受限

可以看到org.apache.catalina.core.StandardContextValve#invoke方法中,

在这里,应该是会将访问路径中的;进行忽略处理,比如对于路径/s/s3gundo/_/;anythingulike/WEB-INF/web.xml将会首先取;前的/s/s3gundo/_/,再取/后的/WEB-INF/web.xml,最后将两者进行拼接得到:/s/s3gundo/_//WEB-INF/web.xml。因为这里传入的时候对url做了转发处理,所以将前面的/s/s3gundo给删去了,得到/;anythinulike/WEB-INF/web.xml,后面会讲到。

返回的值是//WEB-INF/web.xml

public static String normalize(String path, boolean replaceBackSlash) { //这里传入的时候是为true
        if (path == null) {
            return null;
        } else {
            String normalized = path;
            if (replaceBackSlash && path.indexOf(92) >= 0) {
                normalized = path.replace('\\', '/');  //存在反斜杠就替换为斜杠
            }

            if (!normalized.startsWith("/")) {
                normalized = "/" + normalized;
            }

            boolean addedTrailingSlash = false;
            if (normalized.endsWith("/.") || normalized.endsWith("/..")) {
                normalized = normalized + "/";
                addedTrailingSlash = true;
            }

            while(true) {
                int index = normalized.indexOf("//");
                if (index < 0) {
                    while(true) {
                        index = normalized.indexOf("/./");
                        if (index < 0) {
                            while(true) {
                                index = normalized.indexOf("/../");
                                if (index < 0) {
                                    if (normalized.length() > 1 && addedTrailingSlash) {
                                        normalized = normalized.substring(0, normalized.length() - 1);
                                    }

                                    return normalized;
                                }

                                if (index == 0) {
                                    return null;
                                }

                                int index2 = normalized.lastIndexOf(47, index - 1);
                                normalized = normalized.substring(0, index2) + normalized.substring(index + 3);
                            }
                        }

                        normalized = normalized.substring(0, index) + normalized.substring(index + 2);
                    }
                }

                normalized = normalized.substring(0, index) + normalized.substring(index + 1); //把双斜杠替换为单斜杠
            }
        }
    }
}

最后经过normlize的返回是/WEB-INF/web.xml

3、 UrlRewriteFilter

这块主要分为两大部分,一是org.tuckey.web.filters.urlrewrite.RuleChain#process,二是org.tuckey.web.filters.urlrewrite.RuleChain#handleRewrite。逐个攻破

先是process方法:

关键在org.tuckey.web.filters.urlrewrite.ClassRule#matches(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)方法中箭头所指向的反射方法,matchstr默认为matches,然后得到matchesMethod的方法为public org.tuckey.web.filters.urlrewrite.extend.RewriteMatch com.atlassian.jira.plugin.webresource.CachingResourceDownloadRewriteRule.matches(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse),再将所需的参数传入。

先来来看看匹配的模式

^/s/(.*)/_/((?i)(?!WEB-INF)(?!META-INF).*)

前面的(?i)表示是一种模式修饰符,i即匹配时不区分大小写。以前只见过放在最后面的。

后面的(?!)表示在那串字符串后面的不能是以web-infmeta-inf结尾的。

至此,调用的堆栈是:

matches:53, CachingResourceDownloadRewriteRule (com.atlassian.jira.plugin.webresource)
invoke:-1, GeneratedMethodAccessor308 (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
matches:119, ClassRule (org.tuckey.web.filters.urlrewrite)
matches:101, ClassRule (org.tuckey.web.filters.urlrewrite)
doRuleProcessing:83, RuleChain (org.tuckey.web.filters.urlrewrite)
process:137, RuleChain (org.tuckey.web.filters.urlrewrite) //上班部分process的
doRules:144, RuleChain (org.tuckey.web.filters.urlrewrite)
processRequest:92, UrlRewriter (org.tuckey.web.filters.urlrewrite)
doFilter:394, UrlRewriteFilter (org.tuckey.web.filters.urlrewrite)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core) [10]
doFilter:30, CorrelationIdPopulatorFilter (com.atlassian.jira.servermetrics)
doFilter:32, AbstractHttpFilter (com.atlassian.core.filters)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
...(省略)
doFilterInternal:115, GzipFilter (com.atlassian.gzipfilter)
doFilter:92, GzipFilter (com.atlassian.gzipfilter)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core) [1]
invoke:199, StandardWrapperValve (org.apache.catalina.core)
invoke:96, StandardContextValve (org.apache.catalina.core)
invoke:493, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:206, StuckThreadDetectionValve (org.apache.catalina.valves)
invoke:137, StandardHostValve (org.apache.catalina.core)
invoke:81, ErrorReportValve (org.apache.catalina.valves)
invoke:87, StandardEngineValve (org.apache.catalina.core)
invoke:660, AbstractAccessLogValve (org.apache.catalina.valves)
service:343, CoyoteAdapter (org.apache.catalina.connector)
service:798, Http11Processor (org.apache.coyote.http11)
process:66, AbstractProcessorLight (org.apache.coyote)
process:808, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1498, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1149, ThreadPoolExecutor (java.util.concurrent)
run:624, ThreadPoolExecutor$Worker (java.util.concurrent)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:748, Thread (java.lang)

后面有的dontProcessAnyMoreRules把ruleIdxToRun赋值为rule.size(),才后面就会跳出判断,不再进行匹配。

至此,process方法结束,接下来是handleRewrite,这部分主要是请求转发

简单了解一下请求转发的作用域:

访问受保护目录下的资源
requestDispatcher:是服务器的资源封装器,可以封装服务器内部所有资源。
(包括WEB-INF下资源)
WEB-INF是受保护目录,不能够通过浏览器直接访问
可以通过请求转发去访问

可以看到10-11之间的调用堆栈,这里具体是对请求进行了一次转发。

于是接下来对请求直接进行了dofilter的操作,从而没有经过org.apache.catalina.core.StandardContextValve#invoke,个人认为请求转发作用域延伸到受保护目录下的资源也是因为如此。

这也导致了第二次访问是由defaultServlet对资源进行的请求,也可以看到这里面filterconfig里面仍然是存在JiraLoginFilter的,因为在web.xml中就已经配置全路径了

4、 JiraLoginFilter放行

看dofilter方法中第一行,这里是函数式接口,能够获取到SessionInvalidator并且存在的话,将这个值符给jiraUserSessionInvalidator这个参数,并执行handleSessionInvalidation方法。这里获取到存在的变量是jiraUserSessionTracker,所以后面执行的方法是com.atlassian.jira.web.session.currentusers.JiraUserSessionInvalidator#handleSessionInvalidation

此处session是为空的,因为我们还没有登录,执行到finally块,判断完其实这里什么都没做。

接下来走到选择filter过滤器再进行doFilter的方法,因为这俩参数都没传,所以会传seraphHttpAuthFilter参数回去,执行他的dofilter方法。

走到HttpAuthFilter父类的方法

看到status为空,所以两个return的块我们也进不去,所以走到最后一行代码继续放行。不做未认证的跳转也返回值,所以最终会交到DefaultServlet的手上。

局限

传入的解析完之后的参数是/WEB-INF/web.xml,局限也就是在于下面部分,会再次去资源进行一个normalize的处理,导致不能跨越web路径进行一个资源的读取,只能在web的路径之下。

file会将web目录的路径和我们请求的绝对路径进行拼接

之后再进行一次normlize的方法,在后面的getResource方法中和web路径进行拼接的时候,也就达不到跨越web路径的目的。

修复

对正则进行了删改

Pattern PATHS_DENIED = Pattern.compile("[^a-zA-Z0-9]((?i)(WEB-INF)|(META-INF))[^a-zA-Z0-9]")

也就是 WEB-INF 或者 META-INF 的前后有特殊字符,则返回 null;

三、总结

1、在渗透测试的过程中,有些waf会拦截;/等关键词,在中间填充字符串可绕过某些特征。

2、做请求转发的操作时,一定要再对url进行过滤的操作,防止读取到敏感文件(尤其是做动态链接)

文笔很烂,如有错误,请多多指教。

2 条评论
某人
表情
可输入 255