CVE-2021-3019 Lanproxy 目录遍历漏洞分析
文章中介绍了一些针对开源漏洞的分析技巧,并对目录遍历漏洞的原理进行了分析。漏洞分析文章为本人原创,转载请注明出处。
项目介绍
lanproxy是一个将局域网个人电脑、服务器代理到公网的内网穿透工具,支持tcp流量转发和任何tcp上层协议(访问内网网站、本地支付接口调试、ssh访问、远程桌面等)。
项目地址
https://github.com/ffay/lanproxy
漏洞概述
laproxy-server中输出网站静态资源数据的outputPages方法接收用户前台发起的请求request并获取用户实际输入的url路径,对路径拼接的过程中未进行合法性校验导致攻击者输入…/进行目录遍历。
影响版本
v0.1
环境搭建
①:git clone 存在漏洞版本的源码。
②:运行 mvn package打包。
打包后生成distribution资源目录,包括client和serve。
③:修改配置文件信息,文件路径为 distribution/proxy-server-0.1/conf/config.properties(若端口不冲突可忽略)。
server.bind=0.0.0.0
#与代理客户端通信端口
server.port=4900
#ssl相关配置
server.ssl.enable=true
server.ssl.bind=0.0.0.0
server.ssl.port=4993
server.ssl.jksPath=test.jks
server.ssl.keyStorePassword=123456
server.ssl.keyManagerPassword=123456
#这个配置可以忽略
server.ssl.needsClientAuth=false
#WEB在线配置管理相关信息
config.server.bind=0.0.0.0
config.server.port=8090
config.admin.username=admin
config.admin.password=admin
④:修改debug调试信息,文件路径为distribution/proxy-server-0.1/bin/startup.sh 。
- 默认8000为远程调试端口
重点注意事项:
- 默认Xdebug参数对应 JDK 1.3.x或更早版本,Xdebug参数应于IDEA中JDK版本保持一致。
- 本人使用了JDK11,因此修改配置文件如下:
⑤:启动程序,访问web页面。
- 启动startup.sh:sh startup.sh debug
- 访问web页面
- 测试IDEA远程debug连接情况
连接成功,环境搭建完毕!
漏洞分析
常见分析思路:信息搜集
-
对于有cve编号的开源漏洞,根据cve编号去cve官网和其它国内外一线漏洞平台搜索漏洞信息。本例中可以拿到漏洞报送者反馈给cve官方的issues。
cve开源漏洞申请流程:对于非CNA组织的项目,个人申请者需要在发现漏洞的Github仓库向作者提交issues,issue中应包含漏洞证明,接下来在cve官方网站上提交申请表单,表单中应包括issues链接供cve官方审核人员快速判断漏洞的真实性。更多细节不再这里展开说明,后期考虑发一个专门分析开源漏洞思路的文章。
-
进入issues,通过图片其实已经拿到了漏洞的POC,为安全研究人员和学习者节约了时间。
漏洞挖掘者甚至发出了该开源项目的全网资产情况,在此致敬漏洞挖掘者。接下来可以看到作者恢复本issues的时间线记录,点击进入。
再次点击fix。
最终跟进到commit:https://github.com/ffay/lanproxy/pull/153/commits/96cc79f58632b1f89a56943eb9d557a50812bf36
常见分析思路:补丁对比
比较修复补丁快速找到危险函数所在的源文件。
通过比对补丁96cc79f58632b1f89a56943eb9d557a50812bf36,发现outputPages方法是输出静态资源数据,即html.jpg等,传入的request参数包含前台用户输入的URL,例如 http://localhost/index.html。对比左右两侧,修复后的代码对uriPath的值进行了合法性效验,说明漏洞的source点为前台传入的url。
知识点:查看漏洞补丁发布日期前后的一段时间的其它commit,其中凡是有关漏洞函数和漏洞文件的commit都可以帮助研究者了解更多信息。
commit1:https://github.com/ffay/lanproxy/commit/5929e1b00d72161ab5c84929de86800b9e4b03b6
commit2:https://github.com/ffay/lanproxy/commit/7787a143f9abf31ada4588e11741c92f0e145240
漏洞分析:代码调试
断点位置:lanproxy-master\proxy-server\src\main\java\org\fengfei\lanproxy\server\config\web\HttpRequestHandler#outputPages
Burpsuite发送请求http://192.168.40.128:8090/…/conf/config.properties
触发断点,request.getUri的值为/…/conf/config.properties并赋给uri,接下来赋给uriPath。
向下逐行单步调试,第64行判断uriPath的值如果为 / 就返回 index.html,如果不为 / 则返回 uriPath 的实际值。
第65行PAGE_FOLDER与uriPath的值进行拼接并赋予path。
点进PAGE_FOLDER。
发现PAGE_FOLDER的值为获取当前程序根目录(本例中为:/opt/project/lanproxy/distribution/proxy-server-0.1)下的webpages目录下的内容。如下图所示程序目录结构,当我们输入http://192.168.40.128:8090/404.html时,就会加载webpages目录下的404.html文件。
继续逐行单步调试,如下图所示,拼接后path的值为/opt/project/lanproxy/distribution/proxy-server-0.1/webpages/…/conf/config.properties,第66行创建了一个File对象rfile,第67行判断rfile是否为目录,第72行判断rfile是否存在。
继续向下逐行调试,第85行调用java.io.RandomAccessFile#RandomAccessFile() 去读取rfile对象所指向的文件内容(conf/config.properties)。
- 关于RandomAccessFile:RandomAccessFile类有两个构造函数,其实这两个构造函数基本相同,只不过是指定文件的形式不同。一个需要使用String参数来指定文件名,一个使用File参数来指定文件本身。除此之外,创建RandomAccessFile对象时还需要指定一个mode参数,该参数指定RandomAccessFile的访问模式,一共有4种模式,其中-r表示以只读方式打开。
到这里,源码调试就完成了,接下来看一下漏洞复现。
漏洞复现
- 读取 conf/config.properties
- 读取 etc/passwd
知识点:…/详解,为什么是六个…/ ?
本例中,项目部署在虚拟机的 /opt/project 目录下,webpages目录的绝对路径为opt/project/lanproxy/distribution/proxy-server-0.1/webpages(详见下图)
如果想要读取 conf/config.properties文件,需要向上跳转一层到项目的根目录下,再拼接conf/config.properties。
因此,读取 etc/passwd 就要先跳转到虚拟机的根目录下,opt/project/lanproxy/distribution/proxy-server-0.1/webpages 对应了6层目录结构,所以需要向上跳转六层,所以对应了六个…/ ,再拼接etc/passwd。
修复方案
使用最新版本master分支。