AD RMS Discovery and Administrative Reconnaissance
An adversary is identifying on-premises AD RMS clusters via DNS and rights-policy template enumeration before escalating privileges via local group membership to reach the administrative surface.
Based on research by Huntress 2026-09-20 12 steps · 5 queries T1018 T1078.002 T1083 T1090.003
Brief
Why Hunt for AD RMS Reconnaissance
AD Rights Management Service (AD RMS) often sits in the background of an enterprise, protecting the most sensitive documents via a trust model built on a Server Licensor Certificate (SLC). As detailed in the Huntress article, AD Rights Management Service (Part 1): Architecture, Deprecation, and Reconnaissance, these certificates are frequently valid for decades or even centuries. They cannot be rotated without re-protecting every single document in the environment. This permanence makes AD RMS a high-value target for adversaries looking to maintain long-term access to encrypted data.
How the Hunt Flows
The hunt begins by scoping the environment for Windows Servers. This phase defines the target list of potential AD RMS hosts. While the hunt can run broad, identifying known servers running the IIS role or specific RMS-related processes helps prioritize subsequent queries.
Next, the hunt monitors for infrastructure discovery across two surfaces: DNS and HTTP. The first query searches for rare DNS lookups targeting internal domain suffixes that reveal server locations. Simultaneously, the hunt looks for HTTP traffic targeting public SOAP endpoints like template.asmx. An analyst reviews these signals to identify domain accounts successfully locating and enumerating RMS rights-policy templates.
The hunt then pivots to detect the transition from discovery to exploitation. It monitors process activity for commands adding users to the local AD RMS Service Group. This specific group gates access to the administrative SOAP surface. If an adversary gains this membership, they can interact with privileged endpoints that standard users cannot reach.
Finally, the hunt correlates the group modifications with successful HTTP 200 OK responses on the administrative SOAP paths. An analyst synthesizes these events to determine if a domain user followed a clear path from discovery to privilege escalation and administrative access. This correlation differentiates legitimate administrative work from a coordinated intrusion.
Blind Spots and Limitations
This hunt relies heavily on the visibility of internal web traffic. If the environment does not centralize and log HTTP activity from internal IIS servers, template enumeration and administrative surface interaction remain invisible. Additionally, if an adversary has direct access to the back-end SQL configuration database, they may bypass the SOAP surface entirely to extract metadata or keys. The hunt does not cover direct database queries unless SQL logging is specifically ingested.
How to Run the Hunt
This hunt is provided as a hunt.md playbook. This format allows you to import the logic directly into Huntbase or any other hunt.md-aware runtime. The playbook contains the specific SQLite queries and the triage steps required to move from initial server scoping to a final containment decision.
Steps
-
Identify Potential AD RMS Servers
Query · scopingDefine the target scope of Windows Servers that could host the AD RMS role.
reads hb_devicessqlSELECT hostname AS device_hostname, os_name, os_version, time FROM hb_devices WHERE platform = 'windows' AND (LOWER(os_name) LIKE '%server%' OR os_version LIKE '10.0.2%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')What a hit looks like. A list of Windows Servers likely hosting the role. No servers means the hunt remains broad.
-
DNS-based AD RMS Discovery
Query · baselineDetect rare DNS lookups targeting internal domain suffixes that reveal server locations.
reads hb_dns_activitysqlSELECT query_hostname, device_hostname, COUNT(DISTINCT device_hostname) AS host_count, MIN(time) AS first_seen FROM hb_dns_activity WHERE LOWER(query_hostname) LIKE '%' || LOWER('{{target_domain}}') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY query_hostname HAVING host_count < 3What a hit looks like. Anomalous DNS queries for internal member servers. Silence means no rare discovery was captured.
-
HTTP Template Enumeration
Query · detection candidateDetect requests to public SOAP endpoints used for template retrieval.
reads hb_http_activitysqlSELECT device_hostname, actor_user_name, url_path, status_code, time FROM hb_http_activity WHERE (instr(',' || '{{rms_client_paths}}' || ',', ',' || LOWER(url_path) || ',') > 0) AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')What a hit looks like. Successful (200) or failed (401/403) HTTP requests to certification and template endpoints.
-
Assess Early Discovery Signals
Agent triageDetermine if specific hosts or users are performing coordinated RMS reconnaissance.
-
AD RMS Service Group Abuse
Query · triageDetect unauthorized users being added to the local group gating admin access.
reads hb_process_activitysqlSELECT device_hostname, user_name, process_cmd_line, time FROM hb_process_activity WHERE (LOWER(process_cmd_line) LIKE '%localgroup%' OR LOWER(process_cmd_line) LIKE '%localgroupmember%') AND LOWER(process_cmd_line) LIKE '%' || LOWER('{{rms_group_name}}') || '%' AND LOWER(process_cmd_line) LIKE '%add%' AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')What a hit looks like. Processes adding domain users to the RMS administrative group. Silence proves no such command was run by a monitored agent.
-
Administrative SOAP Surface Interaction
Query · enrichmentIdentify successful authenticated calls to the administrative surface.
reads hb_http_activitysqlSELECT device_hostname, src_endpoint_ip, actor_user_name, url_path, status_code, time FROM hb_http_activity WHERE (instr(',' || '{{rms_admin_paths}}' || ',', ',' || LOWER(url_path) || ',') > 0) AND status_code = 200 AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')What a hit looks like. Authenticated 200 OK responses on administrative paths, indicating successful surface interaction.
-
Synthesize Exploitation Path
Agent triageCorrelate early discovery with privilege escalation and admin access.
-
Route on Intrusion Evidence
DecisionRespond based on the certainty of AD RMS cluster compromise.
-
Isolate Compromised RMS Server
Response actionContain the threat before the SLC private key can be extracted or used.
-
Analyst Review and Tuning
Analyst taskReview evidence and identify authorized administrative activity.
-
Hunt Close-out
Analyst taskRecord findings and status.
Coverage
Scenario coverage
| Stage | Covered | How, or why not |
|---|---|---|
| AD RMS Cluster Discovery T1018 |
Yes | dns-discovery |
| Rights Policy Template Enumeration T1083 |
Yes | template-enumeration |
| Service Group Membership Abuse T1078.002 |
Yes | group-abuse |
| Administrative Surface Interaction T1090.003 |
Yes | admin-surface-access |
Blind spots
- Needs hb_http_activity on internal IIS servers. If internal web server logs are not centralized, template enumeration and administrative surface interaction will be invisible. It would answer Are internal SOAP calls being logged and forwarded?.
- Needs SQL query logging on the back-end configuration database. If an adversary has direct SQL access, they can extract metadata or keys without ever touching the SOAP endpoints. It would answer Did the adversary bypass the SOAP surface and query the database directly?.
Parameters & data
Parameters
| Parameter | Type | Default | What it is |
|---|---|---|---|
lookback_days | number | 14 | Number of days to search for discovery and exploitation signals. |
rms_admin_paths | list[path] | /_wmcs/admin/admin.asmx | SOAP endpoints for the AD RMS administrative surface. |
rms_client_paths | list[path] | /_wmcs/certification/certification.asmx, /_wmcs/licensing/licensing.asmx, /_wmcs/template/template.asmx | SOAP endpoints used for client certification and template distribution. |
rms_group_name | string | AD RMS Service Group | The local group on RMS servers gating administrative access. |
scope_hosts | list[host] | — | List of hostnames to scope the search for template and admin access. |
target_domain | string | sopranos.local | The internal domain name for identifying DNS discovery traffic. |
Telemetry
| Source | Category | Telemetry |
|---|---|---|
| Endpoint telemetry (hb_ surfaces) | endpoint | endpoint |
| Web server / proxy logs | siem | network |
Source
---
analysis: A single rule fires on the addition of a user to a local group, but it misses
the context of preceding RMS discovery and subsequent SOAP administrative traffic.
This hunt connects the long-term risk of unrotatable SLC keys with the specific
multi-stage path an adversary takes to reach them across DNS, HTTP, and process
surfaces.
blind_spots:
- id: missing-http-telemetry
question: Are internal SOAP calls being logged and forwarded?
requires: hb_http_activity on internal IIS servers
risk: If internal web server logs are not centralized, template enumeration and
administrative surface interaction will be invisible.
stage: reconnaissance-rms-templates
- id: direct-sql-recon
question: Did the adversary bypass the SOAP surface and query the database directly?
requires: SQL query logging on the back-end configuration database
risk: If an adversary has direct SQL access, they can extract metadata or keys without
ever touching the SOAP endpoints.
stage: administrative-recon-soap
coverage:
- stage: discovery-rms-service-location
status: covered
steps:
- dns-discovery
- stage: reconnaissance-rms-templates
status: covered
steps:
- template-enumeration
- stage: privilege-escalation-service-group
status: covered
steps:
- group-abuse
- stage: administrative-recon-soap
status: covered
steps:
- admin-surface-access
guardrails:
claims: no_unsupported
evidence: citation_required
missing_data: not_benign
telemetry: untrusted
hunt:
applicability: campaign-specific
handoff: keep-as-periodic-hunt
justification: AD RMS protects the most sensitive files in a Windows environment;
unauthorized access to its administrative surface is a precursor to master key
extraction and permanent decryption of enterprise content.
methodology: model-assisted
trigger: intel-report
hypothesis: An adversary is identifying on-premises AD RMS clusters via DNS and rights-policy
template enumeration before escalating privileges via local group membership to
reach the administrative surface.
labels:
- hunt
- attack.t1018
- attack.t1083
- attack.t1078.002
- attack.t1090.003
name: AD RMS Discovery and Administrative Reconnaissance
parameters:
lookback_days:
default: '14'
description: Number of days to search for discovery and exploitation signals.
type: number
rms_admin_paths:
default:
- /_wmcs/admin/admin.asmx
description: SOAP endpoints for the AD RMS administrative surface.
from:
kind: article
observed: '2026-09-08'
ref: https://www.huntress.com/blog/ad-rms-architecture-and-recon
type: list[path]
rms_client_paths:
default:
- /_wmcs/certification/certification.asmx
- /_wmcs/licensing/licensing.asmx
- /_wmcs/template/template.asmx
description: SOAP endpoints used for client certification and template distribution.
from:
kind: article
observed: '2026-09-08'
ref: https://www.huntress.com/blog/ad-rms-architecture-and-recon
type: list[path]
rms_group_name:
default: AD RMS Service Group
description: The local group on RMS servers gating administrative access.
from:
kind: article
observed: '2026-09-08'
ref: https://www.huntress.com/blog/ad-rms-architecture-and-recon
type: string
scope_hosts:
default: []
description: List of hostnames to scope the search for template and admin access.
type: list[host]
target_domain:
default: sopranos.local
description: The internal domain name for identifying DNS discovery traffic.
from:
kind: article
observed: '2026-09-08'
ref: https://www.huntress.com/blog/ad-rms-architecture-and-recon
type: string
provenance:
authors:
- name: Huntbase hunt generation
org: huntbase.io
generated:
by: huntbase-hunt-generation
from: https://www.huntress.com/blog/ad-rms-architecture-and-recon
gates:
- dry-run
- lint
- critic
model: hb_google/gemini-3-flash-preview
rationale: Focus on Windows Servers in the domain, specifically member servers rather
than Domain Controllers. Prioritize hosts running the IIS w3wp.exe process.
references:
- name: 'AD Rights Management Service (Part 1): Architecture, Deprecation, and Reconnaissance'
url: https://www.huntress.com/blog/ad-rms-architecture-and-recon
related:
- hunt: ad-rms-key-extraction-and-decryption
reason: This hunt identifies the reconnaissance phase; the follow-on hunt identifies
the extraction of the SLC private key.
relation: follows
scenario:
stages:
- name: AD RMS Cluster Discovery
observables:
- DNS lookups for newjersey.sopranos.local
- Network connections to VESUVIO (RMS server) or BARONE (SQL server)
- Scanning for HTTP/HTTPS listeners on ports 80 or 443 on domain member servers
slug: discovery-rms-service-location
tactic: discovery
techniques:
- T1018
- name: Rights Policy Template Enumeration
observables:
- HTTP GET requests to /_wmcs/certification/certification.asmx
- HTTP GET requests to /_wmcs/licensing/licensing.asmx
- HTTP GET requests to /_wmcs/template/template.asmx
- Retrieval of XrML rights-policy templates by ordinary domain users like paulie.gualtieri
slug: reconnaissance-rms-templates
tactic: discovery
techniques:
- T1083
- name: Service Group Membership Abuse
observables:
- Addition of domain users (e.g., tony.soprano) to the local 'AD RMS Service Group'
on VESUVIO
- Execution of 'net localgroup' commands to audit or modify RMS group membership
- Logons to VESUVIO by users not typically associated with RMS administration
slug: privilege-escalation-service-group
tactic: privilege-escalation
techniques:
- T1078.002
- name: Administrative Surface Interaction
observables:
- Authenticated SOAP calls to administrative endpoints on VESUVIO
- Traffic proxying from the RMS server (VESUVIO) to the back-end SQL configuration
database (BARONE)
- Requests to the administrative pipeline yielding 200 OK for Service Group members
versus 401 for plain users
slug: administrative-recon-soap
tactic: command-and-control
techniques:
- T1090.003
summary: An attacker performs reconnaissance against an on-premises Active Directory
Rights Management Services (AD RMS) deployment to identify the cluster and its
templates. By leveraging membership in the local AD RMS Service Group, they gain
access to the administrative SOAP surface, positioning themselves to target the
Server Licensor Certificate (SLC) private key stored in the SQL configuration
database.
severity: high
targets:
analyst:
name: Tier-2 analyst
role: analyst
endpoint:
category: endpoint
name: Endpoint telemetry (hb_ surfaces)
telemetry:
- endpoint
hunter:
agent: true
name: Hunt agent
web:
category: siem
name: Web server / proxy logs
telemetry:
- network
tlp: clear
type: investigation
---
# AD RMS Discovery and Administrative Reconnaissance
Because the Server Licensor Certificate (SLC) is valid for centuries and cannot be rotated, identifying this reconnaissance phase early is vital to preventing a permanent compromise of the document trust model. The hunt searches for standard domain users mapping the infrastructure via DNS and template enumeration. It then monitors for the transition to exploitation where an adversary adds accounts to the local AD RMS Service Group to reach the privileged SOAP administrative surface. The analyst confirms the legitimacy of group changes and coordinates with the AD team to secure the RMS cluster.
## identify-potential-servers
<!-- Identify Potential AD RMS Servers -->
Define the target scope of Windows Servers that could host the AD RMS role.
```sqlite target=endpoint role=scoping params=(lookback_days=lookback_days, scope_hosts=scope_hosts)
~~~yaml
expected: A list of Windows Servers likely hosting the role. No servers means the
hunt remains broad.
reads:
- hostname
- os_name
- os_version
- platform
- time
silence: not_evidence_of_absence
source: hb_devices
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT hostname AS device_hostname, os_name, os_version, time FROM hb_devices WHERE platform = 'windows' AND (LOWER(os_name) LIKE '%server%' OR os_version LIKE '10.0.2%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')
```
## parallel-early-discovery
<!-- Search for RMS Infrastructure Discovery -->
parallel:
- → dns-discovery
- → template-enumeration
join: → early-stage-triage
## dns-discovery
<!-- DNS-based AD RMS Discovery -->
Detect rare DNS lookups targeting internal domain suffixes that reveal server locations.
```sqlite target=endpoint role=baseline params=(lookback_days=lookback_days, target_domain=target_domain, scope_hosts=scope_hosts)
~~~yaml
baseline:
compare: first_seen
window: '{{lookback_days}}d'
expected: Anomalous DNS queries for internal member servers. Silence means no rare
discovery was captured.
prevalence:
by: device_hostname
key:
- query_hostname
rare_below: 3
reads:
- query_hostname
- device_hostname
- time
silence: not_evidence_of_absence
source: hb_dns_activity
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT query_hostname, device_hostname, COUNT(DISTINCT device_hostname) AS host_count, MIN(time) AS first_seen FROM hb_dns_activity WHERE LOWER(query_hostname) LIKE '%' || LOWER('{{target_domain}}') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY query_hostname HAVING host_count < 3
```
## template-enumeration
<!-- HTTP Template Enumeration -->
Detect requests to public SOAP endpoints used for template retrieval.
```sqlite target=web role=detection-candidate params=(lookback_days=lookback_days, rms_client_paths=rms_client_paths, scope_hosts=scope_hosts)
~~~yaml
expected: Successful (200) or failed (401/403) HTTP requests to certification and
template endpoints.
reads:
- device_hostname
- actor_user_name
- url_path
- status_code
- time
silence: not_evidence_of_absence
source: hb_http_activity
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT device_hostname, actor_user_name, url_path, status_code, time FROM hb_http_activity WHERE (instr(',' || '{{rms_client_paths}}' || ',', ',' || LOWER(url_path) || ',') > 0) AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')
```
## early-stage-triage
<!-- Assess Early Discovery Signals -->
```agent target=hunter
cite: required
context:
- dns-discovery
- template-enumeration
max_iterations: 3
objective: Identify domain users who successfully located and enumerated RMS rights-policy
templates.
success_criteria: A list of confirmed AD RMS servers and the domain accounts querying
them.
tools:
- endpoint
- web
```
## parallel-exploitation
<!-- Search for Escalation and Admin Access -->
parallel:
- → group-abuse
- → admin-surface-access
join: → follow-on-triage
## group-abuse
<!-- AD RMS Service Group Abuse -->
Detect unauthorized users being added to the local group gating admin access.
```sqlite target=endpoint role=triage params=(lookback_days=lookback_days, rms_group_name=rms_group_name, scope_hosts=scope_hosts)
~~~yaml
expected: Processes adding domain users to the RMS administrative group. Silence proves
no such command was run by a monitored agent.
reads:
- device_hostname
- user_name
- process_cmd_line
- time
silence: not_evidence_of_absence
source: hb_process_activity
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT device_hostname, user_name, process_cmd_line, time FROM hb_process_activity WHERE (LOWER(process_cmd_line) LIKE '%localgroup%' OR LOWER(process_cmd_line) LIKE '%localgroupmember%') AND LOWER(process_cmd_line) LIKE '%' || LOWER('{{rms_group_name}}') || '%' AND LOWER(process_cmd_line) LIKE '%add%' AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')
```
## admin-surface-access
<!-- Administrative SOAP Surface Interaction -->
Identify successful authenticated calls to the administrative surface.
```sqlite target=web role=enrichment params=(lookback_days=lookback_days, rms_admin_paths=rms_admin_paths, scope_hosts=scope_hosts)
~~~yaml
expected: Authenticated 200 OK responses on administrative paths, indicating successful
surface interaction.
reads:
- device_hostname
- src_endpoint_ip
- actor_user_name
- url_path
- status_code
- time
silence: not_evidence_of_absence
source: hb_http_activity
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT device_hostname, src_endpoint_ip, actor_user_name, url_path, status_code, time FROM hb_http_activity WHERE (instr(',' || '{{rms_admin_paths}}' || ',', ',' || LOWER(url_path) || ',') > 0) AND status_code = 200 AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')
```
## follow-on-triage
<!-- Synthesize Exploitation Path -->
```agent target=hunter
cite: required
context:
- early-stage-triage
- group-abuse
- admin-surface-access
max_iterations: 5
objective: Determine if a domain user followed the discovery of the RMS cluster with
a group modification and successful administrative surface access.
success_criteria: A per-host verdict citing discovery traffic, group changes, and
admin SOAP requests.
tools:
- endpoint
- web
```
## route-on-evidence
<!-- Route on Intrusion Evidence -->
if~: "the triage verdict is malicious for at least one host, indicating successful admin surface interaction following a group modification." (confidence: high, judge=hunter)
then: → isolate-server
indeterminate: → analyst-review
unavailable: → analyst-review (blind_spot: missing-http-telemetry)
else: → close-out
## isolate-server
<!-- Isolate Compromised RMS Server -->
```action target=endpoint
~~~yaml
approval: required
~~~
Isolate the AD RMS cluster server and notify the Active Directory team immediately.
```
→ analyst-review
## analyst-review
<!-- Analyst Review and Tuning -->
```manual target=analyst
Review the actor identity and the timing of the group modification. Confirm if the actor is a legitimate administrator. Investigate if any non-standard tools were used for the SOAP calls.
```
→ close-out
## close-out
<!-- Hunt Close-out -->
```manual target=analyst
Update the known AD RMS server list. Document any unauthorized group changes discovered.
```
→ end
Run it
Take this hunt into your environment.
Open it in Huntbase to run every step against your own connections, with Scout weighing the evidence and your analysts in command. Or take the open hunt.md file anywhere that reads the format.
Machine-drafted by huntbase-hunt-generation using hb_google/gemini-3-flash-preview, gated by dry-run, lint, critic, then reviewed by a person.