这次打的是 Management,一台 Linux Easy 靶机。我从首页的 SSO 入口开始,先折腾了前端资源解密、JATO 参数和匿名账户权限,后来通过 OpenAM 拿到 Shell,再沿着 GLPI 凭据和备份权限拿到了两个 Flag。中间也走了些弯路,一起记在这里。

目标靶机信息

项目内容
平台Hack The Box
靶机Management
难度Easy
目标 IP10.129.159.168(初始扫描)
实战日期2026-11-01 至 2026-11-02(北京时间)
完成情况已取得 user flag 和 root flag,未建立交互式 root shell

Management 靶机信息:Easy / Linux

图中的实例 IP 为 10.129.160.183,本文首次扫描使用的是 10.129.159.168。

路径概览

打完后回头看,主要过程可以串成下面这条线:

已验证的关键节点

01 · 从 Web 入口定位 OpenAM

扫描发现 Web 与 LDAP 服务,Client Login 跳转到 SSO;先后检查前端资源、JATO 参数和匿名用户权限。

02 · 获得 openam Shell

修正 PoC 的目标入口后取得命令执行回显,再建立服务账户 Shell。

03 · 从 GLPI 凭据切换到 owen

读取 GLPI 配置和 LDAP 服务账户密文,解密后验证密码复用,取得 user flag。

04 · 通过备份读取 root flag

利用 owen 的 sudo 备份规则绕过路径限制,将 /root 复制到可读取的位置,取得 root flag。

信息收集

端口扫描

先用 Nmap 看一下开放了哪些服务:

1
sudo nmap -sS -sV -sC -T4 -n --reason -O -oA nmap_10.129.159.168 10.129.159.168
端口服务当时获得的线索
22SSHOpenSSH 9.6p1,Ubuntu
80 / 443HTTP / HTTPSnginx 1.24.0;证书包含 management.htb 与通配域名
4444SSL / LDAP证书指向 sso.management.htb
50389LDAPNmap 提示 Anonymous bind OK
展开 Nmap 原始输出摘录

关键输出:

1
2
3
4
5
6
7
8
9
10
11
12
 22/tcp    open  ssh      syn-ack ttl 63 OpenSSH 9.6p1 Ubuntu 3ubuntu13.19 (Ubuntu Linux; protocol 2.0)

80/tcp open http syn-ack ttl 63 nginx 1.24.0 (Ubuntu)

443/tcp open ssl/http syn-ack ttl 63 nginx 1.24.0 (Ubuntu)
Subject Alternative Name: DNS:management.htb, DNS:*.management.htb

4444/tcp open ssl/ldap syn-ack ttl 63
ssl-cert: Subject: commonName=sso.management.htb/organizationName=Administration Connector RSA Self-Signed Certificate

50389/tcp open ldap syn-ack ttl 63 (Anonymous bind OK)

Nmap 扫描结果:Web、SSH 与 LDAP 服务

发现 SSH、HTTP、HTTPS 和 LDAP 服务。
其中 LDAP 扫描结果提示允许匿名绑定,但当时我还不了解 LDAP 协议和具体利用方法。

Web 页面与 SSO 入口

LDAP 暂时没什么思路,我先去看看 Web 页面。
在 management.htb 首页看到统一身份认证、资产管理、监控和备份等介绍,先保留这些信息。

首页文字与状态信息(原始摘录)
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
  The platform

One pane of glass for the whole estate.
Management isn't a folder of disconnected tools. Identity, assets, monitoring and backup feed a single operations platform, so every engineer sees the same truth and nothing falls through the cracks.

Unified identity broker
Every portal, from the service desk to client dashboards, authenticates through one hardened SSO gateway.

One operations platform
Assets, tickets, monitoring and backup share a single source of truth — no spreadsheets, no blind spots.

Recovery you can prove
Restores are rehearsed on a schedule, not improvised during an incident.

15-minute response
Priority incidents are acknowledged by a named engineer within fifteen minutes, day or night.

identity.broker
online
service-desk
online
monitoring
online
backup.repo
verified
$ estate status --summary
hosts managed 4218 / 4218 healthy
open incidents 2 low 0 high
last backup 02:14 · integrity ok

Management 首页与 Client Login 入口

点击 Client Login 后跳转到:

