本文承接之前的两篇:
使用JPDA调试Tomcat(一)
使用JPDA调试Tomcat(二)
在本文中,我们继续动态调试工作:
动态调试
在
前一篇文章中,我们在IntelliJ中完成了对调试环境的配置,创建了tc6-jpda这个profile。
点击这个bug icon:
可以在下面的console窗口看到,IntelliJ已经连接到了Tomcat的8000调试端口上面:
一切准备就绪,我们打开浏览器,重现BUG。首先访问
http://127.0.0.1:8080/secured-webapp/admin,用户名及密码使用user/user:
点击Login,此时,IntelliJ窗口会弹出到最前面,并显示Tomcat停到了断点上面:
这就是JPDA的强大之处-它是一个基于网络协议的调试架构。我们顺便看一下IntelliJ下面的调试窗口。IntelliJ的调试窗口共分为三个部分,我们从左到右依次查看:
如上图所示,最左边是StackTrace,我们可以很方便地看到程序是怎样一层一层执行到断点,这在分析复杂问题的时候,非常有用。比如在这里,我们可以看到Tomcat是怎么样从SingleSignOnValve一步一步执行到RealmBase中的断点处的。因为本文的重点是介绍工作方法,而不是分析Tomcat的技术细节,所以这里不展开讨论。但是你在对自己手里的项目进行调试的时候,必须要对项目的设计架构和源代码有相当的熟悉程度才可以,这个学习和分析工作是最花时间的。
接下来是参数列表:
IntelliJ帮我们把当前Scope下的参数及其Value都列在了这里,方便分析代码。接下来是Watches窗口:
我们在这里可以添加一些默认的参数列表中没有监测到的变量,也可以添加表达式。在这个具体的问题中,我们关心的是这个:
if (requestURI.endsWith(Constants.FORM_ACTION))
所以,可以在Watches里面点+号,添加这个表达式:
这样我们就可以很方便地检查这个表达式的判断结果:
总结上面的分析过程,并点击Go按钮,让Tomcat继续服务:
此时浏览器这边会返回这个页面:
接下来我们访问这个地址,重现BUG:
http://127.0.0.1:8080/secured-webapp/admin/j_security_check
这个时候IntelliJ再次把Tomcat停到这个断点上面,这次看到不同的是,Watches中的表达式判断变成了true,于是点击单步执行的按钮:
可以很清楚地看到,hasResourcePermission方法直接返回true,bypass掉了权限检查:
点击Go按钮,让程序继续执行,在浏览器里面,我们看到user用户能够进到了Admin的页面:
Okay,动态调试及验证过程到这里就算是完成了。 接下来做一些清理收尾工作,去掉断点,点击Stop按钮,结束调试过程:
最后不要忘记停掉Tomcat:
我们接下来试着Fix这个BUG:
修复BUG
这个BUG比较容易,我们删掉这段多余的代码就可以了:
接下来编译Tomcat的源代码。找到源码中的BUILDING.txt,看一下应该如何编译Tomcat的源代码:
编译过程取决于你的机器速度,完成后,会显示编译成功:
接下来要做的是找找这个Patch过的RealmBase被封装在哪个jar里面,因为我们只需要把改变过的jar替换到运行的Tomcat当中,而不需要把全部的组件全部替换:
从上面的图中可以看出,catalina.jar中包含RealmBase,可以具体验证一下:
确定了就是这个jar包含RealmBase.class。我们将这个jar拷贝到在使用的tomcat中,替换原有的:
然后启动Tomcat:
接下来的工作是验证BUG已经被修复。
验证PATCH有效性
首先我们访问照例访问
http://127.0.0.1:8080/secured-webapp/admin,并使用user登录:
登录后照例出现权限不足的页面:
接下来我们要验证这个地址:
http://127.0.0.1:8080/secured-webapp/admin/j_security_check,确保权限认证没有被bypass掉:
成功!我们的PATCH生效了,user不再能通过这个漏洞访问admin的页面。
CVE
对于Tomcat这样的成熟社区项目,一般类似这样的BUG都会被很快发现,并会产生一个标准的CVE安全报告。比如我们在本文中用到的这个BUG,它的CVE报告在这里:
http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-3546
在里面有对问题的描述,以及相关的PATCH。我们看一下报告中link的PATCH:
http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/realm/RealmBase.java?r1=1377892&r2=1377891&pathrev=1377892
可以看到和我们分析的结果是一样的。所以说,给社区中的项目做开发,并没有想象的那么难对不对?
总结
在这套文章中,我介绍了我在社区中进行日常工作中的流程,使用到的工具,分析问题的思路和方法。在实际的工作过程中,遇到的问题难易程度不一样,但分析问题的方法万变不离其踪。
此外,本文中使用IntelliJ做为调试环境,如果你用Eclipse,可以参考我写的这篇文章:
灵活使用Eclipse与Java远程调试功能