转载

技术分享|浅谈Java Web漏洞分析

介绍

本文是为和我一样有"Java Web漏洞分析恐惧症"的人准备的,所以前置知识需求很少,只需要能看懂Java代码,有Linux/OSX系统操作基础,有一定耐心去一步步实操即可。基于这个目的,本文会假设读者完全没有Java Web开发经验,所以会从最基础的环境安装开始并且比较啰嗦,有一定基础的读者可以自行配置好环境并跳过相应段落。

限于笔者个人水平(主要)和文章篇幅限制,本文并不会讲解如何挖掘Java Web 0day之类较深入的问题。本文希望达到的初步效果仅是读者在学习之后能在有现成POC情况下自行进行原理分析。如果本文反响不错,后续会考虑再推出进阶篇介绍在无漏洞详情的情况下根据补丁分析出漏洞利用方法。

本文主要解决的"难点"列举如下,这些也是笔者自己分析过程中遇到的一些稍难解决的问题:

1. 环境配置中的一些小坑 如源码关联/server建立/软件版本等。       

2. ognl表达式/struts2基础。

3. 开始时在哪里断点/为什么会执行这个ognl表达式(代码逻辑是啥) / 调试的时候哪些语句应该跟进,哪些可以跳过。

环境搭建

本文将以S2-045(CVE-2017-5638)漏洞作为示例进行分析。使用这个上古漏洞来做案例的原因有几个:

1. 这个漏洞非常老,所以可以搜到许多参考资料,如果本文有不清楚的地方,也可以很方便的查到结果。

2. payload编写较其他Java Web漏洞简单一些,适合新手。

在分析之前,首先要能够在IDE里跑起来Struts2,至少能加断点调试,所以需要安装如下软件(本文实验环境是OSX系统,但是对于Linux系统操作也是一样的):

1. JDK

2. Apache  Tomcat

3. IDE

4. Struts2

第一步安装JDK1.8,这一步实在太过简单,读者可以搜到无数相关文章,所以此处不再赘述。安装完成后在命令行执行javac,能看到帮助信息就说明安装成功了。注意这里需要安装的版本是1.8,如果安装了最新的11可能会在配合Tomcat/Struts2使用时有些问题。或者也可以两个都安装,然后在配置里指定本项目使用1.8版本,但是对于初学者,还是直接安装1.8较好。

第二步安装Tomcat,首先去官网下载安装包6.0.9 下载地址,解压到任意合适的目录下,进入bin目录下运行

chmod +x *.sh

/startup.sh

即可启动Tomcat,访问下本地页面能看到熟悉的Tomcat logo就说明成功了。然后可以在本地设置一个CATALINA_HOME环境变量,值就是这里Tomcat的目录地址。值得注意的是笔者使用的Tomcat版本是6.0.9,一个比较老的版本,其他版本没有测试,应该也是可以的,但还是建议使用这个版本。

第三步安装IDE,IDE有很多选择,笔者使用的是目前比较常用的IntelliJ IDEA,这样如果有疑问会比较方便搜索。这个安装过程就不再赘述,也是一直下一步即可。注意网上有很多的汉化包,建议不要安装,首先是需要看懂的英文其实很少,并不难记,二是遇到问题的时候搜索得到的结果往往是按照英文界面讲解的,到时候反而可能比较难找到具体的栏目。

第四步Struts2代码下载,在这里下载,解压到任意目录下就行了。

接下来就是把这几个软件放在一起工作了。

下面介绍的代码部署思路是直接用官方生成的war包进行调试,本地仅仅进行库和代码的关联。这种部署方法好处是简便,也省去maven下载编译的时间,缺点是不能真的修改代码来改变逻辑。其实还有另一种部署方法,就是直接导入官方代码中的源码,路径( struts-2.3.20/src/apps/showcase ),做为maven项目导入,然后配置好服务器就可以用了,缺点是还要学习maven的配置稍微复杂点,优点是能直接修改代码逻辑。本文仅介绍第一种方法,如果对第二种方法有兴趣,可以按照上面的描述自己探索下。

首先在IDE里新建Server环境。从菜单里打开 Preferences ,在左上角的搜索栏输入 server ,找到出现的 Application Servers 栏,点进去之后点击加号进行新建服务器,选择 Tomcat Server ,填入本地目录地址等信息即可。出来时候点击OK保存设置。结果如下:

技术分享|浅谈Java Web漏洞分析

然后是配置项目代码到IDE中。找到刚才保存Struts2代码的地址,进入 struts-2.3.20/src/apps/showcase 目录,把本目录拷贝到任意合适的目录下。直接用IDE打开此目录,然后在最左侧的项目结构窗口里右击根目录名,找到其中的 Open Module Setting 选项。首先打开左侧 Project 选项,配置SDK为当前的1.8 SDK,并点击 Apply 保存。之后选择左侧 Libraries 选项,点击右侧加号选择 Java ,选择当前代码的 WEB-INF/lib 目录。完成后应当如下所示:

技术分享|浅谈Java Web漏洞分析