1
https://sso.management.htb/openam/XUI/#login/

OpenAM 登录页面

OpenAM 版本线索与漏洞检索

发现这是 OpenAM 登录页面,接下来使用 Burp Suite 查看前端请求中的版本线索。

1
GET /openam/XUI/templates/common/NavigationTemplate.html?v=16.0.5 HTTP/1.1

请求里带着 v=16.0.5,我先把它当成版本线索,试着搜索对应漏洞。

展开 SearchSploit 检索记录

使用 SearchSploit 搜索对应版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
┌─[✗]─[meng@parrot]─[~/labs/htb/Management]
└──╼ $searchsploit OpenAM 16.0.5
Exploits: No Results
Shellcodes: No Results
Papers: No Results
┌─[meng@parrot]─[~/labs/htb/Management]
└──╼ $searchsploit OpenAM
---------------------------------------------------------------------------- ---------------------------------
Exploit Title | Path
---------------------------------------------------------------------------- ---------------------------------
OpenAM 13.0 - LDAP Injection | java/webapps/50480.go
---------------------------------------------------------------------------- ---------------------------------
Shellcodes: No Results
Papers: No Results

没有找到对应版本的结果,接下来查看页面源代码是否留有信息。

页面源码与加密前端资源

OpenAM 登录页源码中的 v=16.0.5 与 Base64 库

OpenAM 登录页 HTML 源码
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
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>OpenAM</title>
</head>
<!--[if IE 9]>
<body style="display:none" class="ie9">
<![endif]-->
<!--[if (gt IE 9)|!(IE)]><!-->
<body style="display:none">
<!--<![endif]-->
<div id="messages" class="clearfix"></div>
<div id="wrapper">Loading...</div>
<div id="popup">
<div id="popup-content" class="radious"></div>
</div>
<footer id="footer" class="footer text-muted"></footer>
<script src="libs/base64-1.0.0-min.js"></script>
<script type="text/javascript">
var require = {
urlArgs : "v=16.0.5",
deps : ['main']
};
</script>
<script src="libs/requirejs-2.3.7-min.js"></script>
</body>
</html>

看到 base64-1.0.0-min.js 时,我一开始以为前后端通信做了什么加密。后来才理清:Base64 只是编码,单凭加载这个库还看不出它具体处理了什么。

接下来查看 https://management.htb/ 的源代码,发现页面会下载 assets/app.enc,再使用脚本内给出的密钥执行 AES-GCM 解密。

展开前端资源加载脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
(function(){
var KEY="C+XDTUB0EuPY7BE+xzRcMP6dNjWd0h0GPxdt8tWSTVY=",FILE="assets/app.enc";
function b64(s){var b=atob(s),a=new Uint8Array(b.length),i=0;for(;i<b.length;i++)a[i]=b.charCodeAt(i);return a;}
function fail(){var a=document.getElementById("app");if(a)a.innerHTML='<div style="min-height:100dvh;display:grid;place-items:center;color:#94a6bb;font-family:ui-sans-serif,system-ui,sans-serif;text-align:center;padding:24px"><div><h1 style="color:#e7eef6;font-size:1.4rem;margin:0 0 10px">Secure browser required</h1><p>This portal renders over an encrypted channel. Enable JavaScript and load the site over HTTPS to continue.</p></div></div>';}
if(!window.crypto||!crypto.subtle){return fail();}
fetch(FILE,{cache:"no-store"}).then(function(r){if(!r.ok)throw 0;return r.text();}).then(function(t){
var raw=b64(t.trim()),iv=raw.slice(0,12),ct=raw.slice(12);
return crypto.subtle.importKey("raw",b64(KEY),{name:"AES-GCM"},false,["decrypt"]).then(function(k){
return crypto.subtle.decrypt({name:"AES-GCM",iv:iv},k,ct);
});
}).then(function(buf){
(0,eval)(new TextDecoder().decode(buf));
}).catch(function(){fail();});
})();

这里提供了密钥、加密文件位置和解密函数。接下来尝试将文件下载到本地。

1
2
3
4
5
┌─[meng@parrot]─[~/labs/htb/Management]
└──╼ $curl -O http://management.htb/assets/app.enc
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 178 100 178 0 0 87 0 00:02 00:02 0

