文章目录
0. 前言
我发现很多文章包括教程,大概套路是:只说有漏洞的点,将有漏洞的点指出,然后分析代码;或者黑盒测试出漏洞之后,然后分析代码。
我认为这是在分析漏洞代码,而非代码审计。代码审计文章或教程应该是从0开始找到漏洞所在,包括思路!
所以这里不管有没有漏洞,我都会把审计过程写出来,因此篇幅会很长,但我认为这样对你会很有帮助。
知其然亦知所以然。
由于篇幅较长,因此我会分几篇进行,本篇是整个系列的第3篇,讲解1个内容:
- 文件上传漏洞审计
本系列文章:
- Java代码审计篇 | ofcms系统审计思路讲解 - 篇1 | 环境搭建、路由机制
- Java代码审计篇 | ofcms系统审计思路讲解 - 篇2 | SQL注入漏洞审计
- Java代码审计篇 | ofcms系统审计思路讲解 - 篇3 | 文件上传漏洞审计
搭建好环境,确定好项目结构之后,按我思路是应该审计项目所使用框架漏洞的,这里关于框架漏洞就放最后篇章来说了,我们想了解下基础漏洞的审计~
文章中有错误点,或者思路上有什么问题的,欢迎师傅们留言指出~
1. 文件上传代码审计【有1处】
文件上传漏洞我们需要着重关注的是文件在被java代码解析到保存下来之间有无验证过滤,因此什么样的上传方式,什么样的保存方式都不重要,大家着重关注代码对文件的验证过滤手段即可。
文件上传代码审计常搜索的关键字如下:
file
upload
write
fileName
filePath
getPart
FileOutputStream
transferTo
getRealPath
FileItem
ServletFileUpload
DiskFileItemFactory
....
1.1 可疑点1【无漏洞】
1.1.1 直接搜索upload关键字
1.1.2 选择第一个,点进去分析一下
类名为UeditorAction
可以在当前页面搜索upload关键字,也可以同时参考该类的所有方法名称。
大概看一下,四个方法的写法是差不多的:
- getFile()方法的第2个参数不太相同,通过参数名称uploadPath可以大概判断出来,这个参数表示的是文件上传路径,也就是说调用不同的方法会保存到不同的目录。
通过下方的msg.put("url", "/upload/image/" + file.getFileName())
也可以大体确定这一点;
当然msg.put不是用来保存文件用的,只是返回的信息而已。
以uploadImage()
方法为例进行分析,可以看到,这个方法内部与保存文件相关的代码就前面两行(后面的都是关于返回的消息相关了),即
UploadFile file = this.getFile("file", "image");
file.getFile().createNewFile();
我们先分析下this.getFile()
方法
1.1.3 分析this.getFile()方法
点击进入看一下:
getFile()
方法,首先调用了getFiles(uploadPath)
,跟进之下:就在上面
可以看到这里先做了个判断,判断request
对象是否是MultipartRequest
类型的对象,如果不是则创建一个新的MultipartRequest
类型的对象,总之就是保证request
对象是MultipartRequest
类型的对象。
然后调用.getFiles()
方法。
额外的:其中request对象就是HttpServletRequest,相关代码如下:
这里目前来看,需要有两个点需要分析:
- 1)
new MultipartRequest(request, uploadPath)
- 2)
request.getFiles()
接下来先分析new MultipartRequest(request, uploadPath)
1.1.4 分析new MultipartRequest(request, uploadPath)
点击进入:
一个构造方法,先调用了父类,然后调用了wrapMultipartRequest()
- 父类可以点击去看看,其实没啥
- 着重看下
wrapMultipartRequest()
点击进入wrapMultipartRequest()
,代码比较长,大概意思已标注:
其中我们需要关注的是文件的过滤代码,也就是图中红框位置:
if (isSafeFile(uploadFile)) {
uploadFiles.add(uploadFile);
}
这里面调用了一个isSafeFile()
方法,进去看一看:
1.1.5 分析isSafeFile()方法
发现这里有了验证过滤:
- 首先是对文件名去空白字符,然后转成小写
- 其次是文件名如果是
.jsp
或者.jspx
结尾则删除,返回false
师傅们可以想一下有无绕过手段
如果没有问题,则将文件add进入uploadFiles
列表中。
1.1.6 分析request.getFiles()方法
然后接下来点进2.1.3步骤中的this.getFile()
方法中的getFiles()
方法进行分析
进来之后,发现方法中没有太多语句,只是直接返回了uploadFiles
,也就是上一步所说的uploadFiles
列表,这里面存放的是经过过滤的文件。
到此为止,其实已经差不多了,已经找到了文件的过滤方式:
- 首先是对文件名去空白字符,然后转成小写
- 其次是文件名如果是
.jsp
或者.jspx
结尾则删除,返回false
刚开始所说的
uploadImage()
方法中的第2条语句 file.getFile().createNewFile();这里没什么,就是通过返回的经过过滤的file对象来创建一个新的文件。
那么接下来可以验证一下:
1.1.7 验证文件的过滤方式,并尝试找到绕过之法(毕竟是黑名单[\偷笑])
根据前面所说的路由机制,可以确定此uploadImage()
方法的调用需要访问admin/ueditor/uploadImage.json
前端找一下呗。怎么找?搜呗…哎真麻烦,说实话好难找啊
根据路径大致判断范围:ueditor,貌似在哪见过这个词?
原来是个富文本编辑器,还好我“见多识广”哈哈
那就在前端找一下哪里有富文本,同时在控制台搜索着ueditor:
经过了九九八十一天,终于找到了。
那admin/ueditor/uploadImage.json
应该就是在富文本上传图片时触发的吧:尝试一下
uploadImage()方法打上断点,yakit也开启抓包拦截
先来个普通图片试一试
大事不妙!之前分析的路由机制不太对,这里怎么是/admin/ueditor/handler.html?action=uploadImage
- 这里我懒得分析了,懂的师傅可以留个言~么么哒
不过没关系,放包!uploadImage()
方法触发了。也就是功能和方法对上了!
在下面也可以看到上传路径在:D:\Program Files\tomcat\apache-tomcat-8.0.17\webapps\ofcms_admin_war\upload\image
当然在返回包中,也可以看到上传路径
访问一下,可以访问到。
接下来尝试绕过一下,已知文件过滤方式
- 首先是对文件名去空白字符,然后转成小写
- 其次是文件名如果是
.jsp
或者.jspx
结尾则删除,返回false
绞尽脑汁~貌似只有一个方式:文件名后面加.绕过,但是这种好像只适用于windows。
来都来了,试一下吧,谁让我现在用的电脑是windows。
这里我直接修改了数据包,将文件名改成了.jsp.
,文件内容用的是冰蝎生成的jsp马子。
这里idea就先不debug了,直接放包,上传成功。
可以到目录中看一下,有没有上传成功:
嘿嘿,成功,同时后缀是.jsp。
接下来就是看看能不能连接了…
测试了下,不太行,虽然能上传,但是不能解析。
这里为了避免中文问题,我改成了webshell.jsp
怎么办?目录穿越试一试,哈哈哈
经过三天三夜的调试,找到了文件名处理的源码(这里过程就不粘出来了,自己可以调一下,如果想看过程,评论区留个言,给兄弟们安排上[\狗头])
- 先计算
/
的位置 - 然后进行原始文件名的截取
即文件名为../../../webshell.jsp.
,最终文件名也将变为webshell.jsp.
1.1.8 这里,这个点就到此结束,总结一下:
- 该位置的文件上传可以任意上传除
.jsp
和.jspx
的文件,但是没什么luan用。 - 当然如果是windows是可以通过加
.
绕过的,不过解析不了。 - 尝试目录穿越,有代码会将路径截取掉。
成果:0day无,分析经验+1
1.2 可疑点2【无漏洞】
分析第二个ComnController
类中的:
进来之后发现,写法和之前是差不多的,同样使用了getFile()
方法,怎么办?放弃!
后面使用了
getFile()
方法的,直接放弃就好了。
换目标!
这里多说一下,其实我们搜的这些,都是jfinal组件中的upload,漏洞基本没有,如果有,那就是组件0Day了。不过分析分析源码也是好的。
1.3 可疑点3【跑偏的漏洞-路径遍历读取.xml文件】
通过upload搜索的,翻了一圈,不是UeditorAction
类,就是ComnController
类,没有别的可疑的了。换关键字搜索。
1.3.1 搜索file关键字
找到一个TemplateController
类中导入了File。进入看看
文件中搜索下file,85个,真不少
进来之后,大概浏览一遍,该类中其中两个方法getTemplates()
和save()
方法中存在文件相关的功能代码,所以这里分开来分析下。先分析getTemplates()
【其实这里不需要分析的,因为它是获取文件用的,没有写入文件保存文件的功能,和文件上传无关】。
既然来都来了,看一下吧~
1.3.2 分析getTemplates()方法
方法代码如下:
首先看前面三行,getPara()
方法获取参数值,获取不到,会有默认值:
点进getPara()
方法,进来一看:欣喜若狂啊
直接通过request.getParameter(name)
获取参数值,也就是说,目录可控啊~
继续往下分析
这一块就没啥了,就是将String类型的路径,封装一下,变成对象。顺便还做了一下验证处理。
- 其中画红框位置表示:pathFile.listFiles表示目录下的文件或目录,然后通过FileFilter做了一下验证,判断是否是目录,是目录的放入dirs列表中。
- 其中的
setAttr()
方法,是将目录对象保存到request对象中。
继续向下分析:这里和之前的差不多,只不过是通过FileFilter限制了白名单文件,即将.html
、.xml
、.css
、.js
的文件放入到了files列表中。
继续向下分析:这段代码作用就是页面上默认显示的模版文件,传入的文件名如果没有则显示files列表中的第一个(所以没有任意文件读取~哈哈哈)
最后返回resource.html或者是index.html
没有文件上漏洞!
1.3.3 柳暗花明又一村
但是别急,回想下,最开始说该方法获取的前端参数可控,即目录可控,那我们能不能不去获取默认路径下的.html
、.xml
、.css
、.js
文件。
直接验证了,前端找到对应功能点,还是那个熟悉的模版~
随便点个模版,yakit拦截下~
默认参数是这样的:
- file_name=contact.html
- dir=/
- dir_name=/
对照下源码,dir是当前目录,file_name是查看的文件
那么修改一下,读取网站下的web.xml试一试
而默认读取的根目录是webapps\ofcms_admin_war\WEB-INF\page\default
所以我们构造dir=../../../../ROOT/WEB-INF
和file_name=web.xml
成功读取!经验+1
1.3.4 多点脑洞
既然这里可以读取web.xml,并且下方可以保存,那我们是不是就可以修改web.xml文件了呢?
尝试修改保存一下,出现请求异常~
直接调试下,在save()方法处打个断点,发现是因为dirs参数值即dirName中存在../
因此被拒绝了。
这里其实很好绕过,聪明的你一点发现了,前面还有个参数:res_path
我们可以修改res_path的值和dirs参数的值,来达到修改web.xml的目的。
这里师傅们可以自己尝试下,因为这里和下面的save()
方法的分析是一样的,就放一起说了。
1.4 可疑点4【有漏洞】
从上一个分析,发现save()
方法其实是有问题的,接下来着重分析一下。
1.4.1 分析save()方法
代码就这么长:
首先看:
这里从前端获取“res_path”的参数值,前面也说过,getPara()
获取的值是可控的。
然后通过res_path
的值决定pathFile
是什么内容,这里其实不用管,因为不管res_path
是什么,pathFile
都是固定的,不是SystemUtile.getSiteTemplateResourcePath()
就是SystemUtile.getSiteTemplatePath()
,没有可变的地方。
接下来是这段,也是核心:
首先从前端获取“dirs”参数的值,不过这个值中不能存在../
,也就是说这个参数不能是利用点了。
然后从前端获取“file_name”参数的值,没有任何限制。
最后从前端获取“file_content”参数的值,仅仅对<
和>
做了转义。
然后就是新建文件,调用FileUtils.writeString(file, fileContent)
写入内容。
writeString
方法如下:直接调用FileOutputStream.write()
写的,没有别的过滤。
分析下来之后,可以利用的点为:file_name和file_content。
文件名和文件内容都可控,这不就是妥妥的任意文件写入(可以理解为任意文件上传)。
1.4.2 利用漏洞写入jsp马
那直接写个jsp马进去!当然,可能解析不了。
file_name和file_content都替换掉,最后别忘了file_content的值使用URL编码一下
- 这里我用的是冰蝎jsp马
成功写入:
访问连接:但是会发现连接失败,看来当前目录下不能直接访问,或者是jsp不解析
继续尝试别的目录:file_name=…/…/…/webshell.jsp
也是失败。
这里尝试在static目录,是可以的。file_name=../../../static/webshell.jsp
小tips:static目录是静态目录,一般文件是可以直接访问。
命令成功执行!撒花~
1.5 最后结尾
最后搜索了其他的类和关键字,没有可以分析利用的点了。师傅们看看就好。
1.5.1 例1:SystemUtile类下
1.5.2 例2:GenUtils类下,都是创建固定名称的文件,并非文件上传
1.5.3 例3:换关键字write搜索,基本都是分析过的,要不就是无用的
文件上传漏洞代码审计到此为止,希望师傅们有所收获,如有问题,评论区留言~
也可扫码加好友,一起进步~