zabbix身份验证流程解析&绕过身份验证的方法

由于实验室产品的监控模块的需求,需要绕过zabbix的验证模块,实现从二级平台到zabbix的无缝接入。
测试发现,zabbix的身份验证并不是想象的那么简单,为了实现功能,遂进行源码分析。


zabbix常规登陆验证流程:

分析./include/classes/user/CWebUser.php中的login和logout可以了解到zabbix的常规验证流程。
主要逻辑如下:

login

  1. 查询出对应用户名密码的user对象
  2. 检查该用户登陆尝试次数是否超出限制
  3. 根据查询的userid验证用户权限,将页面是否允许访问存入guiAccess
  4. 登陆方式选择(ldap、数据库)
  5. 开始新的session: 生成sessionId,将(sessionId、userid、当前时间、激活状态)插入数据库
  6. 根据第3步的guiAccess判断是否结束并抛出异常
  7. 成功登陆后,清除访客的session
  8. 将新的sessionId存入cookie
  9. 对该次用户登录做审计

logout

  1. 从cookie里得到sessionId
  2. 从数据库里删除该登出用户对应的所有状态为PASSIVE的session记录
  3. 将cookie中得到的这个sessionId对应的session记录的状态修改为PASSIVE
  4. 从cookie里删除该sessionId

然而,从一个静态URL想要绕过登陆直接跳转到zabbix的某个监控页面,肯定是不能走这个常规流程,所以,我们以zabbix的监控图表界面对应的charts.php代码来理清zabbix对于URL跳转这类请求的验证流程。

URL跳转验证流程

包括chart.php的几乎每一个zabbix的展示页面的php代码都会在开头包含一个config.inc.php,且并无其他验证相关代码存在,config.inc.php中只有一行主要代码Z::getInstance()->run(),追踪到这个run函数里,就能找到疑似的验证流程入口$this->authenticateUser()
该函数位于 ./include/classes/core/ZBase.php 中。

checkAuthentication

ZBase.php中的函数,是对./include/classes/user/CWebUser.php中同名函数的封装,CWebUser.php中的checkAuthentication又是对zabbix API即./include/classes/api/services/CUser.php中的同名函数的封装。

具体流程如下:

  1. 从cookie中取出zbx_sessionid的值存入sessionId
  2. 如果该sessionId不为空,根据这个sessionId查询出对应的用户userid、autologout字段和处于ACTIVE状态session的lastaccess字段,如果sessionId为空,以访客模式调用login函数并得到新的sessionId,该sessionId对应的GUEST特殊用户将不能通过后面的权限认证
  3. 如果步骤2没有查出记录,那么会报错Session terminated, re-login, please
  4. 调用check_perm2system检查该用户的权限,若有问题,报错No permissions for system access
  5. 如果查出的记录的autologout > 0, 删除数据库中对应用户的过期的session
  6. 更新数据库中当前sessionId和userId对应的session记录的最近访问是时间(lassaccess)字段
  7. 检查该用户的页面访问权限(检查gui_access,与login流程的验证guiAccess一样)
  8. 将userid,sessionid,gui_access存入data变量中
  9. 如果gui_access为GROUP_GUI_ACCESS_DISABLED,即禁用,抛异常,退出。
  10. 将sessionId保存到cookie中
  11. 用sessionId为API设置验证令牌

分析得出结论,如果要实现在任何环境下的一次URL跳转都能成功以管理员的权限进入zabbix,关键在于以上流程的第2步,不能以访客模式进入zabbix,即需要让sessionId不为空且可用,自然得出的办法就是,提前保存好一个可用的sessionId到cookie中。

多番测试后,证实了以上方案是可行的,于是,实施方案如下:

手动在zabbix增加一个session记录,每次直接请求URL的时候,先调用一个自定义php,存入这个session的id,再继续访问,bingo!


后续发现问题,会回来更新。

原文地址:https://www.cnblogs.com/JaSonS-toy/p/4939805.html