当时看到下载完成,误以为已经取得加密文件;后续检查确认这次仅下载了 178 字节的 HTTP 重定向页面。

接下来卡在解密步骤,不知道如何继续。

解密前端资源

页面脚本说明 assets/app.enc 使用 Base64 包装,按这段加载脚本的格式,解码后前 12 字节为 AES-GCM IV,其余为密文和 authentication tag;页面中给出了 Base64 编码的 AES key。

第一次使用 HTTP 下载时得到 Nginx 301 Moved Permanently 页面。改用 HTTPS 并跟随重定向后,成功取得 Base64 文件,并使用 Node.js crypto 按 AES-256-GCM 解密,拆出前 12 字节 IV 和末尾 16 字节 authentication tag,生成 app.dec.js。

1
2
解密完成:63863 bytes,已写入 app.dec.js
app.dec.js: JavaScript source, ASCII text

文件解出来了,但里面还是一堆 _0x... 形式的混淆代码。我当时没有从这里继续找到可用的接口或域名线索。

浏览器中的混淆 JavaScript 与本地保存结果

我也在浏览器 Console 中查看了解码后的内容,并复制保存为 app.dec.runtime.js。

OpenAM 漏洞探测与权限枚举

转向 JATO 参数验证

前端这条线暂时卡住后,我参考资料把方向转到了 CVE-2026-33439,关注点是 jato.clientSession 的 Java 反序列化处理。接下来先找目标上的对应入口,看看请求和响应有什么变化。

JATO 密码重置入口确认

在访问 /openam/ui/PWResetUserValidation 的正常密码重置流程中捕获到:

  • 方法:POST
  • Content-Type:application/x-www-form-urlencoded
  • 表单字段:PWResetUserValidation.fldUserAttr、PWResetUserValidation.tfUserAttr、PWResetUserValidation.btnNext、jato.defaultCommand
  • 当前请求包含 jato.pageSession,其值以 rO0AB 开头,符合 Java 序列化数据经过 Base64 编码后的特征。

这里能看到 JATO 表单和序列化会话参数,不过正常请求带的是 jato.pageSession,还不是我要检查的 jato.clientSession。两个参数不能混为一谈。

这一阶段按顺序做了五次参数验证。最明显的差异是:普通字符串被清空,而 HashMap 值会被保留。

顺序输入与位置观察结果
1POST 表单中的空值HTTP 200,密码重置错误与基线一致
2POST 表单中的字符串 A页面重新渲染空的 hidden input
3POST 表单中的序列化 Java String相同错误,hidden input 仍为空
4Query 中的序列化 HashMaphidden input 原样保留该值
5本地自定义 Comparator 的 PriorityQueue参数被清空,未复现本地触发输出

这一阶段尚不能确认 RCE。 参数被保留只是对象处理行为的线索;HTTP 200、页面长度变化和本地 Comparator 输出,都不能代替目标上的命令执行证据。

展开五次验证的请求与响应记录

随后在同一个 POST 表单中加入空的 jato.clientSession:

1
...&jato.pageSession=<原值>&jato.clientSession=

服务端返回 HTTP 200,并显示 The password cannot be reset for this user.,响应仍是密码重置页面。这次只是把参数放进 POST 表单发了出去;响应与基线一致,单凭 HTTP 200 看不出服务端是否处理了这个参数。

随后将 jato.clientSession 改为普通字符串 A。服务端仍返回 HTTP 200 和相同的密码重置错误,但响应长度增加,并重新渲染了空的 jato.clientSession hidden input。我能确认的是响应中该字段为空;究竟是输入被忽略、处理失败还是被归一化,仅凭页面还分不清。

随后使用合法 Java String 序列化值 rO0ABXQAD21hbmFnZW1lbnQtdGVzdA 作为 jato.clientSession 的 POST 字段。服务端仍返回 HTTP 200、相同的密码重置错误和空的 hidden input;当前结果未出现解析异常或命令执行。该测试仍携带了登录 Cookie,且参数位于 POST 表单中,不能作为预认证漏洞的最终判断。

随后将正常 jato.pageSession 的 HashMap 序列化值放入 Query 参数 jato.clientSession,并保持 POST 表单中的 jato.pageSession。服务端返回 HTTP 200,响应中的 jato.clientSession hidden input 原样保留该 HashMap 值,而普通字符串测试会被清空。该差异与目标接受符合预期结构的对象这一推测相符,但这次也改变了参数位置,尚不能隔离判断差异的原因;目前仍只是无害对象验证,没有执行命令。

