首先声明这不是0day,具体齐博版本就不说了,后面应该有人也发现这个漏洞而且发出来了。php
本文为笃行平常工做记录,文章是大概14年国庆节写的,一直未公开。那时候对当时的齐博系列进行完整的分析和代码审计,漏洞很多,挑几个有意思的发出来~。html
HMAC 是密钥相关的哈希运算消息认证码( Hash-based Message Authentication Code),HMAC 运算利用哈希算法,以一个密钥和一个消息为输入,生成一个消息摘要做为输出。web
详细介绍本身google吧。算法
https://en.wikipedia.org/wiki...shell
见文件inc/function.inc.php
中的mymd5
函数安全
/** *加密与解密函数 **/ function mymd5($string, $action = "EN", $rand = ''){ //字符串加密和解密 global $webdb; $secret_string = $webdb[mymd5].$rand.'5*j,.^&;?.%#@!'; //绝密字符串,能够 任意设定 if (!is_string($string)){ $string = strval($string); } if ($string == = "") return ""; if ($action == "EN") $md5code = substr(md5($string), 8, 10); else{ $md5code = substr($string, -10); $string = substr($string, 0, strlen($string) - 10); } //$key = md5($md5code.$_SERVER["HTTP_USER_AGENT"].$secret_string); $key = md5($md5code.$secret_string); $string = ($action == "EN" ? $string : base64_decode($string)); $len = strlen($key); $code = ""; for ($i = 0; $i < strlen($string); $i++){ $k = $i%$len; $code . = $string[$i] ^ $key[$k]; } $code = ($action == "DE" ? (substr(md5($code), 8, 10) == $md5code ? $code : NULL) : base64_encode($code)."$md5code"); return $code; }
开发人员把加密和解密的算法都合起来在了一块儿。这里的交换密钥以下(附带 rand 随机变量),且系统安装时候也会随机产生参数$webdb[mymd5]
见 data/config.php
:cookie
$secret_string = $webdb[mymd5].$rand.'5*j,.^&;?.%#@!'
结算构成的密文信息结构以下以下:网络
|任意长度密文|固定10字节hashcode|
整体加密过程归纳以下:函数
Data = 原文 Key = $webdb[mymd5].$rand.'5*j,.^&;?.%#@!' HashKey = md5( Data + Key ) HashKey1 = substr( md5(Data),8,10 ) 固定 10 位的 hashcode,签名用。 EncryptData = HashKey^Data 任意长度 最终密文则为 EncryptData+hashcode 因为网络传输字符关系,齐博这里对 EncryptData 进行了一次 base64encode 操做。 实际获得的是 base64encode(EncryptData)+hashcode
整体解密过程以下:ui
Data = 密文 Key = $webdb[mymd5].$rand.'5*j,.^&;?.%#@!' DataReal = base64decode( substr( Data, 0, len(Data) – 8 )) 取出真正的密文,而且还原成二进制 HashKey = md5(DataReal + Key) HashKey1 = substr(Data,-8 ) 固定 10 位的 HashKey1,取最后 10 位。 OriginalData = HashKey^ DataReal 还原密文,并非逆向解密。 最终一步 HashKey1 校验 若是 md5(OriginalData) 等于 HashKey1 那么则认为这个消息是有效的那么最终明文为 OriginalData
整个交换的过程当中对于用户来讲密钥 Key 是不可见的,根据明文和密文是不可能直接还原出整个加密的算法,用户对计算出 10 位 HashKey1 的签名也是未知的,整体安全性比较高,要暴力破解枚举则要 16^10 次方的数量级。
通过分析,次算法可能存在一个极大的安全隐患就是计算EncryptData 引入的是 Data 的长度,在长度较短的时候能够进行暴力枚举。
$len = strlen($key); $code = ""; for ($i = 0; $i < strlen($string); $i++){ $k = $i%$len; $code . = $string[$i] ^ $key[$k];
若是咱们构造原文Data = ‘ly’
那么 HashKey1 则能够固定成 c6555942cb
,那么整个加密串应该是 EncryptDatac6555942cb
的形式。只要让算法知足 substr( md5(HashKey ^ Data),8,10)
等于 c6555942cb
便可。
又有:
Data = EncryptData^HashKey EncryptData =HashKey ^ Data
咱们的明文只有 2 位,那么能构造的 EncryptData 是 00~FF 规模为 16^2 数量至关可观,那么暴力枚举规模就转换成了 16^len(Data) 复杂度。
四.齐博前台认证 COOKIES 注入
齐博系统在COOKIE 中使用了 HMAC,COOKIE 产生和使用其实都是服务端作的工做,其实没有第一章所说的明显的客户端/服务端模式交换密钥,这里的场景是广义的 C/S模式,相似UC_Client/UC_Server,在业务逻辑上的C/S 模式并不是物理的 C/S 模式。进入整理,看文件 admin/global.php
/*用户登陆*/ if ($_POST[loginname] && $_POST[loginpwd]) { //省略省略 login_logs($_POST[loginname], "成功登陆,保密了"); $_COOKIE[Admin] = "$rs[uid]\t".mymd5($rs[password]); setcookie("Admin", $_COOKIE[Admin], 0, "/"); } if ($ForceEnter == 1){ $groupdb = @include(ROOT_PATH."data/group/3.php"); $Apower = ($groupdb[allowadmindb]); }elseif(!$userdb){ include './template/login.htm'; exit; } else{ //同步前台登陆 $md5code = mymd5("$lfjdb[uid]\t$lfjdb[username]\t$lfjdb[password]", 'E N', $onlineip); } setcookie("adminID", $md5code, $timestamp + 1800, '/'); }
在验证登陆的后,屡次使用了 HMAC 算法对 cookie 进行加密典型的有 $_COOKIE[adminID]
和$_COOKIE[admin]
。
再看文件 inc/function.inc.php
//同步后台登陆 if ($_COOKIE["adminID"] && $detail = mymd5($_COOKIE["adminID"], 'DE', $onlin eip)){ unset($_uid, $_username, $_password); list($_uid, $_username, $_password) = explode("\t", $detail); $lfjdb = $db->get_one("SELECT * FROM {$pre}memberdata WHERE uid='$ _uid' AND username='$_username'"); }
由上文可知 $_COOKIE[adminID]
的明文形式是
uid\tusername\tpassword
三个参数由 tab 分割。
那么 list($_uid, $_username, $_password) = explode("\t", $detail);
以后就把结果保存到 $_uid
, $_username
和 $_password
若是先后参数个数不一样,那么 那个参数就会为空。
下面进行查库
$lfjdb = $db->get_one("SELECT * FROM {$pre}memberdata WHERE uid='$_uid' AND username='$_username'");
若是咱们构造 uid = “1’#”
那么查询语句实际就变成了 SELECT * FROM {$pre}memberdata WHERE uid=1
知足条件,前台登陆成功,前台登陆认证就这一出。
因为 COOKIE 采用了密文交换,那么咱们就不用关心 单引号被转移的问题,真是太棒了,固然这个 uid 必定要存在,通常创始人 uid 基本=1结合上文,咱们只要构造出一个解密后的 COOKIE[adminID] = “1’#”
便可。那么根据暴力分析一章,咱们要构造的 EncryptData 长度为 3范围 000~fff总计 16^3=4096 规模,至关可观。
计算出 hashcode 为 cdb8c28d5d
ps:md5(1’#) =a425ef44cdb8c28d5d434b2a1343205e
那么咱们只要枚举 base64_encode( 000~fff) + cdb8c28d5d。
最重要的一点咱们发如今认证登陆的时候传入了$onlineip
变量,可能致使 key 变更,不能暴力,看代码 inc/function.inc.php
if ($_SERVER['HTTP_CLIENT_IP']){ $onlineip = $_SERVER['HTTP_CLIENT_IP']; }elseif($_SERVER['HTTP_X_FORWARDED_FOR']){ } else{ } $onlineip = $_SERVER['HTTP_X_FORWARDED_FOR']; $onlineip = $_SERVER['REMOTE_ADDR'];
那么咱们直接构造固定的HTTP_X_FORWARDED_FOR 参数便可。
而且 http 请求提交登陆认证页面,并根据返回值判断。
其实前台能够直接getshell不过不在咱们讨论范围内。
最后 ending…若有不足请指点,亦可留言或联系 fobcrackgp@163.com.
本文为笃行原创文章首发于大题小做,永久连接:齐博CMS:HMAC+COOkie注入漏洞分析
https://www.ifobnn.com/qibocmshmac.html