漏洞复现
复现前准备:
1.在对应目录下创建一个1.txt,并随便写一些东西
2.你想下载哪个目录下的1.txt就更改ruoYiConfig.setProfile('C://Users//luche//Desktop//1.txt')里的格式
复现过程:
调用目标字符串: ruoYiConfig.setProfile('c://1.txt')这里可更改为本地环境下的路径,例如我这里改为了ruoYiConfig.setProfile('C://Users//luche//Desktop//1.txt')就表示在该路径下,下载应用。 注意使用双反引号,因为单反引号可能会出错 |
cron表达式: 0/10 * * * * ? |
最后访问下载地址:
http://127.0.0.1:88/common/download/resource?resource=C://1.txt
即可成功下载文件
代码审计
接下来我们进入代码审计的环节,我将一句一句的解析每一段代码,尽量保证每个人都能看懂。因为我
自己本身就是一个小白。所以更懂大家。
首先找到该页面的接口调用代码
从代码的层面可以看到,只有当if (!FileUtils.checkAllowDownload(resource))为不为true的时候才会进入到下载的方法。
我们首先传入的值,是传入到String resource。其他两个值可以不用管
那么我们进入FileUtils.checkAllowDownload,查看如何满足条件
通过方法追踪,成功找到该方法
注意前面方法中的逻辑是:if (!FileUtils.checkAllowDownload(resource)),if中有!反逻辑号。所以当FileUtils.checkAllowDownload(resource)
返回为false的时候,其实是执行语句,也就是不允许下载。
那我们回到checkAllowDownload()方法中
只有当true的时候才可以下载目录。我们将这段代码解释一下,都写上注释了。
可以看到if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource)))是关键,只有当这个判断返回true,才可以下载。
那么我们进入到ArrayUtils.contains中的两个参数去查看具体的代码
那么我们进入到两个变量去查看
MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION
FileTypeUtils.getFileType(resource)
这里查看public static String getFileType(String fileName)的代码片段
通过MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION方法看到,可以任意下载的文件有图片、压缩文件、pdf、视频、word等。
那么举一反三,其实这个漏洞不仅仅是下载txt文件,想下载其他文件也可以通过该漏洞。
但也侧面说明了,该漏洞并不是任意文件下载,此任意文件下载还是有限制的。
细心的朋友可能已经发现FileTypeUtils类中,有两个一样名称的方法
这里涉及到一个JAVA的特性
方法重载
1.方法重载允许一个类中定义多个方法,方法名相同,但它们的参数列表必须不同(可以是参数的数 量、类型,或者顺序不同)。
2.当调用这些方法时,Java 编译器会根据传入的参数类型或数量来选择调用哪个重载的方法。
在这里,一个接受 File 对象,另一个接受 String 类型的文件名。
根据传入参数的类型,Java 编译器会自动选择调用哪个方法。
那在这个地方,我们传入的是什么?回到传入变量的地方,接口的地方,很明显,我们传入的是字符串类型
所以要关注的方法只有下面的这个
我们给这段方法加上注释
那么逻辑的很清晰了
假如我们要下载一个1.txt的文件,首先传入resourceDownload()函数中,然后将变量resource传入FileUtils.checkAllowDownload()中。
接着在checkAllowDownload()函数中判断是否可以下载文件【if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource)))】语句,那么变量就会进入FileTypeUtils.getFileType()函数中。
在该函数中成功传入getFileType(String fileName)函数中,通过语句判断,将传入的变量名如1.txt,的扩展名txt提取出来。
提取完成后,回到【if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource)))】返回true
返回true后成功跳过if (!FileUtils.checkAllowDownload(resource))该if语句判断
至此,我们终于过了第一个流程
此流程我们得到的情报有:
首先路径不能包含 .. 因为这样会进入到下载失败的判断
第二 能下载的文件只有DEFAULT_ALLOWED_EXTENSION数组中的,否则下载失败
// 图片
"bmp", "gif", "jpg", "jpeg", "png",
// word excel powerpoint
"doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
// 压缩文件
"rar", "zip", "gz", "bz2",
// 视频格式
"mp4", "avi", "rmvb",
"pdf" };
第三传入的文件名一定要带 . 例如:1.txt,如果是1txt则会下载失败,原因是这段代码
那么进入下一个代码
跟进代码RuoYiConfig.getProfile();
注意下面这个函数,这是漏洞造成的重要原因
查看这个变量哪里来的
看到是声明的一个静态变量,继续看这个变量哪里来的
噢,原来是使用的Sprint Boot得到的,内部的一个函数,我们无法更改,有兴趣可以详细搜搜看。
根据注释,那么这个变量应该在application.yml中。(值得注意的是,这里是一个伏笔,漏洞成因可不是
获取这里的profile值。)
果然在这
所以profile = D:/ruoyi/uploadPath
那么我继续进入到下一个函数,
解释一下这串代码的意思
那么我们进入到Constants.RESOURCE_PREFIX里面去看
所以代码逻辑是,最终得到String downloadPath= D:/ruoyi/uploadPathC://1.txt
StringUtils.substringAfter函数逻辑在这,像这类方法都是固定的,就是无法更改的。所以只需要记录作用即可。
我们继续看下一行代码,作用已经写明,所以dowloadName最后返回的是1.txt
StringUtils.substringAfterLast函数在这里
那么到此为止,这个流程就走完了,最后三段是下载代码,不会对结果产生什么影响。所以可以不用管他。
漏洞成因
那么整个流程我们重新梳理一下重要节点
1.if (!FileUtils.checkAllowDownload(resource)),这里判断文件类型,是否为合法类型,且对..进行了检测,防止目录遍历进行下载
2.String localPath = RuoYiConfig.getProfile();获取了profile值,此值是静态值,本来是固定好的,但由于public void setProfile(String profile)
方法的存在,导致漏洞形成(具体原因在后面篇幅)
其实整个漏洞的成因就以上两个原因问题
1.文件合法类型过于多了,造成我们可以遍历名称,下载各种方法。
2.由于public void setProfile(String profile)函数的存在,导致我们可以任意控制目录,最终造成可以下载目标服务器的任意文件
漏洞复现--代码层面
那我们以代码的视角走一遍吧
走代码之前,将明白public void setProfile(String profile)为什么是造成漏洞的原因
你看这个调用,是不是就是调用了setProfile函数,而这个函数的作用也很简单,就是给profile赋值,也就是现在profile的值为:
C://Users//luche//Desktop//1.txt了。
说明以后我们就以代码的视角复现一下这个漏洞吧
使用payload:http://127.0.0.1:88/common/download/resource?resource=C://1.txt
首先传入resource为C://1.txt
然后成功没有..且在白名单内,所以成功进入下一个方法
下一个方法通过刚刚我们说的public void setProfile(String profile)给予了profile值为C://Users//luche//Desktop//1.txt所以localPath
的值就是profile值
带着这个值进入下一个方法
可以看到经过提取,最后dowloadPath的值为C://Users//luche//Desktop//1.txt
带着这个值我们继续往下走
最后成功下载对应的值文件。
至此这段代码审计便是完结了。第一次写文章,有写的不明白的地方请多多指教。
自我总结
当然有两坑没有解释:
1.为什么定时任务的设定可以影响到profile赋值,这个我还没弄明白,下一篇可以补充
2.为什么String downloadPath = localPath + StringUtils.substringAfter(resource, Constants.RESOURCE_PREFIX);
localPath的值是C://Users//luche//Desktop//1.txt
StringUtils.substringAfter(resource, Constants.RESOURCE_PREFIX);的值是1.txt
那么他们相加不应该是C://Users//luche//Desktop//1.txt1.txt吗?为什么最后downloadPath的值是C://Users//luche//Desktop//1.txt
只能等我解决再讲了。
郑重声明,因为是第一次进行java的代码审计,全程都是靠chatgpt一边翻译理解一边审计的,所以可能很多东西表达是不规范的,错误的。希望各位佬多给点意见进行改正吧,希望大家都能早日成为白盒的黑阔高手。
4A评测 - 免责申明
本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。
不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。
本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。
如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!
程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。
侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)