随后将本地 PQTest$DemoComparator 序列化的 PriorityQueue 放入 jato.clientSession。服务端仍返回 HTTP 200,但响应把 jato.clientSession 清空,页面流程未改变。可能是目标缺少 PQTest$DemoComparator,也可能是类型检查或其他处理失败;没有服务端异常信息,还不能确定原因。这次没有在目标上复现本地 Comparator 的触发效果。

请求关键字段对照

我主要对比了下面两种传参方式,这里只列关键部分,会话值用占位符表示。

POST 表单中的普通字符串:

1
2
3
4
POST /openam/ui/PWResetUserValidation
Content-Type: application/x-www-form-urlencoded

...&jato.pageSession=<原值>&jato.clientSession=A

返回 HTTP 200,页面提示 The password cannot be reset for this user.,响应里的 jato.clientSession hidden input 为空。

Query 中的 HashMap:

1
2
3
4
POST /openam/ui/PWResetUserValidation?jato.clientSession=<HashMap 序列化值>
Content-Type: application/x-www-form-urlencoded

...&jato.pageSession=<原值>

这次把正常 jato.pageSession 中的 HashMap 值放进 Query,原来的 POST 表单仍然保留。响应也是 HTTP 200,但 hidden input 留下了传入的值。

这里有个问题:我同时改了参数内容和位置,所以还不能确定是哪一个因素造成了差异。到这一步也没有命令执行回显,不能算利用成功。

匿名会话权限枚举

