最近爆了个通达 OA 任意用户登录漏洞,正好分析分析,顺便师傅一起学习。

漏洞分析

第一处

首先我们找到文件根目录的文件 logincheck_code.php,这个文件是没有权限验证的。

我们会发现在 180 行附近有两行代码:

$LOGIN_UID = $UID;
$LOGIN_USER_ID = $USER_ID;
...
$_SESSION["LOGIN_UID"] = $LOGIN_UID;
$_SESSION["LOGIN_USER_ID"] = $LOGIN_USER_ID;

验证登录时就是判断的这两个 SESSION

往上翻翻 $UID 哪来的:

可以发现是直接从 $_POST 中获取的,也就是任意控制即可。

但是 15 行附近有个判断,如果缓存里没有 CODE_LOGIN.$CODEUID$CODEUID 也是可以任意控制的) 就退出程序了,我们可以全局搜索一下这个缓存在哪里设置了。

很快找到一处: ispirit\login_code.php

<?php

include_once "inc/utility_all.php";
include_once "inc/utility_cache.php";
include_once "inc/phpqrcode.php";
$codeuid = $_GET["codeuid"];
$login_codeuid = TD::get_cache("CODE_LOGIN_PC" . $codeuid);

if (empty($login_codeuid)) {
    // 给 codeuid 设置个随机值
    $login_codeuid = getUniqid();
}

$databack = array("codeuid" => $login_codeuid, "source" => "pc", "codetime" => time());
$dataStr = td_authcode(json_encode($databack), "ENCODE");
$dataStr = "LOGIN_CODE" . $dataStr;
$databacks = array("codeuid" => $login_codeuid, "authcode" => $dataStr);

//将 codeuid 存入缓存
TD::set_cache("CODE_LOGIN_PC" . $login_codeuid, $login_codeuid, 120);

//输出 codeuid
echo json_encode(td_iconv($databacks, MYOA_CHARSET, "utf-8"));
echo "\r\n\r\n\r\n";

?>

这里给重要的三句话写了注释。我们只要直接访问一次这个文件就可以伪造了。

复现测试

首先访问一次 /ispirit/login_code.php

存下这个 codeuid。然后访问 /logincheck_code.php

UID 设置成 1,这个 ID 默认是管理员。然后 CODEUID 设置成: _PC+codeuid

随便访问个需要验证的 url/pda/main.php

第二处任意登录

一样的思路,我们全局搜索会找到在文件 \ispirit\login_code_check.php 处有类似的代码:

我们往上翻:

会发现 $UID 来自 $code_info$code_info 又来自缓存 CODE_INFO_PC+$login_codeuid

这里的 $code_info[type] 需要等于 confirm.

再上面一点有这样的代码:

//$codeuid 可控
$login_codeuid = TD::get_cache("CODE_LOGIN_PC" . $codeuid);

这里和之前一样得。

然后我找找哪里有设置 CODE_INFO_PC 的代码,在文件 general\login_code_scan.php

可以发现这里的 codeuidtype 都是可控的。现在就可以利用了。

漏洞复现

  1. 首先访问 /ispirit/login_code.php 获取 codeuid
  2. 访问 /general/login_code_scan.php 提交 post 参数:

source=pc&type=confirm&codeuid={5D9B864F-07AD-519C-13D1-E573E226302A}&uid=1&

  1. 最后访问 /ispirit/login_code_check.php?codeuid=xxx

这样 $_SESSION 里就有了登录的信息了。

补丁分析

第一处修复 logincheck_code.php

这里从 redis 中获取了数据,判断了 $UID 不等于 0 的话才能下一步,相当于做了个权限验证吧。

如果我们能找到一处设置 OA:authcode:token:XXX 的地方,或者找到一处可以控制键值的缓存,即可绕过。

第二处修复 \general\login_code_scan.php

在设置 CODE_INFO_PC前进行了权限验证,这里根据传入的 session 查询此 session 是否登陆过,如果没登陆过就退出程序

思考及总结

这个漏洞其实挺简单的,但是到现在才发现,看来挖掘这样的洞更需要一些耐心和细心。由于这个程序用了全局覆盖,我们可以直接覆盖 _SESSION 里的数据,但是 _SESSION 是存在 redis 中的。所以如果有一处先开启 session_start 然后引入了 session.php 文件,即可直接覆盖 _SESSION 里的数据。

点击收藏 | 0 关注 | 2
  • 动动手指,沙发就是你的了!
登录 后跟帖