这里有一个需要注意的地方了,就是需要将源码和Jar包关联起来,否则就无法调试了,还是在这个界面,点击最下方的最左侧加号来添加关联源码目录地址,选择Strtus2代码目录下的src目录,点选OK即可。再点击最右侧有红点的加号,选择当前lib目录。这里还有 Artifacts 需要配置,选择添加一个 Web Application: Exploded 类型的Artifact,名字随便,之后再添加 Directory Content ,主要是包含进去 WEB-INF META-INF 两个目录。

接来下配置Tomcat。关掉刚才的窗口,在主界面点击右侧的 Add Configuration (在绿色的运行按钮旁),在弹出的 Run/Debug Configurations 窗口点加号,选择Tomcat Server下的Local,在右边 Application Server 里选择刚才的6.0.9的服务器。然后选择打开的URL是

http://localhost:8080/showcase/index.action

最后记得要把war包拷贝到tomcat的webapps目录下,名字改为showcase.war。现在点击右上角的绿色Run按钮,等上一会就会打开Struts官方测试应用的页面了:

技术分享|浅谈Java Web漏洞分析

Struts简介

在进行实际的分析前,还需要对Struts有一些简单的了解。如下是Struts官方给出的设计架构图:

技术分享|浅谈Java Web漏洞分析

可以看到请求会经过很多的Filter,然后才会通过ActionMapper送到真正的业务逻辑中。而Filter其实就是对原请求进行各种修改/添加的操作。Struts将Action和具体操作类(Class)关联起来的方法是配置文件,名字是struts.xml,本项目路径是/WEB-INF/src/java/struts.xml,这里会给一个action的名字匹配到具体的操作类上。

OGNL是一种表达式语言,并且功能很强大,可以调用Java对象树中的任意属性、方法。目前了解这些就差不多了。

逐步分析

代码分析的主要问题之一就是在哪里下断点,而第一个断点下在哪里就是这个问题的关键,因为我们目前的假设是,手头有POC,了解大致问题出现在文件上传处,所以可以简单点。首先,运行起来项目,然后找到这个Demo项目的文件上传页面

http://localhost:8080/showcase/fileupload/doUpload.action

(虽然这个POC不一定需要有真正的上传,但是这样比较容易理解)。然后直接对这个地址运行POC,这样在Tomcat的报错信息里就会看到一些具体出错的文件地址和行号:

技术分享|浅谈Java Web漏洞分析

我们从上往下看,找到第一个属于Struts2的报错文件(从上往下第二个红框)是

StrutsPrepareFilter.java:88 ,进入这个文件,转到88行在这里下断点,开始调试(下文调试有时要F7有时则是F8,有耐心可以尝试全部F7,否则也可以按我给出的行数直接下断点,但是不建议)。注意现在这个断点是Struts2的入口,所以所有请求都会触发断点,下面调试最好不要用浏览器打开了,而是直接用POC去打。同样,因为这个断点是入口,所以其实要跟进到最后的触发点其实还要很久,如果比较急也可以直接在刚才报错信息的最上一个地方(第一个红框)下断点,但是这样就错过了对整个请求流程的观察,对新手可能不太好。

现在再用POC打一下刚才的地址,终于停在了断点处。可以看到这里应该是刚才架构图里的Filter功能,会对原始请求进行修改。一直F8,直到文件 /src/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java 835行:

技术分享|浅谈Java Web漏洞分析

看到这里在取content-type,感觉到地方了(因为payload就在content-type里),先加个断点。再看代码,看到这里会判断必须包含 multipart/form-data ,这就是为啥POC里会有个(#nike='multipart/form-data'),否则无法进入这个逻辑。

继续一直F8和F7,直到进入文件 src/core/src/main/java/org/apache/struts2/dispatcher/multipart/JakartaMultiPartRequest.java 127行,发现这里进入了错误处理,跟进 buildErrorMessage ,接着进入 LocalizedTextUtil.findText ,发现这个函数里传进了我们的Payload,跟进 findText 。但是发现这个函数挺长,实在不想一个个跟了,所以直接找到Payload存在的变量出现的地方,在542行,于是直接下断点,跟进函数 getDefaultMessage 。就这样一路跟着Payload,终于到了这里:

技术分享|浅谈Java Web漏洞分析

继续跟进几步,就能发现,这里就是最终转换错误信息中的OGNL表达式并真正执行的函数了。

到这里,就完成了初步的漏洞分析,虽然上面的内容大部分都是环境搭建,看起来调试环节很少,但实际上调试还是会遇到很多问题的。其中最主要的问题就是,经常懒得跟进函数,结果错过sink点。解决方法其实也很简单,就是尽量全部F7,除非非常确定这个函数是干什么的,否则就跟进去看看;同时,也可以多下断点,发现自己走错了就直接快速重来,也能节省很多时间。

技术分享|浅谈Java Web漏洞分析

原文  https://mp.weixin.qq.com/s/S4UEHy-JTVJ23VcPjooqcQ
正文到此结束
Loading...