使用已建立的匿名会话观察 Dashboard 请求,记录到以下结果(会话 Cookie 与 token 未写入):

  • GET /openam/json/serverinfo/* 返回实例配置:realm 为 /,域名为 .management.htb,secureCookie=false、cookieSameSite=null、allowedWithoutReferer=true,自助注册和忘记密码功能关闭。
  • GET /openam/json/realms/root/users/anonymous 返回匿名用户资料。角色仅为 ui-self-service-user,状态为 Active,LDAP DN 为 uid=anonymous,ou=people,dc=management,dc=htb,universal ID 为 id=anonymous,ou=user,dc=management,dc=htb。
  • GET /openam/json/realms/root/dashboard/assigned 返回空分配结果:{"_id":"0","_rev":"0"}。
  • GET /openam/json/realms/root/users/anonymous/oauth2/applications?_queryFilter=true 返回 result: [],当前用户没有 OAuth2 应用。
  • GET /openam/json/users?_action=idFromSession 返回 400 Unrecognized request parameter '_action',该请求尚未证明可用,需结合实际方法、路径和 API 版本继续核对。

匿名账户能进入 Dashboard,也能看到自己的资料和部分实例配置,但没有分配应用。我还没找到能从这里进入管理功能的办法。

anonymous 账户的个人资料页面

API 文档权限观察

在匿名会话的 OpenAM Dashboard 帮助菜单中发现 API Explorer 和 API Docs,访问 API Docs 时显示 403 页面。我的账户只有 ui-self-service-user 角色,接下来就去看看这个 403 是在哪里拦下来的。

API Docs 路由权限

OpenAM XUI main.js 中的路由配置如下(整理展示时的转义):

1
2
3
4
5
6
7
apiDocView: {
view: "org/forgerock/openam/ui/admin/views/api/ApiDocView",
role: "ui-global-admin",
url: /^api\/doc/,
pattern: "api/doc",
navGroup: "admin"
}

这里要求 ui-global-admin,而我的匿名账户只有 ui-self-service-user。结合 Network 来看,这次是 XUI 的路由检查先拦住了页面,加载的是 403.html 模板,并没有发出 API Docs 的后端请求。

进一步在 XUI 已加载模块中观察到权限分层:GlobalRoutes.js 主要使用 ui-global-admin,RealmsRoutes.js 使用 ui-realm-admin,AMRoutesConfig.js 和 UserRoutesConfig.js 包含 ui-self-service-user/ui-user 路由;AppConfiguration.js 也按这些角色控制菜单可见性。既然当前只有 ui-self-service-user,我就继续检查 self-service 路由和它们调用的 API。

各路由文件的具体观察如下,按当时的审阅顺序排列:

路由审阅结果:

  • ui-global-admin:全局认证设置(configure/authentication)、全局服务(configure/global-services)、部署站点(deployment/sites)、部署服务器(deployment/servers),以及服务器编辑/服务器默认配置的 general、security、session、sdk、cts、uma、advanced、directoryConfiguration 等树节点。
  • ui-realm-admin:全局服务下的子 Schema 新建、编辑和嵌套编辑(configure/global-services/.../new、.../edit/...)。
  • 文件中没有未设置 role 的管理路由;所有路由均属于 navGroup: "admin"。

看完这个文件,没有找到匿名账户可以直接进入的管理路由。

路由权限分析:

  • dashboard:要求 ui-self-service-user,匿名账户具备该角色,已实际访问。
  • oauth2Tokens:要求 ui-user,匿名账户当前没有该角色,预计会被拒绝,需以实际响应验证。
  • confirmLogin:要求 ui-user,属于登录确认流程。
  • continuePasswordReset、continueSelfRegister、loggedOut、loginFailure、sessionExpired:未声明 role,但它们是认证生命周期辅助页面,不是管理功能。实例 serverinfo 已显示忘记密码和自助注册均关闭,因此不能仅因缺少 role 就推断可利用。

当时我打算继续看 Dashboard 的 self-service API;OAuth2、密码重置和注册这几条线还没有验证出可用结果。

路由权限分析:

  • profile:要求 ui-self-service-user,匿名账户可以访问个人资料页面。
  • forgotUsername、passwordReset、selfRegistration:未声明 role,属于找回用户名、密码重置和注册流程;结合实例配置中的 forgotUsername=false、forgotPassword=false、selfRegistration=false,它们不是当前可用的管理入口。

个人资料页可以打开,我当时主要查看它调用的 API,没有提交资料或密码修改。

API Docs 显示 Forbidden,Network 中出现 403.html 模板请求

漏洞利用与获取 Shell(2026-11-02)

前面的参数测试和权限枚举暂时没有继续推进,接下来我用 PoC 验证命令执行。

OpenAM 命令执行与 openam Shell

一开始,我把 PoC 的 --url 指向 https://sso.management.htb/openam/XUI/#login/,只得到 OpenAM 的前端 HTML。该 URL 中 #login/ 是浏览器端 fragment,不会发送给服务器,且 XUI 登录页不是候选漏洞的后端入口。

将目标改为:

1
https://sso.management.htb/openam/ui/PWResetUserValidation

执行 id 后回显:

1
uid=996(openam) gid=987(openam) groups=987(openam)

随后建立 reverse shell,whoami 回显 openam。/home/owen 存在,但权限为 drwxr-x--- owen owen,openam 无法进入。

OpenAM PoC 执行 id 后返回 openam 身份

监听端收到回连,获得 openam Shell

凭据获取与用户切换

GLPI 配置与数据库访问

OpenAM Java 进程以 openam 运行,启动参数给出 /opt/openam/config。手工枚举中没有发现直接可用的 SUID、capability 或普通 cron 提权点;但发现 /opt/glpi/config/config_db.php 对 openam 可读。

读取 GLPI 数据库配置,并连接 glpidb 查看数据表

配置里有本地 MariaDB 的 GLPI 账户。我用它连上了 glpidb,随后从 glpi_authldaps 读取到:

1
2
rootdn: cn=svc-glpi,ou=services,dc=management,dc=htb
rootdn_passwd: <可逆加密密文,已省略>

解密 LDAP 服务账户密码

GLPI 配置目录中的 glpicrypt.key 同样可读。我在 /opt/glpi 下调用项目自带的 GLPIKey()->decrypt() 解密该密文,解出了一条 12 字符的明文密码。

查询 LDAP 服务账户密文,并调用 GLPIKey 解密

看到 WP 中提到密码复用,我就拿这条密码试着切换到 owen,结果成功了。

密码复用取得 owen 与 user flag

切换用户时,输入刚才解出的密码:

1
su - owen

切换成功后,id 返回:

1
uid=1000(owen) gid=1000(owen) groups=1000(owen)

进入 /home/owen 后读到了 user.txt。拿到 user flag,接下来继续看 sudo 权限。

切换到 owen,确认身份并读取 user flag

备份权限利用与 root flag

owen 的 sudo 备份权限

以 owen 执行 sudo -l 得到:

1
2
(root) NOPASSWD: /usr/bin/rdiff-backup --server --restrict-path /opt/backup
--restrict-mode read-only *

sudo -l 显示 owen 可执行的 rdiff-backup 规则

该规则原意是只读访问 /opt/backup,但末尾 * 允许附加参数。WP 提到,这台靶机上的重复 --restrict-path 参数可以覆盖前面的路径限制。我接下来按这个方向尝试备份 /root。

绕过备份路径限制并复制 /root

我以 owen 身份运行带 --remote-schema 的 rdiff-backup 客户端命令,输出里出现了:

1
NOTE: Starting mirror from source path /root to destination path /tmp/root_backup

rdiff-backup 绕过路径限制,开始备份 root 目录

随后 ls -la /tmp/root_backup 显示了 root 的家目录副本,包括 .ssh、rdiff-backup-data 及 root.txt。这次确实把 /root 的内容复制出来了。截图中的命令还触发了旧版命令行兼容提示,所以这里记录的是靶机上实际验证的行为,不把它泛化到所有 rdiff-backup 版本。

获取 root flag

最后读取备份里的 Flag:

1
cat /tmp/root_backup/root.txt

从 root_backup 目录中的 root.txt 读取 root flag

成功拿到 root flag,到这里两个 Flag 都齐了。

两个 Flag 都已取得。 最后我是通过备份副本读到 root flag 的,没有拿到交互式 root Shell。

总结反思

这次最值得保留的,是几处判断发生变化的过程:

当时的观察这次记录留下的提醒
curl 显示下载完成下载成功不等于内容正确;第一次得到的是 301 页面
前端加载了 Base64 库库的存在不能证明通信加密,需要继续检查实际逻辑
JATO 参数响应出现差异先记录差异,再区分对象处理与命令执行证据
API Docs 显示 403结合 Network 区分前端路由拦截与后端接口拒绝
成功读取 root.txt准确描述为读取 root 文件,不能写成已获得 root Shell

参考与后续复习

这次前端解密脚本、OpenAM PoC、回连以及 GLPI 查询和解密命令用到了 AI 帮助;GLPI 凭据链、密码复用和备份绕过也参考了公开 WP,再由我实际验证。

虽然两个 Flag 都拿到了,但 OpenAM 入口怎么选、可逆加密和哈希有什么区别,以及 rdiff-backup 的 remote schema 怎么工作,我还需要脱离原来的命令再梳理一遍。

知识点与阅读索引

这些资料用于补原理。复习时先合上文章自己解释,再按卡住的部分查阅,不必一次全部看完。

主题复习时关注的问题资料
URL 与前后端路由浏览器中的 URL、发出的请求和页面路由如何对应?MDN:URI fragment
Base64 与 AES-GCM文件的编码层、IV、密文和认证标签分别是什么?Node.js 如何验证解密结果?Node.js 20:Crypto / setAuthTag
Java 反序列化如何识别序列化数据?格式特征、对象处理与可利用性之间还缺哪些证据?OWASP:Deserialization Cheat Sheet
OpenAM 漏洞范围公告中的产品、受影响版本和修复版本如何与目标证据对应?OpenIdentityPlatform 官方公告:CVE-2026-33439
密码存储与服务凭据登录口令校验与应用访问另一个系统时,对凭据的需求有什么区别?OWASP:Password Storage / Hashing vs Encryption
备份的权限边界客户端、服务端、remote schema 和限制参数分别在哪一层起作用?rdiff-backup 官方手册(重点看 REMOTE OPERATION、RESTRICT OPTIONS,并对照实际版本)
补充笔记:参数测试如何做对照

这次 JATO 测试同时改了对象内容和参数位置,难以分清响应差异的来源。后续做类似验证,可以先固定入口、请求方法、会话和参数位置,每轮只改变一个因素;再单独比较 Query 与 POST。这样得到的差异才更容易解释。

除了状态码,还应比较响应体和错误信息,并保留正常请求作基线。某个字段被回填或被清空,不能直接推出内部调用成功,也不能替代命令执行证据。这是对本次测试方法的复盘,不代表当时已经完成了全部控制变量测试。