ARTEX Agentic Pentest Framework Abused Against South Korean Financial Institutions
Executive Summary
An unattributed actor, assessed with moderate confidence as a financially motivated Chinese speaker, used ARTEX, an open-source agentic penetration-testing framework developed in China, together with several large language models to attack South Korean financial institutions from late September to early October 2026. CrowdStrike reported the campaign on 2026-10-07 after finding attacker-controlled open directories. Zscaler ThreatLabz (2026-10-14) describes ARTEX agents probing peripheral internet-facing web applications such as customer-facing inquiry portals and employee-facing mobile application backends, exploiting SQL injection and authentication bypass, and exfiltrating records. Zscaler puts the loss at personally identifiable information for approximately 67,000 individuals, including names, phone numbers, annual income and loan limits.
Most of this activity happens in the web-application and identity tiers, where endpoint telemetry has no view. The honest endpoint angles are narrow: a web server process spawning a shell or interpreter, binaries dropped into web paths, connections to the published attacker host and proxy addresses, and discovery or credential-access behaviour after the application is compromised. This pack builds those hunts, ships nine queries, and states plainly in section 15 where the sensor is blind.
No indicator in this pack meets the two-independent-source bar for prevent, so all eleven ship detect. The ARTEX host address is high confidence but single-primary-source: it originates with CrowdStrike and is restated by The Hacker News and the CSA note, and Zscaler does not publish it. The nine proxy addresses and the likely LLM reseller domain are likewise single-origin infrastructure. Legitimate LLM service endpoints appear as hunt context with a false-positive note and are never in the import file.
Source Review & Web Hunter Notes
| Tier | Source | Key finding | Carry forward | Snapshot |
|---|---|---|---|---|
| T1 | CrowdStrike Counter Adversary Operations, 2026-10-07 | Primary. Two-server architecture, ARTEX host address, nine proxy addresses, LLM backends, likely reseller domain, ATT&CK mapping, unattributed, moderate-confidence Chinese-speaking financially motivated assessment | Yes | 01-crowdstrike.txt |
| T1 | Zscaler ThreatLabz, 2026-10-14 | Four-phase campaign, SQL injection and authentication bypass in peripheral web apps, data fields stolen, approximately 67,000 individuals, behavioural indicators, ARTEX scope guardrails exclude finance, enumeration and egress recommendations | Yes (behaviour, no atomic IOCs) | 02-zscaler.txt |
| T2 | The Hacker News, 2026-10-08 | Write-up that restates the CrowdStrike report (not independent research). Repeats the ARTEX host address and the likely reseller domain. Developer took ARTEX closed source. Unrelated second campaign in the same article excluded | Partial | 03-thn.txt |
| T3 | Cloud Security Alliance AI Safety Initiative, 2026-10-10 | Secondary analysis of the CrowdStrike report. Adds the ARTEX listener port 18899 (also visible in the CrowdStrike URL) and an evidentiary caveat: public reporting gives no initial-access detail | Partial (caveats only) | 04-csa.txt |
| T3 | RuntimeWire digest of Wall Street Journal and Yonhap reporting | Thin. Investigators had not established whether ARTEX was used in every incident; about 600 addresses worldwide host ARTEX instances, which does not mean each was used in an attack | Partial (caveat only) | 05-runtimewire.txt |
Decisions
- Excluded, personal details. The CrowdStrike post contains alleged operator identity details. They are not a hunt signal and no natural person's details appear in this pack.
- Excluded, other campaign. The Hacker News article also covers an unrelated credential-stuffing platform reported by a different vendor. None of its indicators are carried here.
- Excluded, victim names. Affected institutions named in the press are not repeated. The hunts are written around technique.
- Not used. The CrowdStrike post footnotes a Korean newspaper article that was not fetched and is not cited. A further outlet returned an access error and was not saved. No fetched page contained instructions aimed at an AI agent.
- Attribution. No actor, campaign or malware name is assigned. ARTEX is the tool, not an actor.
Hunt Brief & Attack Chain
Hypotheses
| # | Hypothesis | Technique | Telemetry (plain terms, then platforms) | Conf |
|---|---|---|---|---|
| H1 | After exploiting a peripheral web app, the operator or agent gets command execution, visible as a web server worker spawning an interpreter | T1190, T1059 | Process creation with parent and command line (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents) | High |
| H2 | The same parent then runs host and network discovery | T1082, T1087 | Process creation with parent and command line (same sources) | Medium |
| H3 | Credential access follows, again with a web-tier parent | T1003 | Process creation with command line (same sources) | High |
| H4 | Implants or libraries are dropped into web content paths | T1505.003 | Executable file written (CrowdStrike: PeFileWritten, NewExecutableWritten. SentinelOne: File Creation. Defender: DeviceFileEvents) | Medium |
| H5 | Hosts talk to the published ARTEX host and proxy addresses, or the likely reseller domain | T1090, T1583.003 | Outbound IPv4 connection (CrowdStrike: NetworkConnectIP4. SentinelOne: IP Connect. Defender: DeviceNetworkEvents); DNS lookup with requesting process (CrowdStrike: DnsRequest. SentinelOne: DNS Resolved. Defender: no endpoint DNS table) | High for the ARTEX host, medium otherwise |
| H6 | Servers or unusual processes call LLM service endpoints, or run ARTEX itself | T1588.007 | DNS lookup and process creation (same sources) | Low |
| H7 | Reconnaissance, SQL injection, authentication bypass, enumeration and HTTPS exfiltration happen entirely in the web and identity tiers | T1595, T1190, T1110, T1041 | Web server, WAF and database audit logs (outside endpoint telemetry) | Not endpoint-detectable |
Attack chain
| Step | Behaviour (Zscaler, CrowdStrike) | Telemetry | Hunt angle |
|---|---|---|---|
| 1 Recon | ARTEX agents autonomously find and probe peripheral internet-facing apps outside core banking | Web, WAF and proxy logs (outside endpoint telemetry) | Section 7 native hunts |
| 2 Exploit | LLM-driven agents exploit SQL injection and authentication bypass | Web, WAF and application logs (outside endpoint telemetry) | Section 7; Q1 for the post-exploitation tail |
| 3 Post-exploit | Command execution, discovery, possible web shell. Not detailed in public reporting; the file indicator is Zscaler's | Process creation, file write | Q1, Q2, Q3, Q4, Q8 |
| 4 Exfiltrate | Names, phone numbers, income figures and loan limits leave over HTTPS; sequential lookups at scale | Web and DB logs, proxy, DLP (outside endpoint telemetry) | Section 7 |
| 5 Infrastructure | Two-server layout: a Hong Kong-based address (not published) and the ARTEX host; nine proxy addresses; LLM backends via a likely reseller | Network and DNS | Q5, Q6, Q7, Q9 |
| 6 Monetise | Operator asks an LLM where to sell Korean breach data | Not observable by victims | None |
Consolidated IOC Table
Eleven atomic indicators ship, all detect. Prevent requires the exact value in two independent primary sources, and none qualifies: the ARTEX host address is restated by secondary write-ups of one CrowdStrike report, and the rest are CrowdStrike only.
| Type | Value | Confidence | Action | Context and provenance | Expiry |
|---|---|---|---|---|---|
| IPv4 | 38.244.50.120 | High | Detect | ARTEX instance and open directory host. Originating research: CrowdStrike (01). Restated, not independently confirmed, by The Hacker News (03) and CSA (04), which also adds port 18899. Zscaler (02) does not publish it. Expiry is 12 months rather than 6 because an attacker-controlled host with a pinned address tends to stay in use longer than a rotating proxy, but it is still one primary source | 2027-10-16 |
| IPv4 | 101.53.80.20 | Medium | Detect | Proxy address seen in the operator's Claude Code sessions (CrowdStrike only, 01) | 2027-04-16 |
| IPv4 | 205.214.59.31 | Medium | Detect | Proxy address seen in the operator's Claude Code sessions (CrowdStrike only, 01) | 2027-04-16 |
| IPv4 | 124.155.252.63 | Medium | Detect | Proxy address seen in the operator's Claude Code sessions (CrowdStrike only, 01) | 2027-04-16 |
| IPv4 | 154.201.79.246 | Medium | Detect | Proxy address seen in the operator's Claude Code sessions (CrowdStrike only, 01) | 2027-04-16 |
| IPv4 | 23.248.249.90 | Medium | Detect | Proxy address seen in the operator's Claude Code sessions (CrowdStrike only, 01) | 2027-04-16 |
| IPv4 | 23.158.220.98 | Medium | Detect | Proxy address seen in the operator's Claude Code sessions (CrowdStrike only, 01) | 2027-04-16 |
| IPv4 | 103.248.148.84 | Medium | Detect | Proxy address seen in the operator's Claude Code sessions (CrowdStrike only, 01) | 2027-04-16 |
| IPv4 | 203.160.133.172 | Medium | Detect | Proxy address seen in the operator's Claude Code sessions (CrowdStrike only, 01) | 2027-04-16 |
| IPv4 | 209.209.85.38 | Medium | Detect | Proxy address seen in the operator's Claude Code sessions (CrowdStrike only, 01) | 2027-04-16 |
| Domain | xcai.pro | Medium | Detect | Likely LLM API proxy or reseller (01, 03). A reseller is a shared service, so no prevent | 2027-04-16 |
api.anthropic.com, api.deepseek.com, open.bigmodel.cn and api.x.ai are legitimate LLM service endpoints that ordinary business traffic uses. They appear only as hunt context (Q7), with a false-positive note, and are not in the import file. Likewise the DeepSeek, GLM and Grok model names are context, not hashes or addresses. No file hashes, filenames or hostnames were published for this campaign.Affected Surface & Telemetry Matrix
| Surface | Telemetry (plain terms, then platforms) | Priority | Gap |
|---|---|---|---|
| Internet-facing web apps: partner lookup portals, employee mobile backends | Web server, WAF and application logs | Critical | Entirely outside endpoint telemetry; the exploitation, enumeration and exfiltration are invisible to the sensor |
| Web and application server hosts (IIS, Apache, nginx, Tomcat, Node, .NET) | Process creation with parent (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents); executable file written (CrowdStrike: PeFileWritten. SentinelOne: File Creation. Defender: DeviceFileEvents) | High | Plain-text .aspx and .jsp writes are not PE writes and are not reliably seen; in-process SQL execution leaves no child process |
| Server egress | Outbound connection (CrowdStrike: NetworkConnectIP4. SentinelOne: IP Connect. Defender: DeviceNetworkEvents); DNS lookup (CrowdStrike: DnsRequest. SentinelOne: DNS Resolved. Defender: no endpoint DNS table) | High | No byte counts or HTTP content, so HTTPS exfiltration volume and payload are outside endpoint telemetry |
| Identity and authentication on the apps | Application authentication logs, IdP and core identity logs | High | Authentication bypass is an application flaw; outside endpoint telemetry |
| Operator infrastructure | Threat intelligence | Medium | The Hong Kong-based address and the Claude Code session contents are not published |
ATT&CK Mapping
Techniques marked vendor are listed by CrowdStrike. Others are analyst-mapped from behaviour Zscaler describes and are marked mapped.
| Tactic | Technique | Observed behaviour | Query or control |
|---|---|---|---|
| Resource Development | T1583.003 Virtual Private Server (vendor) | Attacker acquires VPS infrastructure | Q5 |
| Resource Development | T1588.007 Obtain Capabilities: Artificial Intelligence (vendor) | ARTEX plus LLM backends | Q6, Q7, Q8 |
| Command and Control | T1090 Proxy (vendor) | Nine proxy addresses | Q5, Q9 |
| Reconnaissance | T1595 Active Scanning (mapped) | Agents probe peripheral apps | Section 7 native hunts |
| Initial Access | T1190 Exploit Public-Facing Application (mapped) | SQL injection, authentication bypass | Section 7; Q1 post-exploit tail |
| Persistence | T1505.003 Web Shell (mapped, Zscaler file indicator) | Unexpected .aspx or .jsp files | Q4 (PE only), section 7 |
| Execution | T1059 Command and Scripting Interpreter (mapped) | Web worker spawns interpreter | Q1 |
| Discovery | T1082, T1087 (mapped) | Host and account discovery | Q2 |
| Credential Access | T1003 OS Credential Dumping (mapped) | Hive export or LSASS dump after compromise. Hypothesis, not described in public reporting | Q3 |
| Collection / Exfiltration | T1213, T1041 (mapped) | Sequential record lookups; structured PII over HTTPS | Section 7 only |
Native Audit-Log Hunts
The core of this campaign is visible in logs the endpoint sensor does not collect. Run these first.
| Source | Hunt | Signal | Why |
|---|---|---|---|
| Web server, WAF and reverse-proxy access logs | Requests per client address and endpoint, last 30 days | One address issuing evenly paced, sequential or incrementing identifiers against login and search endpoints | Zscaler: alert on sequential lookups and unusually high records per session |
| Same | Query-string and body inspection for SQL injection probes | Time-delay functions, UNION SELECT, tautologies and database error strings returned in responses | Zscaler: SQL injection in peripheral apps |
| Application authentication logs | Authenticated responses without a prior valid login | HTTP success on protected lookup endpoints from sessions with no authentication event | Zscaler: authentication bypass |
| Database audit logs | Bulk reads by the application service account | Row counts per hour far above baseline on customer, income and loan tables | Data fields stolen per Zscaler |
| Web content directories (file integrity) | Files with .aspx, .jsp, .ashx extensions created or modified outside deployments | New script files in upload, temp or layout paths | Zscaler file indicator. The endpoint sensor does not reliably see these writes |
| Firewall, proxy and DNS egress logs | Connections from server networks to the ten published addresses and the reseller domain | Any hit; also server-tier lookups of the four LLM endpoints | Q5, Q6, Q7 for endpoints; this covers hosts without a sensor |
| Outbound proxy or DLP | Structured PII (names with phone numbers and financial fields) sent to unapproved external destinations | Large HTTPS POSTs from the application tier | Zscaler behavioural indicator |
Hunt Queries: CrowdStrike CQL, SentinelOne PowerQuery and Defender XDR KQL
Use your tenant's own address. SentinelOne does not document a link that pre-fills a query, so each "Copy & open SentinelOne" button copies the query and opens this console: paste it into PowerQuery.
Pick your portal. Microsoft documents a Share link in advanced hunting but not its URL format, so each "Copy & open Defender" button copies the query and opens Advanced hunting: paste it into the editor.
Looks for: A web or application server worker starting a command interpreter. This is the endpoint-visible moment after SQL injection or an uploaded web shell turns into command execution. FP: application frameworks that shell out for PDF, image or report generation.
Known limitations and deployment notes
- PowerQuery: A shell started by a process the agent did not observe starting is not captured.
- PowerQuery assumes: src.process.name holds the creating process name on Process Creation events.
- PowerQuery assumes: The anchored regex matches the whole process name, case-insensitively.
- KQL – Defender XDR: A shell started by a process the sensor did not observe starting is not captured.
- KQL – Defender XDR assumes: InitiatingProcessFileName holds the creating process name.
// HUNT: Web server process spawning a shell or interpreter // MITRE: T1190, T1505.003, T1059 // CONF: high FP: medium COST: low | REQUIRES: ProcessRollup2 (process creation with parent, command line) // FALSE POSITIVES: build agents, deployment hooks, and some Java or Node apps legitimately shell out; java as parent is the noisiest // TUNING: exclude documented application launchers by CommandLine, and scope java and node parents to hosts that front an internet-facing application #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | ParentBaseFileName=/^(w3wp|httpd|nginx|apache2|php-fpm|tomcat[0-9]*|java|node|dotnet|iisexpress)(\.exe)?$/i | FileName=/^(cmd|powershell|pwsh|sh|bash|dash|zsh|python[0-9.]*|perl|mshta|wscript|cscript|certutil|bitsadmin)(\.exe)?$/i | table([@timestamp, ComputerName, aid, UserName, ParentBaseFileName, FileName, CommandLine], limit=200)
3 required fields · View requirements
Required telemetry
- Process Creation
Required fields
event.type: Event typesrc.process.name: Creating process nametgt.process.name: Created process name
Recommended fields
tgt.process.cmdline: Created command linesrc.process.user: Creating user
Readiness test
event.type = 'Process Creation' | limit 5 | columns event.type, src.process.name, tgt.process.name
// HUNT: Web server process spawning a shell or interpreter // SCHEMA PROFILE: sentinelone-endpoint-ocsf // MITRE: T1190, T1505.003, T1059 // CONF: high | FP: medium // FALSE POSITIVES: build agents, deployment hooks, and some Java or Node apps legitimately shell out; java as parent is the noisiest // EQUIVALENCE: equivalent-with-schema-assumptions - Same process-creation predicate: a web server or application runtime process as the creator and a shell or interpreter as the created process. On a Process Creation event the creator is src.process.* and the new process is tgt.process.*. matches is case-insensitive, which reproduces the CQL /i intent. // REQUIRES: SentinelOne process-creation telemetry with creator and new-process names // TUNING: Exclude documented application launchers by command line, and scope java and node creators to hosts that front an internet-facing application. // LOOKBACK: 7d; extend to 30d for the incident window // CONFIGURATION: No lookup objects required. // KNOWN GAPS: A shell started by a process the agent did not observe starting is not captured. // VALIDATION LEVEL: static-reviewed event.type = 'Process Creation' src.process.name matches '^(w3wp|httpd|nginx|apache2|php-fpm|tomcat[0-9]*|java|node|dotnet|iisexpress)([.]exe)?$' tgt.process.name matches '^(cmd|powershell|pwsh|sh|bash|dash|zsh|python[0-9.]*|perl|mshta|wscript|cscript|certutil|bitsadmin)([.]exe)?$' | columns event.time, endpoint.name, src.process.user, src.process.name, tgt.process.name, tgt.process.cmdline | limit 200
Automated validation: static-passed
3 required fields · View requirements
Required telemetry
- DeviceProcessEvents
Required fields
Timestamp: Event timeInitiatingProcessFileName: Creating process nameFileName: Created process name
Recommended fields
ProcessCommandLine: Created command line
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceProcessEvents | where Timestamp > ago(1d) | project Timestamp, InitiatingProcessFileName, FileName | take 5
// HUNT: Web server process spawning a shell or interpreter // SCHEMA PROFILE: microsoft-defender-xdr // MITRE: T1190, T1505.003, T1059 // CONF: high | FP: medium // FALSE POSITIVES: build agents, deployment hooks, and some Java or Node apps legitimately shell out; java as parent is the noisiest // EQUIVALENCE: equivalent-with-schema-assumptions - Same process-creation predicate. The created process is FileName and its creator is InitiatingProcessFileName. matches regex with a (?i) prefix reproduces the CQL /i intent. // REQUIRES: Defender for Endpoint process-creation telemetry (DeviceProcessEvents) // TUNING: Exclude documented application launchers by command line, and scope java and node creators to hosts that front an internet-facing application. // LOOKBACK: 7d; extend to 30d for the incident window // CONFIGURATION: No lookup objects required. // KNOWN GAPS: A shell started by a process the sensor did not observe starting is not captured. // VALIDATION LEVEL: schema-aligned // AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0 DeviceProcessEvents | where Timestamp >= ago(7d) | where InitiatingProcessFileName matches regex @'(?i)^(w3wp|httpd|nginx|apache2|php-fpm|tomcat[0-9]*|java|node|dotnet|iisexpress)([.]exe)?$' | where FileName matches regex @'(?i)^(cmd|powershell|pwsh|sh|bash|dash|zsh|python[0-9.]*|perl|mshta|wscript|cscript|certutil|bitsadmin)([.]exe)?$' | project Timestamp, ReportId, DeviceId, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine | sort by Timestamp desc | take 200
Looks for: Host and network discovery run by a web-tier parent, the usual first step after an agent or operator gains command execution through the application.
Known limitations and deployment notes
- PowerQuery: Only one sample command line per group is shown, where the CQL tab collects up to five.
- PowerQuery assumes: src.process.name holds the creating process name on Process Creation events.
- PowerQuery assumes: any() returns an arbitrary single command line per group.
- KQL – Defender XDR: A utility started by a process the sensor did not observe starting is not captured.
- KQL – Defender XDR assumes: InitiatingProcessFileName holds the creating process name.
// HUNT: Web server process spawning discovery utilities // MITRE: T1082, T1087, T1016, T1033 // CONF: medium FP: medium COST: low | REQUIRES: ProcessRollup2 (process creation with parent, command line) // FALSE POSITIVES: monitoring agents and health checks run whoami, hostname or ipconfig from service accounts // TUNING: baseline per host first; exclude known health-check CommandLine values and alert on two or more distinct utilities from one parent within ten minutes #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | ParentBaseFileName=/^(w3wp|httpd|nginx|apache2|php-fpm|tomcat[0-9]*|java|node|dotnet|iisexpress)(\.exe)?$/i | FileName=/^(whoami|hostname|ipconfig|ifconfig|systeminfo|nltest|net1?|quser|arp|netstat|id|uname|nslookup|dsquery|tasklist)(\.exe)?$/i | groupBy([aid, ComputerName, ParentBaseFileName, FileName], function=[count(as=runs), collect([CommandLine], limit=5)]) | sort(runs, order=desc, limit=100)
5 required fields · View requirements
Required telemetry
- Process Creation
Required fields
event.type: Event typesrc.process.name: Creating process nametgt.process.name: Created process nameagent.uuid: Endpoint identifierendpoint.name: Host name
Recommended fields
tgt.process.cmdline: Created command line
Readiness test
event.type = 'Process Creation' | limit 5 | columns event.type, agent.uuid, endpoint.name, src.process.name, tgt.process.name
// HUNT: Web server process spawning discovery utilities // SCHEMA PROFILE: sentinelone-endpoint-ocsf // MITRE: T1082, T1087, T1016, T1033 // CONF: medium | FP: medium // FALSE POSITIVES: monitoring agents and health checks run whoami, hostname or ipconfig from service accounts // EQUIVALENCE: approximate-analytic - Same creator and discovery-utility predicates and the same grouping. The CQL collects up to five command lines per group; PowerQuery here keeps one sample command line per group with any(). // REQUIRES: SentinelOne process-creation telemetry with creator and new-process names // TUNING: Baseline per host first, exclude known health-check command lines, and alert on two or more distinct utilities from one creator within ten minutes. // LOOKBACK: 7d; extend to 30d for the incident window // CONFIGURATION: No lookup objects required. // KNOWN GAPS: Only one sample command line per group is shown, where the CQL tab collects up to five. // VALIDATION LEVEL: static-reviewed event.type = 'Process Creation' src.process.name matches '^(w3wp|httpd|nginx|apache2|php-fpm|tomcat[0-9]*|java|node|dotnet|iisexpress)([.]exe)?$' tgt.process.name matches '^(whoami|hostname|ipconfig|ifconfig|systeminfo|nltest|net1?|quser|arp|netstat|id|uname|nslookup|dsquery|tasklist)([.]exe)?$' | group runs=count(), sample_cmdline=any(tgt.process.cmdline) by agent.uuid, endpoint.name, src.process.name, tgt.process.name | sort -runs | limit 100
Automated validation: static-passed
5 required fields · View requirements
Required telemetry
- DeviceProcessEvents
Required fields
Timestamp: Event timeInitiatingProcessFileName: Creating process nameFileName: Created process nameDeviceId: Device identifierDeviceName: Device name
Recommended fields
ProcessCommandLine: Created command line
Readiness test
DeviceProcessEvents | where Timestamp > ago(1d) | project Timestamp, DeviceId, DeviceName, InitiatingProcessFileName, FileName | take 5
// HUNT: Web server process spawning discovery utilities // SCHEMA PROFILE: microsoft-defender-xdr // MITRE: T1082, T1087, T1016, T1033 // CONF: medium | FP: medium // FALSE POSITIVES: monitoring agents and health checks run whoami, hostname or ipconfig from service accounts // EQUIVALENCE: equivalent-with-schema-assumptions - Same creator and discovery-utility predicates and the same grouping. make_set keeps up to five command lines per group, as the CQL does. // REQUIRES: Defender for Endpoint process-creation telemetry (DeviceProcessEvents) // TUNING: Baseline per host first, exclude known health-check command lines, and alert on two or more distinct utilities from one creator within ten minutes. // LOOKBACK: 7d; extend to 30d for the incident window // CONFIGURATION: No lookup objects required. // KNOWN GAPS: A utility started by a process the sensor did not observe starting is not captured. // VALIDATION LEVEL: schema-aligned // AUTOMATED VALIDATION: static-passed; static=passed-with-warnings; fixtures=0/0 DeviceProcessEvents | where Timestamp >= ago(7d) | where InitiatingProcessFileName matches regex @'(?i)^(w3wp|httpd|nginx|apache2|php-fpm|tomcat[0-9]*|java|node|dotnet|iisexpress)([.]exe)?$' | where FileName matches regex @'(?i)^(whoami|hostname|ipconfig|ifconfig|systeminfo|nltest|net1?|quser|arp|netstat|id|uname|nslookup|dsquery|tasklist)([.]exe)?$' | summarize runs = count(), LastSeen = max(Timestamp), CommandLines = make_set(ProcessCommandLine, 5) by DeviceId, DeviceName, InitiatingProcessFileName, FileName | sort by runs desc | take 100
Looks for: Credential dumping or registry hive export where the parent is a web-tier process. Covers the credential-access behaviour that follows web exploitation.
Known limitations and deployment notes
- PowerQuery: Credential access that does not put one of these keywords on a command line is not captured.
- PowerQuery assumes: src.process.name holds the creating process name on Process Creation events.
- PowerQuery assumes: The regex is an unanchored search over the new process command line, as in the CQL.
- KQL – Defender XDR: Credential access that does not put one of these keywords on a command line is not captured.
- KQL – Defender XDR assumes: InitiatingProcessFileName holds the creating process name.
// HUNT: Credential-access tooling launched from a web server process // MITRE: T1003, T1555 // CONF: high FP: low COST: low | REQUIRES: ProcessRollup2 (process creation with parent, command line) // FALSE POSITIVES: authorized red-team and pentest activity against the web tier #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | ParentBaseFileName=/^(w3wp|httpd|nginx|apache2|php-fpm|tomcat[0-9]*|java|node|dotnet|iisexpress)(\.exe)?$/i | CommandLine=/(lsass|comsvcs|minidump|procdump|ntdsutil|sekurlsa|mimikatz|reg(\.exe)?\s+save\s+hklm)/i | table([@timestamp, ComputerName, aid, UserName, ParentBaseFileName, FileName, CommandLine], limit=200)
3 required fields · View requirements
Required telemetry
- Process Creation
Required fields
event.type: Event typesrc.process.name: Creating process nametgt.process.cmdline: Created command line
Recommended fields
tgt.process.name: Created process namesrc.process.user: Creating user
Readiness test
event.type = 'Process Creation' | limit 5 | columns event.type, src.process.name, tgt.process.cmdline
// HUNT: Credential-access tooling launched from a web server process // SCHEMA PROFILE: sentinelone-endpoint-ocsf // MITRE: T1003, T1555 // CONF: high | FP: low // FALSE POSITIVES: authorized red-team and pentest activity against the web tier // EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: a web server or application runtime process as the creator and a credential-access keyword in the new process command line. matches is case-insensitive, which reproduces the CQL /i intent. // REQUIRES: SentinelOne process-creation telemetry with creator name and new-process command line // TUNING: Authorized red-team and pentest activity against the web tier will match; exclude approved test hosts and accounts after review. // LOOKBACK: 7d; extend to 30d for the incident window // CONFIGURATION: No lookup objects required. // KNOWN GAPS: Credential access that does not put one of these keywords on a command line is not captured. // VALIDATION LEVEL: static-reviewed event.type = 'Process Creation' src.process.name matches '^(w3wp|httpd|nginx|apache2|php-fpm|tomcat[0-9]*|java|node|dotnet|iisexpress)([.]exe)?$' tgt.process.cmdline matches '(lsass|comsvcs|minidump|procdump|ntdsutil|sekurlsa|mimikatz|reg([.]exe)?\\s+save\\s+hklm)' | columns event.time, endpoint.name, src.process.user, src.process.name, tgt.process.name, tgt.process.cmdline | limit 200
Automated validation: static-passed
3 required fields · View requirements
Required telemetry
- DeviceProcessEvents
Required fields
Timestamp: Event timeInitiatingProcessFileName: Creating process nameProcessCommandLine: Created command line
Recommended fields
FileName: Created process name
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceProcessEvents | where Timestamp > ago(1d) | project Timestamp, InitiatingProcessFileName, ProcessCommandLine | take 5
// HUNT: Credential-access tooling launched from a web server process // SCHEMA PROFILE: microsoft-defender-xdr // MITRE: T1003, T1555 // CONF: high | FP: low // FALSE POSITIVES: authorized red-team and pentest activity against the web tier // EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: the creator is InitiatingProcessFileName and the credential-access keywords are searched in ProcessCommandLine. A (?i) prefix reproduces the CQL /i intent. // REQUIRES: Defender for Endpoint process-creation telemetry (DeviceProcessEvents) // TUNING: Authorized red-team and pentest activity against the web tier will match; exclude approved test hosts and accounts after review. // LOOKBACK: 7d; extend to 30d for the incident window // CONFIGURATION: No lookup objects required. // KNOWN GAPS: Credential access that does not put one of these keywords on a command line is not captured. // VALIDATION LEVEL: schema-aligned // AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0 DeviceProcessEvents | where Timestamp >= ago(7d) | where InitiatingProcessFileName matches regex @'(?i)^(w3wp|httpd|nginx|apache2|php-fpm|tomcat[0-9]*|java|node|dotnet|iisexpress)([.]exe)?$' | where ProcessCommandLine matches regex @'(?i)(lsass|comsvcs|minidump|procdump|ntdsutil|sekurlsa|mimikatz|reg([.]exe)?\s+save\s+hklm)' | project Timestamp, ReportId, DeviceId, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine | sort by Timestamp desc | take 200
Looks for: A web-tier process dropping a PE into a web content directory. Limit: these events are PE-centric, so a plain-text .aspx or .jsp web shell is not seen here. That case is a stated gap in section 15 and is covered only by the native file-integrity hunt in section 7.
Known limitations and deployment notes
- PowerQuery: Executables written with an unusual extension, and files modified in place rather than created, are not captured.
- PowerQuery assumes: On File Creation events the acting process is src.process.*.
- PowerQuery assumes: The extension list approximates the PE-centric file-write events of the CQL tab.
- PowerQuery assumes: tgt.file.sha256 is populated for the written file.
- KQL – Defender XDR: Executables written with an unusual extension, and files modified in place rather than created, are not captured.
- KQL – Defender XDR assumes: FolderPath holds the full path of the written file.
- KQL – Defender XDR assumes: The extension list approximates the PE-centric file-write events of the CQL tab.
// HUNT: Executable or library written into a web application path by a web server process // MITRE: T1505.003, T1105 // CONF: medium FP: medium COST: low | REQUIRES: PeFileWritten, NewExecutableWritten (PE-centric file-write events) // FALSE POSITIVES: application deployments, hot-fix drops and framework compilation write DLLs under bin or App_Code // TUNING: exclude deployment windows and the deployment service account; alert on first-seen DLL names per host #event_simpleName=/PeFileWritten|NewExecutableWritten/ | ContextBaseFileName=/^(w3wp|httpd|nginx|apache2|php-fpm|tomcat[0-9]*|java|node|dotnet|iisexpress)(\.exe)?$/i | TargetFileName=/(inetpub|wwwroot|webapps|App_Code)/i | table([@timestamp, ComputerName, aid, ContextBaseFileName, TargetFileName, SHA256HashData], limit=200)
3 required fields · View requirements
Required telemetry
- File Creation
Required fields
event.type: Event typesrc.process.name: Writing process nametgt.file.path: Written file path
Recommended fields
tgt.file.sha256: Written file hash
Readiness test
event.type = 'File Creation' | limit 5 | columns event.type, src.process.name, tgt.file.path
// HUNT: Executable or library written into a web application path by a web server process // SCHEMA PROFILE: sentinelone-endpoint-ocsf // MITRE: T1505.003, T1105 // CONF: medium | FP: medium // FALSE POSITIVES: application deployments, hot-fix drops and framework compilation write DLLs under bin or App_Code // EQUIVALENCE: approximate-analytic - SentinelOne has no PE-only file-write event. The hunt uses File Creation by a web server or runtime process under a web application path, restricted to executable and library extensions as a proxy for the CQL's PE-centric events. // REQUIRES: SentinelOne file-creation telemetry with the writing process and target path // TUNING: Exclude deployment windows and the deployment service account; alert on first-seen library names per host. // LOOKBACK: 7d; extend to 30d for the incident window // CONFIGURATION: No lookup objects required. // KNOWN GAPS: Executables written with an unusual extension, and files modified in place rather than created, are not captured. // VALIDATION LEVEL: static-reviewed event.type = 'File Creation' src.process.name matches '^(w3wp|httpd|nginx|apache2|php-fpm|tomcat[0-9]*|java|node|dotnet|iisexpress)([.]exe)?$' tgt.file.path matches '(inetpub|wwwroot|webapps|App_Code)' tgt.file.path matches '[.](exe|dll|sys|ocx|scr)$' | columns event.time, endpoint.name, src.process.name, tgt.file.path, tgt.file.sha256 | limit 200
Automated validation: static-passed
5 required fields · View requirements
Required telemetry
- DeviceFileEvents
Required fields
Timestamp: Event timeActionType: Action typeInitiatingProcessFileName: Writing process nameFolderPath: Written file pathFileName: Written file name
Optional fields
SHA1: Hash. SHA256 is often unpopulated; SHA1 is the safer pivot.
Readiness test
DeviceFileEvents | where Timestamp > ago(1d) | project Timestamp, ActionType, InitiatingProcessFileName, FolderPath, FileName | take 5
// HUNT: Executable or library written into a web application path by a web server process // SCHEMA PROFILE: microsoft-defender-xdr // MITRE: T1505.003, T1105 // CONF: medium | FP: medium // FALSE POSITIVES: application deployments, hot-fix drops and framework compilation write DLLs under bin or App_Code // EQUIVALENCE: approximate-analytic - Defender has no PE-only file-write event. The hunt uses FileCreated by a web server or runtime process under a web application path, restricted to executable and library extensions as a proxy for the CQL's PE-centric events. // REQUIRES: Defender for Endpoint file telemetry (DeviceFileEvents) // TUNING: Exclude deployment windows and the deployment service account; alert on first-seen library names per host. // LOOKBACK: 7d; extend to 30d for the incident window // CONFIGURATION: No lookup objects required. // KNOWN GAPS: Executables written with an unusual extension, and files modified in place rather than created, are not captured. // VALIDATION LEVEL: schema-aligned // AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0 DeviceFileEvents | where Timestamp >= ago(7d) | where ActionType == 'FileCreated' | where InitiatingProcessFileName matches regex @'(?i)^(w3wp|httpd|nginx|apache2|php-fpm|tomcat[0-9]*|java|node|dotnet|iisexpress)([.]exe)?$' | where FolderPath matches regex @'(?i)(inetpub|wwwroot|webapps|App_Code)' | where FileName matches regex @'(?i)[.](exe|dll|sys|ocx|scr)$' | project Timestamp, ReportId, DeviceId, DeviceName, InitiatingProcessFileName, FolderPath, SHA1, SHA256 | sort by Timestamp desc | take 200
Looks for: Any host connecting to the ten CrowdStrike-published addresses. Inbound probing from these addresses is not visible to endpoint telemetry; use the firewall and WAF logs in section 7. Addresses are perishable and easy for the operator to replace.
Known limitations and deployment notes
- PowerQuery: Connections made through an intermediate proxy show the proxy address, not these addresses.
- PowerQuery assumes: IP Connect events carry the remote address in dst.ip.address.
- KQL – Defender XDR: Connections made through an intermediate proxy show the proxy address, not these addresses.
- KQL – Defender XDR assumes: RemoteIP holds the remote address for outbound connections.
// HUNT: Outbound connection to the published ARTEX host and proxy addresses // MITRE: T1090, T1583.003, T1071 // CONF: high FP: low COST: low | REQUIRES: NetworkConnectIP4 (outbound IPv4 connections) // FALSE POSITIVES: shared hosting and proxy ranges can be reassigned to unrelated tenants over time #event_simpleName=NetworkConnectIP4 | in(RemoteAddressIP4, values=["38.244.50.120", "101.53.80.20", "205.214.59.31", "124.155.252.63", "154.201.79.246", "23.248.249.90", "23.158.220.98", "103.248.148.84", "203.160.133.172", "209.209.85.38"]) | groupBy([aid, ComputerName, RemoteAddressIP4, RemotePort], function=[count(as=conns), min(@timestamp, as=first_seen), max(@timestamp, as=last_seen)]) | sort(conns, order=desc, limit=100)
6 required fields · View requirements
Required telemetry
- IP Connect
Required fields
event.type: Event typeevent.time: Event timedst.ip.address: Remote addressdst.port.number: Remote portagent.uuid: Endpoint identifierendpoint.name: Host name
Readiness test
event.type = 'IP Connect' | limit 5 | columns event.type, event.time, agent.uuid, endpoint.name, dst.ip.address, dst.port.number
// HUNT: Outbound connection to the published ARTEX host and proxy addresses
// SCHEMA PROFILE: sentinelone-endpoint-ocsf
// MITRE: T1090, T1583.003, T1071
// CONF: high | FP: low
// FALSE POSITIVES: shared hosting and proxy ranges can be reassigned to unrelated tenants over time
// EQUIVALENCE: equivalent-with-schema-assumptions - Same destination-address list, grouped by endpoint, address and port with a count and first and last seen times.
// REQUIRES: SentinelOne IP Connect telemetry
// TUNING: Shared hosting and proxy ranges can be reassigned over time; confirm the process behind a hit before escalating.
// LOOKBACK: 30d; connections to these addresses are rare enough to search wide
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: Connections made through an intermediate proxy show the proxy address, not these addresses.
// VALIDATION LEVEL: static-reviewed
event.type = 'IP Connect' dst.ip.address in ('38.244.50.120', '101.53.80.20', '205.214.59.31', '124.155.252.63', '154.201.79.246', '23.248.249.90', '23.158.220.98', '103.248.148.84', '203.160.133.172', '209.209.85.38')
| group conns=count(), first_seen=min(event.time), last_seen=max(event.time) by agent.uuid, endpoint.name, dst.ip.address, dst.port.number
| sort -conns
| limit 100Automated validation: static-passed
5 required fields · View requirements
Required telemetry
- DeviceNetworkEvents
Required fields
Timestamp: Event timeRemoteIP: Remote addressRemotePort: Remote portDeviceId: Device identifierDeviceName: Device name
Readiness test
DeviceNetworkEvents | where Timestamp > ago(1d) | project Timestamp, DeviceId, DeviceName, RemoteIP, RemotePort | take 5
// HUNT: Outbound connection to the published ARTEX host and proxy addresses
// SCHEMA PROFILE: microsoft-defender-xdr
// MITRE: T1090, T1583.003, T1071
// CONF: high | FP: low
// FALSE POSITIVES: shared hosting and proxy ranges can be reassigned to unrelated tenants over time
// EQUIVALENCE: equivalent-with-schema-assumptions - Same destination-address list, grouped by device, address and port with a count and first and last seen times.
// REQUIRES: Defender for Endpoint network telemetry (DeviceNetworkEvents)
// TUNING: Shared hosting and proxy ranges can be reassigned over time; confirm the process behind a hit before escalating.
// LOOKBACK: 30d (Advanced Hunting retention)
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: Connections made through an intermediate proxy show the proxy address, not these addresses.
// VALIDATION LEVEL: schema-aligned
// AUTOMATED VALIDATION: static-passed; static=passed-with-warnings; fixtures=0/0
DeviceNetworkEvents
| where Timestamp >= ago(30d)
| where RemoteIP in ('38.244.50.120', '101.53.80.20', '205.214.59.31', '124.155.252.63', '154.201.79.246', '23.248.249.90', '23.158.220.98', '103.248.148.84', '203.160.133.172', '209.209.85.38')
| summarize conns = count(), first_seen = min(Timestamp), last_seen = max(Timestamp) by DeviceId, DeviceName, RemoteIP, RemotePort
| sort by conns desc
| take 100Looks for: Resolution of the domain CrowdStrike names as a likely LLM API proxy or reseller. Detect only: a resolver or reseller is a shared service, not proven attacker-owned infrastructure.
Known limitations and deployment notes
- PowerQuery: Lookups answered from a cache the agent does not see, and encrypted DNS that bypasses the host resolver, are not captured.
- PowerQuery assumes: The queried name is in event.dns.request, which is attested by the independent pySigma mapping. Vendor field docs name dns.request, so confirm which is populated on the tenant.
- PowerQuery assumes: src.process.name is populated on DNS Resolved events.
- KQL – Defender XDR: Lookups answered from a cache the sensor does not see, and encrypted DNS that bypasses the host resolver, are not captured.
- KQL – Defender XDR assumes: The DnsQueryResponse action type is populated on the tenant.
- KQL – Defender XDR assumes: AdditionalFields carries the queried name under DnsQueryString or QueryName.
// HUNT: DNS lookup of the likely LLM API proxy domain // MITRE: T1090, T1588.007 // CONF: medium FP: low COST: low | REQUIRES: DnsRequest (DNS lookup with requesting process) // FALSE POSITIVES: none known; the domain is described by the source as a likely proxy or reseller #event_simpleName=DnsRequest | DomainName=/(^|\.)xcai\.pro$/i | table([@timestamp, ComputerName, aid, ContextBaseFileName, DomainName], limit=200)
2 required fields · View requirements
Required telemetry
- DNS Resolved
Required fields
event.type: Event typeevent.dns.request: Queried name
Recommended fields
src.process.name: Requesting process
Fallbacks
event.dns.request→dns.request: Some tenants populate the unprefixed name instead.
Readiness test
event.type = 'DNS Resolved' | limit 5 | columns event.type, event.dns.request, src.process.name
// HUNT: DNS lookup of the likely LLM API proxy domain
// SCHEMA PROFILE: sentinelone-endpoint-ocsf
// MITRE: T1090, T1588.007
// CONF: medium | FP: low
// FALSE POSITIVES: none known; the domain is described by the source as a likely proxy or reseller
// EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: a DNS lookup of the domain itself or any subdomain of it. On a DNS Resolved event the acting process is src.process.*.
// REQUIRES: SentinelOne DNS Resolved telemetry with the requesting process
// TUNING: None expected; the domain is described by the source as a likely proxy or reseller.
// LOOKBACK: 30d; a lookup of this domain is rare enough to search wide
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: Lookups answered from a cache the agent does not see, and encrypted DNS that bypasses the host resolver, are not captured.
// VALIDATION LEVEL: static-reviewed
event.type = 'DNS Resolved' (event.dns.request in:anycase ('xcai.pro') or event.dns.request matches '[.]xcai[.]pro$')
| columns event.time, endpoint.name, src.process.name, event.dns.request
| limit 200Automated validation: static-passed
3 required fields · View requirements
Required telemetry
- DeviceEvents
Required fields
Timestamp: Event timeActionType: Action typeAdditionalFields: Action details
Recommended fields
InitiatingProcessFileName: Requesting process
Readiness test
DeviceEvents | where Timestamp > ago(1d) | where ActionType == 'DnsQueryResponse' | project Timestamp, ActionType, AdditionalFields | take 5
// HUNT: DNS lookup of the likely LLM API proxy domain // SCHEMA PROFILE: microsoft-defender-xdr // MITRE: T1090, T1588.007 // CONF: medium | FP: low // FALSE POSITIVES: none known; the domain is described by the source as a likely proxy or reseller // EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: a DNS lookup of the domain itself or any subdomain of it. Defender has no endpoint DNS table, so the name is read from AdditionalFields on the DnsQueryResponse action. // REQUIRES: Defender for Endpoint DNS action telemetry (DeviceEvents, DnsQueryResponse) // TUNING: None expected; the domain is described by the source as a likely proxy or reseller. // LOOKBACK: 30d (Advanced Hunting retention) // CONFIGURATION: No lookup objects required. // KNOWN GAPS: Lookups answered from a cache the sensor does not see, and encrypted DNS that bypasses the host resolver, are not captured. // VALIDATION LEVEL: schema-aligned // AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0 DeviceEvents | where Timestamp >= ago(30d) | where ActionType == 'DnsQueryResponse' | extend QueryName = tostring(coalesce(parse_json(AdditionalFields)['DnsQueryString'], parse_json(AdditionalFields)['QueryName'])) | where QueryName =~ 'xcai.pro' or QueryName endswith '.xcai.pro' | project Timestamp, ReportId, DeviceId, DeviceName, InitiatingProcessFileName, QueryName | sort by Timestamp desc | take 200
Looks for: Context only, never a block. Zscaler lists outbound calls to four LLM service endpoints as a behavioural indicator to weigh with other evidence. Those endpoints are legitimate services and must not be treated as malicious infrastructure. The useful signal is a server or an unusual process calling them.
Known limitations and deployment notes
- PowerQuery: These are legitimate services, so hits are context only. Lookups made through a browser or IDE are excluded by design.
- PowerQuery assumes: The queried name is in event.dns.request, which is attested by the independent pySigma mapping. Vendor field docs name dns.request, so confirm which is populated on the tenant.
- PowerQuery assumes: src.process.name is populated on DNS Resolved events.
- KQL – Defender XDR: These are legitimate services, so hits are context only. Lookups made through a browser or IDE are excluded by design.
- KQL – Defender XDR assumes: The DnsQueryResponse action type is populated on the tenant.
- KQL – Defender XDR assumes: AdditionalFields carries the queried name under DnsQueryString or QueryName.
// HUNT: Unexpected resolution of LLM service endpoints (hunt context only) // MITRE: T1588.007, T1071.001 // CONF: low FP: high COST: medium | REQUIRES: DnsRequest (DNS lookup with requesting process) // FALSE POSITIVES: these are legitimate LLM service endpoints used by ordinary business traffic: developer workstations, approved AI gateways, IDE plugins and browsers all call them // TUNING: exclude approved developer and AI-gateway host groups and browser or IDE processes; focus on server-class hosts and on processes that are not a browser, IDE or approved agent #event_simpleName=DnsRequest | DomainName=/^(api\.anthropic\.com|api\.deepseek\.com|open\.bigmodel\.cn|api\.x\.ai)$/i | ContextBaseFileName!=/^(chrome|msedge|firefox|safari|code|cursor|claude)(\.exe)?$/i | groupBy([aid, ComputerName, ContextBaseFileName, DomainName], function=[count(as=lookups)]) | sort(lookups, order=desc, limit=100)
5 required fields · View requirements
Required telemetry
- DNS Resolved
Required fields
event.type: Event typeevent.dns.request: Queried namesrc.process.name: Requesting processagent.uuid: Endpoint identifierendpoint.name: Host name
Fallbacks
event.dns.request→dns.request: Some tenants populate the unprefixed name instead.
Readiness test
event.type = 'DNS Resolved' | limit 5 | columns event.type, agent.uuid, endpoint.name, event.dns.request, src.process.name
// HUNT: Unexpected resolution of LLM service endpoints (hunt context only)
// SCHEMA PROFILE: sentinelone-endpoint-ocsf
// MITRE: T1588.007, T1071.001
// CONF: low | FP: high
// FALSE POSITIVES: these are legitimate LLM service endpoints used by ordinary business traffic: developer workstations, approved AI gateways, IDE plugins and browsers all call them
// EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: a lookup of one of four LLM service endpoints by a process that is not a browser or IDE, grouped by endpoint, process and domain. in:anycase matches the whole name case-insensitively.
// REQUIRES: SentinelOne DNS Resolved telemetry with the requesting process
// TUNING: Exclude approved developer and AI-gateway host groups and browser or IDE processes; focus on server-class hosts and on processes that are not a browser, IDE or approved agent.
// LOOKBACK: 7d; extend to 30d for the incident window
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: These are legitimate services, so hits are context only. Lookups made through a browser or IDE are excluded by design.
// VALIDATION LEVEL: static-reviewed
event.type = 'DNS Resolved' event.dns.request in:anycase ('api.anthropic.com', 'api.deepseek.com', 'open.bigmodel.cn', 'api.x.ai') !(src.process.name matches '^(chrome|msedge|firefox|safari|code|cursor|claude)([.]exe)?$')
| group lookups=count() by agent.uuid, endpoint.name, src.process.name, event.dns.request
| sort -lookups
| limit 100Automated validation: static-passed
6 required fields · View requirements
Required telemetry
- DeviceEvents
Required fields
Timestamp: Event timeActionType: Action typeAdditionalFields: Action detailsInitiatingProcessFileName: Requesting processDeviceId: Device identifierDeviceName: Device name
Readiness test
DeviceEvents | where Timestamp > ago(1d) | where ActionType == 'DnsQueryResponse' | project Timestamp, ActionType, AdditionalFields, InitiatingProcessFileName, DeviceId, DeviceName | take 5
// HUNT: Unexpected resolution of LLM service endpoints (hunt context only)
// SCHEMA PROFILE: microsoft-defender-xdr
// MITRE: T1588.007, T1071.001
// CONF: low | FP: high
// FALSE POSITIVES: these are legitimate LLM service endpoints used by ordinary business traffic: developer workstations, approved AI gateways, IDE plugins and browsers all call them
// EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: a lookup of one of four LLM service endpoints by a process that is not a browser or IDE, grouped by device, process and domain. Defender has no endpoint DNS table, so the name is read from AdditionalFields on the DnsQueryResponse action.
// REQUIRES: Defender for Endpoint DNS action telemetry (DeviceEvents, DnsQueryResponse)
// TUNING: Exclude approved developer and AI-gateway host groups and browser or IDE processes; focus on server-class hosts and on processes that are not a browser, IDE or approved agent.
// LOOKBACK: 7d; extend to 30d for the incident window
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: These are legitimate services, so hits are context only. Lookups made through a browser or IDE are excluded by design.
// VALIDATION LEVEL: schema-aligned
// AUTOMATED VALIDATION: static-passed; static=passed-with-warnings; fixtures=0/0
DeviceEvents
| where Timestamp >= ago(7d)
| where ActionType == 'DnsQueryResponse'
| extend QueryName = tostring(coalesce(parse_json(AdditionalFields)['DnsQueryString'], parse_json(AdditionalFields)['QueryName']))
| where QueryName in~ ('api.anthropic.com', 'api.deepseek.com', 'open.bigmodel.cn', 'api.x.ai')
| where not(InitiatingProcessFileName matches regex @'(?i)^(chrome|msedge|firefox|safari|code|cursor|claude)([.]exe)?$')
| summarize lookups = count(), LastSeen = max(Timestamp) by DeviceId, DeviceName, InitiatingProcessFileName, QueryName
| sort by lookups desc
| take 100Looks for: The tool name appearing in a process image or command line. The sources describe ARTEX running on attacker infrastructure, so a hit on a corporate endpoint is mostly an insider, a contractor, or an unsanctioned pentest harness (Zscaler advises treating such harnesses as hostile code).
Known limitations and deployment notes
- PowerQuery: A tool renamed and run without the string on its command line is not captured.
- PowerQuery assumes: tgt.process.name and tgt.process.cmdline hold the new process on Process Creation events.
- KQL – Defender XDR: A tool renamed and run without the string on its command line is not captured.
- KQL – Defender XDR assumes: FileName and ProcessCommandLine hold the created process.
// HUNT: ARTEX named in a process image or command line // MITRE: T1588.007 // CONF: low FP: medium COST: medium | REQUIRES: ProcessRollup2 (process creation with command line) // FALSE POSITIVES: authorized internal pentest hosts, and unrelated software whose name contains the string // TUNING: scope to hosts that are not on the approved pentest asset list and exclude your own red-team accounts #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | FileName=/artex/i or CommandLine=/artex/i | table([@timestamp, ComputerName, aid, UserName, FileName, CommandLine, ParentBaseFileName], limit=200)
3 required fields · View requirements
Required telemetry
- Process Creation
Required fields
event.type: Event typetgt.process.name: Created process nametgt.process.cmdline: Created command line
Recommended fields
src.process.name: Creating process namesrc.process.user: Creating user
Readiness test
event.type = 'Process Creation' | limit 5 | columns event.type, tgt.process.name, tgt.process.cmdline
// HUNT: ARTEX named in a process image or command line // SCHEMA PROFILE: sentinelone-endpoint-ocsf // MITRE: T1588.007 // CONF: low | FP: medium // FALSE POSITIVES: authorized internal pentest hosts, and unrelated software whose name contains the string // EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: the string is present in the new process name or command line. contains is case-insensitive, which reproduces the CQL /i intent. // REQUIRES: SentinelOne process-creation telemetry with new-process name and command line // TUNING: Scope to hosts that are not on the approved pentest asset list and exclude your own red-team accounts. // LOOKBACK: 7d; extend to 30d for the incident window // CONFIGURATION: No lookup objects required. // KNOWN GAPS: A tool renamed and run without the string on its command line is not captured. // VALIDATION LEVEL: static-reviewed event.type = 'Process Creation' (tgt.process.name contains 'artex' or tgt.process.cmdline contains 'artex') | columns event.time, endpoint.name, src.process.user, tgt.process.name, tgt.process.cmdline, src.process.name | limit 200
Automated validation: static-passed
3 required fields · View requirements
Required telemetry
- DeviceProcessEvents
Required fields
Timestamp: Event timeFileName: Created process nameProcessCommandLine: Created command line
Recommended fields
InitiatingProcessFileName: Creating process name
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceProcessEvents | where Timestamp > ago(1d) | project Timestamp, FileName, ProcessCommandLine | take 5
// HUNT: ARTEX named in a process image or command line // SCHEMA PROFILE: microsoft-defender-xdr // MITRE: T1588.007 // CONF: low | FP: medium // FALSE POSITIVES: authorized internal pentest hosts, and unrelated software whose name contains the string // EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: the string is present in the created process name or command line. contains is case-insensitive, which reproduces the CQL /i intent. // REQUIRES: Defender for Endpoint process-creation telemetry (DeviceProcessEvents) // TUNING: Scope to hosts that are not on the approved pentest asset list and exclude your own red-team accounts. // LOOKBACK: 7d; extend to 30d for the incident window // CONFIGURATION: No lookup objects required. // KNOWN GAPS: A tool renamed and run without the string on its command line is not captured. // VALIDATION LEVEL: schema-aligned // AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0 DeviceProcessEvents | where Timestamp >= ago(7d) | where FileName contains 'artex' or ProcessCommandLine contains 'artex' | project Timestamp, ReportId, DeviceId, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName | sort by Timestamp desc | take 200
Looks for: A web-tier process initiating outbound connections to public space on ports other than 80, 443 and 53, which fits reverse tunnels and proxy callbacks after exploitation. Costly because of the join: run it per host group.
Known limitations and deployment notes
- PowerQuery: Connections to the excluded ports 80, 443 and 53 are not shown, so tunnelling over those ports is out of scope.
- PowerQuery assumes: On IP Connect events the acting process is src.process.*.
- PowerQuery assumes: IPv6 destinations are excluded to match the CQL's IPv4 scope.
- KQL – Defender XDR: Connections to the excluded ports 80, 443 and 53 are not shown, so tunnelling over those ports is out of scope.
- KQL – Defender XDR assumes: RemoteIP holds an IPv4 address for the connections of interest; rows with an IPv6 address drop out of the range test.
// HUNT: Web server process connecting to public addresses on non-web ports // MITRE: T1090, T1071, T1041 // CONF: medium FP: medium COST: high | REQUIRES: NetworkConnectIP4 joined to ProcessRollup2 // FALSE POSITIVES: database replication, SMTP relays, license servers and monitoring agents legitimately connect outbound from application hosts // TUNING: baseline each application host's egress set first and alert only on destinations outside it; exclude known partner API ranges #event_simpleName=NetworkConnectIP4 | !cidr(RemoteAddressIP4, subnet=["10.0.0.0/8","172.16.0.0/12","192.168.0.0/16","127.0.0.0/8","169.254.0.0/16"]) | !in(RemotePort, values=[80,443,53]) | rename(field=ContextProcessId_decimal, as=TargetProcessId_decimal) | join(query={#event_simpleName=ProcessRollup2 | FileName=/^(w3wp|httpd|nginx|apache2|php-fpm|tomcat[0-9]*|java|node|dotnet|iisexpress)(\.exe)?$/i}, field=[aid, TargetProcessId_decimal], include=[FileName, CommandLine], mode=inner) | table([@timestamp, ComputerName, aid, FileName, RemoteAddressIP4, RemotePort], limit=200)
4 required fields · View requirements
Required telemetry
- IP Connect
Required fields
event.type: Event typesrc.process.name: Acting process namedst.ip.address: Remote addressdst.port.number: Remote port
Recommended fields
endpoint.name: Host name
Readiness test
event.type = 'IP Connect' | limit 5 | columns event.type, src.process.name, dst.ip.address, dst.port.number
// HUNT: Web server process connecting to public addresses on non-web ports // SCHEMA PROFILE: sentinelone-endpoint-ocsf // MITRE: T1090, T1071, T1041 // CONF: medium | FP: medium // FALSE POSITIVES: database replication, SMTP relays, license servers and monitoring agents legitimately connect outbound from application hosts // EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: an outbound IPv4 connection from a web server or runtime process to a non-private address on a port other than 80, 443 and 53. The CQL joins the connection to the process record; SentinelOne carries the acting process on the IP Connect event itself, so no join is needed. // REQUIRES: SentinelOne IP Connect telemetry with the acting process // TUNING: Baseline each application host's egress set first and alert only on destinations outside it; exclude known partner API ranges. // LOOKBACK: 7d; extend to 30d for the incident window // CONFIGURATION: No lookup objects required. // KNOWN GAPS: Connections to the excluded ports 80, 443 and 53 are not shown, so tunnelling over those ports is out of scope. // VALIDATION LEVEL: static-reviewed event.type = 'IP Connect' src.process.name matches '^(w3wp|httpd|nginx|apache2|php-fpm|tomcat[0-9]*|java|node|dotnet|iisexpress)([.]exe)?$' !(dst.ip.address contains ':') !(net_ipsubnet(dst.ip.address, '10.0.0.0/8') or net_ipsubnet(dst.ip.address, '172.16.0.0/12') or net_ipsubnet(dst.ip.address, '192.168.0.0/16') or net_ipsubnet(dst.ip.address, '127.0.0.0/8') or net_ipsubnet(dst.ip.address, '169.254.0.0/16')) !(dst.port.number in (80, 443, 53)) | columns event.time, endpoint.name, src.process.name, dst.ip.address, dst.port.number | limit 200
Automated validation: static-passed
4 required fields · View requirements
Required telemetry
- DeviceNetworkEvents
Required fields
Timestamp: Event timeInitiatingProcessFileName: Acting process nameRemoteIP: Remote addressRemotePort: Remote port
Recommended fields
DeviceName: Device name
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceNetworkEvents | where Timestamp > ago(1d) | project Timestamp, InitiatingProcessFileName, RemoteIP, RemotePort | take 5
// HUNT: Web server process connecting to public addresses on non-web ports // SCHEMA PROFILE: microsoft-defender-xdr // MITRE: T1090, T1071, T1041 // CONF: medium | FP: medium // FALSE POSITIVES: database replication, SMTP relays, license servers and monitoring agents legitimately connect outbound from application hosts // EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: an outbound IPv4 connection from a web server or runtime process to a non-private address on a port other than 80, 443 and 53. DeviceNetworkEvents carries the initiating process on the row, so the CQL's join is not needed. // REQUIRES: Defender for Endpoint network telemetry (DeviceNetworkEvents) // TUNING: Baseline each application host's egress set first and alert only on destinations outside it; exclude known partner API ranges. // LOOKBACK: 7d; extend to 30d for the incident window // CONFIGURATION: No lookup objects required. // KNOWN GAPS: Connections to the excluded ports 80, 443 and 53 are not shown, so tunnelling over those ports is out of scope. // VALIDATION LEVEL: schema-aligned // AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0 DeviceNetworkEvents | where Timestamp >= ago(7d) | where InitiatingProcessFileName matches regex @'(?i)^(w3wp|httpd|nginx|apache2|php-fpm|tomcat[0-9]*|java|node|dotnet|iisexpress)([.]exe)?$' | where RemotePort !in (80, 443, 53) | where not(ipv4_is_in_range(RemoteIP, '10.0.0.0/8') or ipv4_is_in_range(RemoteIP, '172.16.0.0/12') or ipv4_is_in_range(RemoteIP, '192.168.0.0/16') or ipv4_is_in_range(RemoteIP, '127.0.0.0/8') or ipv4_is_in_range(RemoteIP, '169.254.0.0/16')) | project Timestamp, ReportId, DeviceId, DeviceName, InitiatingProcessFileName, RemoteIP, RemotePort | sort by Timestamp desc | take 200
Detection Rules by Platform
CrowdStrike: Custom IOA
| IOA name | Pattern | Promotes | Action | Exclusions |
|---|---|---|---|---|
| Web server worker spawns interpreter | Process Creation; parent image basename matches the web-tier set (w3wp, httpd, nginx, apache2, php-fpm, tomcat, node, dotnet); child basename matches cmd, powershell, sh, bash, python, perl, mshta, wscript, cscript | Promote Q1 | Detect, high | Documented launchers by CommandLine; java parent only on internet-facing app hosts |
| Credential tooling from web server worker | Process Creation; same parent set; command line matches lsass, comsvcs, minidump, procdump, ntdsutil, sekurlsa, mimikatz, reg save hklm | Promote Q3 | Detect, critical | None expected |
| PE written into web content path by web worker | File Write; initiating process in web-tier set; target path contains inetpub, wwwroot, webapps or App_Code | Promote Q4 | Detect, medium | Deployment windows and deployment account |
Custom IOA rules use basename and path patterns only. Nothing here blocks by name of a legitimate server or interpreter binary; every row is detect.
SentinelOne: custom detection rules
| Rule | Source query | Run as | Notes |
|---|---|---|---|
| Web server worker spawns interpreter | Q1, PowerQuery tab | Scheduled custom detection rule | Bind entities so the alert names the host instead of "Unknown Device" |
| Credential tooling from web server worker | Q3, PowerQuery tab | Scheduled custom detection rule | Bind entities so the alert names the host instead of "Unknown Device" |
| PE written into web content path by web worker | Q4, PowerQuery tab | Hunt only until the gap is closed | Q4 is only an approximation here. SentinelOne has no PE-only file-write event. The hunt uses File Creation by a web server or runtime process under a web application path, restricted to executable and library extensions as a proxy for the CQL's PE-centric events. |
Create the rule with POST /web/api/v2.1/cloud-detection/rules, queryType: "scheduled" and queryLang: "2.0", with the query in data.scheduledParams.query. Use 2.0 operator spellings: a rule written with the 1.0 token ContainsCIS was accepted and never fired on a tenant. The API does not warn, so lint the body before deploying.
Microsoft Defender: custom detection rules
| Rule | Source query | Run as | Notes |
|---|---|---|---|
| Web server worker spawns interpreter | Q1, KQL – Defender XDR tab | Custom detection rule, alert only, every hour | The query returns Timestamp, ReportId and DeviceId, which Microsoft recommends for Defender for Endpoint tables so alerts carry the device group scope and the process tree. Remove the Timestamp filter and the final sort and take before saving the rule: the service applies the rule's lookback itself |
| Credential tooling from web server worker | Q3, KQL – Defender XDR tab | Custom detection rule, alert only, every hour | The query returns Timestamp, ReportId and DeviceId, which Microsoft recommends for Defender for Endpoint tables so alerts carry the device group scope and the process tree. Remove the Timestamp filter and the final sort and take before saving the rule: the service applies the rule's lookback itself |
| PE written into web content path by web worker | Q4, KQL – Defender XDR tab | Hunt only until the gap is closed | Q4 is only an approximation here. Defender has no PE-only file-write event. The hunt uses FileCreated by a web server or runtime process under a web application path, restricted to executable and library extensions as a proxy for the CQL's PE-centric events. |
Q2, Q5, Q6, Q7, Q8 and Q9 stay a hunt in this pack, not a rule. A rule can generate only 150 alerts per run. Continuous (NRT) frequency needs a single table and no comment lines, so delete the generated comment header from the query first.
Machine-Readable IOC Appendix
Eleven atomic indicators, all detect and none prevent. The ARTEX host is high confidence but single-primary-source (CrowdStrike, restated by The Hacker News and CSA), the proxy addresses are single-origin, and the reseller domain is a shared service. The import is detect-only.
type,value,action,severity,expiration,description,tags ipv4,38.244.50.120,detect,high,2027-10-16,ARTEX instance and open directory host (CrowdStrike originating research; restated by The Hacker News and CSA),campaign:ARTEX-Korean-Finance ipv4,101.53.80.20,detect,medium,2027-04-16,Proxy IP used during ARTEX-related activity (CrowdStrike only),campaign:ARTEX-Korean-Finance ipv4,205.214.59.31,detect,medium,2027-04-16,Proxy IP used during ARTEX-related activity (CrowdStrike only),campaign:ARTEX-Korean-Finance ipv4,124.155.252.63,detect,medium,2027-04-16,Proxy IP used during ARTEX-related activity (CrowdStrike only),campaign:ARTEX-Korean-Finance ipv4,154.201.79.246,detect,medium,2027-04-16,Proxy IP used during ARTEX-related activity (CrowdStrike only),campaign:ARTEX-Korean-Finance ipv4,23.248.249.90,detect,medium,2027-04-16,Proxy IP used during ARTEX-related activity (CrowdStrike only),campaign:ARTEX-Korean-Finance ipv4,23.158.220.98,detect,medium,2027-04-16,Proxy IP used during ARTEX-related activity (CrowdStrike only),campaign:ARTEX-Korean-Finance ipv4,103.248.148.84,detect,medium,2027-04-16,Proxy IP used during ARTEX-related activity (CrowdStrike only),campaign:ARTEX-Korean-Finance ipv4,203.160.133.172,detect,medium,2027-04-16,Proxy IP used during ARTEX-related activity (CrowdStrike only),campaign:ARTEX-Korean-Finance ipv4,209.209.85.38,detect,medium,2027-04-16,Proxy IP used during ARTEX-related activity (CrowdStrike only),campaign:ARTEX-Korean-Finance domain,xcai.pro,detect,medium,2027-04-16,Likely LLM API proxy or reseller; shared service so detect only (CrowdStrike and The Hacker News),campaign:ARTEX-Korean-Finance
indicatorType,indicatorValue,action,title,description,expirationTime,severity,recommendedActions,rbacGroups,category,mitretechniques,GenerateAlert IpAddress,38.244.50.120,Audit,ARTEX instance and open directory host,ARTEX instance and open directory host (CrowdStrike originating research; restated by The Hacker News and CSA),2027-10-16T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True IpAddress,101.53.80.20,Audit,Proxy IP used during ARTEX-related activity,Proxy IP used during ARTEX-related activity (CrowdStrike only),2027-04-16T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True IpAddress,205.214.59.31,Audit,Proxy IP used during ARTEX-related activity,Proxy IP used during ARTEX-related activity (CrowdStrike only),2027-04-16T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True IpAddress,124.155.252.63,Audit,Proxy IP used during ARTEX-related activity,Proxy IP used during ARTEX-related activity (CrowdStrike only),2027-04-16T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True IpAddress,154.201.79.246,Audit,Proxy IP used during ARTEX-related activity,Proxy IP used during ARTEX-related activity (CrowdStrike only),2027-04-16T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True IpAddress,23.248.249.90,Audit,Proxy IP used during ARTEX-related activity,Proxy IP used during ARTEX-related activity (CrowdStrike only),2027-04-16T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True IpAddress,23.158.220.98,Audit,Proxy IP used during ARTEX-related activity,Proxy IP used during ARTEX-related activity (CrowdStrike only),2027-04-16T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True IpAddress,103.248.148.84,Audit,Proxy IP used during ARTEX-related activity,Proxy IP used during ARTEX-related activity (CrowdStrike only),2027-04-16T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True IpAddress,203.160.133.172,Audit,Proxy IP used during ARTEX-related activity,Proxy IP used during ARTEX-related activity (CrowdStrike only),2027-04-16T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True IpAddress,209.209.85.38,Audit,Proxy IP used during ARTEX-related activity,Proxy IP used during ARTEX-related activity (CrowdStrike only),2027-04-16T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True DomainName,xcai.pro,Audit,Likely LLM API proxy or reseller; shared service so detect o,Likely LLM API proxy or reseller; shared service so detect only (CrowdStrike and The Hacker News),2027-04-16T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True
Column names and allowed values are from Microsoft's documented parameter table. Check each expirationTime before importing: an indicator whose date has passed is already expired. Download the sample CSV from the Import page to confirm the column order before a bulk import. Each file takes at most 500 indicators. Learn describes the import per entity type, so if a mixed file is rejected, split it by indicatorType. An imported indicator does nothing unless the matching Advanced features are on (for example Custom network indicators and Allow or block file).
This pack does not ship a SentinelOne import block: the import format could not be verified against SentinelOne's own documentation. Export a sample from your console and the block can be added with confidence.
# Behavioral signatures (not importable IOCs)
Port: 38.244.50.120:18899 ARTEX instance and open directory (CrowdStrike URL; CSA)
URL path: /.claude/CLAUDE.md on the ARTEX host, an exposed Claude Code instruction file (CrowdStrike)
Parent: web server worker (w3wp, httpd, nginx, apache2, php-fpm, tomcat, node, dotnet) spawning an interpreter
Files: unexpected *.aspx or *.jsp in web application paths (Zscaler)
Traffic: evenly paced, sequential enumeration against login and search endpoints (Zscaler)
Traffic: structured PII (names, phone numbers, financial fields) outbound over HTTPS to unapproved destinations (Zscaler)
Context: api.anthropic.com, api.deepseek.com, open.bigmodel.cn, api.x.ai are LEGITIMATE LLM services.
Weigh only with other evidence. Never block. Never list as malicious infrastructure.# Named tooling (labels only, no actor or campaign name is assigned) ARTEX open-source agentic pentest framework (project reported as now closed source) LLM backends DeepSeek v4.1-flash (primary), Zhipu GLM-5.3, Grok 4.6 (CrowdStrike, Zscaler) Likely reseller xcai.pro (CrowdStrike, THN) Detection name HTML.MalURL.Gen.LZ (Zscaler product detection for related indicators) Note: forks and downloaded copies may remain in circulation (Zscaler).
# Audit checklist (read-only) [ ] Inventory internet-facing apps outside the main authentication path (partner lookups, employee mobile backends) [ ] Server-side authorization on every record-returning endpoint; one broker cannot read another broker's records [ ] Rate limits and lockouts hold when parameters or headers are altered [ ] Responses return only the fields the operation needs (no income or loan limit on a lookup) [ ] No *.aspx / *.jsp files created outside deployment windows (file integrity) [ ] Server egress allowlisted; LLM endpoints not reachable from server networks [ ] Open-source pentest harnesses isolated from production, no production credentials stored in them [ ] Web, WAF and DB logs retained back to 2026-09-20 and reviewed for sequential enumeration
Detection Validation Gates
| Gate | Check | Pass criteria |
|---|---|---|
| Telemetry ready | Process creation with parent and command line (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents); executable file written (CrowdStrike: PeFileWritten, NewExecutableWritten. SentinelOne: File Creation. Defender: DeviceFileEvents); outbound connection (CrowdStrike: NetworkConnectIP4. SentinelOne: IP Connect. Defender: DeviceNetworkEvents); DNS lookup with process (CrowdStrike: DnsRequest. SentinelOne: DNS Resolved. Defender: no endpoint DNS table). Web, WAF and DB logs reachable for section 7 | Sensor on web-tier hosts; web logs retained 30 days or more |
| Benign baseline | Run Q1, Q2, Q4 and Q9 over 30 days on web-tier hosts before alerting | Documented launchers and deployment accounts excluded; remaining hits triaged |
| Positive tests | Run a benign interpreter from a test web worker in a lab; resolve the reseller domain from a lab host | Q1 and Q6 fire; no production traffic to the published addresses is generated |
| Promotion | Promote Q1, Q3 and Q4 to Custom IOA detect only after baseline; keep Q7 and Q8 as hunts | No prevent on any legitimate LLM endpoint or stock binary. Nothing in this pack ships prevent at all, so the import is detect-only |
Hardening: Tiered
- Server-side authorization and field minimisation on lookup endpoints (MITRE M1018, M1048; OWASP ASVS V4 access control). Verify one partner cannot read another's records and that responses omit income and loan-limit fields unless required. Verify by an authenticated cross-account request in a test tenant.
- Rate limits and enumeration alerts on login and search endpoints (MITRE M1036; Zscaler recommendation). Dynamic IP restrictions on IIS or
limit_reqon nginx. Verify by a scripted burst from a test address and confirm HTTP 403 or 429. - Import the Falcon CSV with detect on all eleven. If your own environment corroborates the ARTEX host address independently, you can promote that single address to prevent yourself as a local decision. Verify with Q5 returning zero or triaged hits.
- Retain and review web, WAF and DB logs back to late September (section 7).
- Server egress allowlist (MITRE M1037; CIS Controls v8 4.4 and 13.4). Web-tier hosts reach only named destinations; this also removes LLM endpoints and proxy callbacks from reach. Verify with Q9 and Q7 returning no new destinations.
- Remove write access for the application identity to code directories (MITRE M1022; CIS IIS benchmark). Verify with an
icaclsreadout and Q4. - WAF rules for SQL injection and authentication anomalies in blocking mode after a monitoring period (MITRE M1050).
- Treat open-source pentest harnesses as hostile code (Zscaler): isolate, no production credentials, scope enforced in code. Flagged
best-practice, no formal benchmark.
- Move partner and employee portals behind zero-trust access instead of the open internet (MITRE M1035; NIST SP 800-207).
- Deception accounts and decoy records on lookup services to catch enumeration (Zscaler recommendation; best-practice, no formal benchmark).
- Continuous automated authorization testing of the external attack surface under written scope (CSA recommendation).
Deployable Playbooks
Step 1: Rate-limit by client address on IIS (dynamic IP restrictions)
Prerequisites: IIS with the IP and Domain Restrictions feature; test on a staging site first. Replace the thresholds with your measured baseline.
Reboot required: No (IIS reloads configuration)
# Windows, elevated prompt %windir%\system32\inetsrv\appcmd.exe set config /section:system.webServer/security/dynamicIpSecurity /denyByRequestRate.enabled:true /denyByRequestRate.maxRequests:REPLACE_WITH_MAX /denyByRequestRate.requestIntervalInMilliseconds:REPLACE_WITH_MS /denyAction:Forbidden /commit:apphost # Verify %windir%\system32\inetsrv\appcmd.exe list config /section:system.webServer/security/dynamicIpSecurity
Rollback: appcmd set config /section:system.webServer/security/dynamicIpSecurity /denyByRequestRate.enabled:false /commit:apphost
Step 2: Rate-limit lookup endpoints on nginx
Prerequisites: nginx 1.x; edit a staged copy and run the config test. Replace the rate with your measured baseline.
Reboot required: No (reload only)
# /etc/nginx/conf.d/lookup-ratelimit.conf (http context) limit_req_zone $binary_remote_addr zone=lookup:10m rate=REPLACE_WITH_RATE; # in the server or location block for login and search endpoints limit_req zone=lookup burst=REPLACE_WITH_BURST nodelay; limit_req_status 429; # Verify nginx -t && nginx -s reload
Rollback: Remove the two directives and the zone file, then nginx -t && nginx -s reload.
Step 3: Deny the application identity write access to code directories
Prerequisites: Windows web server; know which folders legitimately need writes (uploads, temp, logs) and exclude them. Take an ACL backup first.
Reboot required: No
# PowerShell, elevated icacls "REPLACE_WITH_CODE_DIR" /save "$env:TEMP\acl-backup.txt" /t icacls "REPLACE_WITH_CODE_DIR" /deny "IIS APPPOOL\REPLACE_WITH_POOL:(OI)(CI)(W,WD,AD)" # Verify icacls "REPLACE_WITH_CODE_DIR" | Select-String "REPLACE_WITH_POOL"
Rollback: icacls REPLACE_WITH_CODE_DIR /remove:d "IIS APPPOOL\REPLACE_WITH_POOL", or restore from the saved ACL file with icacls REPLACE_WITH_PARENT_DIR /restore $env:TEMP\acl-backup.txt.
Step 4: Server egress allowlist (Linux, nftables)
Prerequisites: Application hosts with a known set of outbound destinations; apply on a staging host and keep an out-of-band console session open. Replace placeholders with your allowed ranges.
Reboot required: No
# Linux, root. Example outbound policy for a web-tier host
nft add table inet egress
nft add chain inet egress out '{ type filter hook output priority 0 ; policy accept ; }'
nft add rule inet egress out ct state established,related accept
nft add rule inet egress out oif lo accept
nft add rule inet egress out ip daddr { REPLACE_WITH_ALLOWED_RANGES } accept
nft add rule inet egress out counter log prefix "egress-deny " drop
# Verify
nft list table inet egressRollback: nft delete table inet egress removes the policy immediately.
Step 5: Read-only audit: script files created outside deployments
Prerequisites: Windows or Linux read access to the web roots. Nothing is changed.
Reboot required: No
# PowerShell, read-only
Get-ChildItem -Path REPLACE_WITH_WEBROOT -Recurse -Include *.aspx,*.ashx,*.jsp -ErrorAction SilentlyContinue |
Where-Object { $_.LastWriteTime -gt (Get-Date).AddDays(-30) } |
Select-Object FullName, LastWriteTime, Length
# Linux, read-only
find REPLACE_WITH_WEBROOT -type f \( -name '*.jsp' -o -name '*.aspx' \) -mtime -30 -lsRollback: none required: the step is read-only.
Containment Runbook
| Phase | Actions | Owner | Evidence |
|---|---|---|---|
| Isolate | Network-contain the affected application host with the EDR; do not power off. Take the exposed lookup endpoint offline or behind a maintenance page if enumeration is ongoing. Perimeter-block the ARTEX host address only if your own evidence corroborates it (local decision; the pack ships it as detect). | SOC / IR, app owner | Containment time, host, endpoint taken offline |
| Isolate: by platform | CrowdStrike: network-contain the host. SentinelOne: network quarantine (Disconnect from Network; confirm the label in your console version). Microsoft Defender: Isolate device (full network isolation). | SOC / IR, app owner | Containment time, host, endpoint taken offline |
| Preserve | Export web, WAF, DB audit and proxy logs from 2026-09-20; copy any unexpected script files and hash them; capture memory if policy allows. | IR | Log exports, file hashes |
| Scope | Run Q1 to Q9 on the aid then fleet-wide; run the section 7 hunts to find enumerated records and the volume taken. | SOC | Hit list, affected record count |
| Eradicate | Remove web shells and dropped binaries by hash and path; fix the injectable or bypassable endpoint; rotate application, database and service credentials. | IR, app owner | Patched build, rotated secrets |
| Recover | Restore from known-good build, re-enable the endpoint with rate limits and field minimisation, monitor Q1, Q3, Q5 for 14 days. | App owner, SOC | Clean hunts for 14 days |
| Notify | Follow regulatory and customer notification duties for exposed PII; warn customers about phishing and loan scams that leaked data enables. | Legal, compliance | Notification record |
Detection Coverage Map
| Technique | Behavior | CQL | IOA | CrowdStrike coverage | SentinelOne | Defender XDR |
|---|---|---|---|---|---|---|
| T1595, T1190 | Autonomous recon, SQL injection and authentication bypass against web apps | None | None | GAP outside endpoint telemetry; web, WAF and application logs only (section 7) | GAP | GAP |
| T1110, T1078 | Authentication bypass and sequential enumeration of records | None | None | GAP identity and application tier | GAP | GAP |
| T1041, T1213 | Structured PII exfiltration over HTTPS | Q9 | None | GAP no byte or content visibility; Q9 sees only odd-port callbacks | GAP: same limit as CrowdStrike | GAP: same limit as CrowdStrike |
| T1505.003 | Web shell: PE drops visible, plain-text .aspx and .jsp not | Q4 | Promote Q4 | Partial Partial: script-file writes are a gap | Partial: Q4 approximate | Partial: Q4 approximate |
| T1059, T1190 | Web worker spawns interpreter after exploitation | Q1 | Promote Q1 | Good only when exploitation yields a child process | Good, with assumptions | Good, with assumptions |
| T1082, T1087 | Discovery from web-tier parent | Q2 | None | Partial | Partial: Q2 approximate | Partial: same limit as CrowdStrike |
| T1003 | Credential access from web-tier parent | Q3 | Promote Q3 | Good for the described commands; hypothesis | Good, with assumptions | Good, with assumptions |
| T1090, T1583.003 | Published ARTEX host, proxy addresses, reseller domain | Q5, Q6 | None | Partial perishable, easily replaced; inbound probing not visible | Partial: same limit as CrowdStrike | Partial: same limit as CrowdStrike |
| T1588.007 | LLM endpoint use, ARTEX tooling on corporate hosts | Q7, Q8 | None | Partial low-fidelity hunts | Partial: same limit as CrowdStrike | Partial: same limit as CrowdStrike |
| T1071, T1090 | Web-tier callbacks to public non-web ports | Q9 | None | Partial | Partial: same limit as CrowdStrike | Partial: same limit as CrowdStrike |
Validation gates: see section 11. Roughly half of the campaign (steps 1, 2 and 4) is outside endpoint telemetry and is covered here only by native log hunts.
Hunt Summary Ticket
TITLE: ARTEX agentic pentest framework abused against South Korean financial institutions (unattributed) SEVERITY: High (PII exfiltration confirmed by vendors; initial-access detail not public) DATE: 2026-10-16 VERSION: v0.1 Draft 2026-10-16 SCOPE: Internet-facing partner and employee web applications and the web-tier hosts behind them; server egress HYPOTHESIS: An agentic framework finds and exploits peripheral web apps, exfiltrates customer records over HTTPS, and leaves endpoint traces only after command execution or in connections to its infrastructure. QUERIES: Q1 web worker interpreter; Q2 discovery; Q3 credential tooling; Q4 PE into web paths; Q5 published addresses; Q6 reseller domain; Q7 LLM endpoint context; Q8 ARTEX by name; Q9 odd-port callbacks DO FIRST: Review web, WAF and DB logs back to 2026-09-20 for sequential enumeration; run Q1, Q3 and Q5 over 30 days; apply rate limits and field minimisation. FINDINGS: any hit on the ARTEX host address, an interpreter child of a web worker, credential tooling from a web parent, or evenly paced record lookups. GAPS: exploitation, enumeration and HTTPS exfiltration are outside endpoint telemetry; plain-text .aspx and .jsp writes are not reliably seen; proxy addresses are single-origin and perishable. ACTIONS: contain, preserve logs, fix the endpoint, rotate credentials, import the CSV (detect only), apply Immediate hardening. OWNER: SOC / Incident Response and application owners FOLLOW-UP: Re-hunt after 7 and 30 days and on any new CrowdStrike or Zscaler update.
Changelog
References
| Tier | Source | Used for | Access date |
|---|---|---|---|
| T1 | CrowdStrike: Unknown Threat Actor Uses AI-Driven ARTEX to Target South Korean Finance (2026-10-07) | Infrastructure IOCs, backends, ATT&CK (snapshot 01) | 2026-10-16 |
| T1 | Zscaler ThreatLabz: Threat Actor Abuses Agentic AI Pentesting Framework (2026-10-14) | Attack phases, behavioural indicators, recommendations (snapshot 02) | 2026-10-16 |
| T2 | The Hacker News: ARTEX AI Pentesting Tool Used in Data Theft Attacks (2026-10-08) | Second source for the ARTEX host and reseller domain (snapshot 03) | 2026-10-16 |
| T3 | Cloud Security Alliance: ARTEX research note (2026-10-10) | Port 18899, evidentiary caveats (snapshot 04) | 2026-10-16 |
| T3 | RuntimeWire digest | Caveat that ARTEX use is not established for every incident (snapshot 05) | 2026-10-16 |
| Ref | MITRE ATT&CK | Technique IDs and mitigation M-numbers | 2026-10-16 |