|
| 1 | +name: PTC Windchill Gateway Command Execution |
| 2 | +id: d725b390-fe86-47df-b9a1-57497915403e |
| 3 | +version: 1 |
| 4 | +creation_date: '2026-06-14' |
| 5 | +modification_date: '2026-06-14' |
| 6 | +author: Nasreddine Bencherchali, Splunk |
| 7 | +status: production |
| 8 | +type: Anomaly |
| 9 | +description: | |
| 10 | + This analytic detects Windchill MethodServer log4j events showing suspicious `c=` command execution or `p=` file read parameters sent to Windchill gateway paths associated with CVE-2026-4681 exploitation. |
| 11 | + PTC identifies `run?c=`, `run?p=`, `.jsp?c=`, and `.jsp?p=` request patterns as indicators to monitor during Windchill and FlexPLM exploitation response. |
| 12 | + Successful activity may allow an unauthenticated attacker to execute operating system commands or read files through a weaponized gateway or JSP component. |
| 13 | +data_source: |
| 14 | + - Windchill Log4j |
| 15 | +search: |- |
| 16 | + `windchill_log4j` |
| 17 | + ("WindchillGW/GW/run" OR "WindchillAuthGW/GW/run" OR "/GW/run?" OR "run?c=" OR "run?p=" OR ".jsp?c=" OR ".jsp?p=" OR "dpr_") |
| 18 | + | rex field=_raw "^(?:[^:\r\n]+:)?(?<log_ts>\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2},\d{3})\s+(?<log_level>\w+)\s+\[(?<thread>[^\]]+)\]\s+(?<logger>\S+)\s+-\s+(?<payload>.*)$" |
| 19 | + | search logger IN ("wt.servlet.ServletRequestMonitor.request", "wt.method.MethodContextMonitor.contexts.servletRequest") |
| 20 | + | rex field=payload "^(?<event_ts>\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}\.\d{3})\s+(?<event_tz>[+\-]\d{4}),\s+(?<rest>.*)$" |
| 21 | + | eval parts=split(rest,", ") |
| 22 | + | eval event_type=case(logger="wt.servlet.ServletRequestMonitor.request","servlet_request",logger="wt.method.MethodContextMonitor.contexts.servletRequest","method_context_servlet_request",true(),"other") |
| 23 | + | eval src_ip=case(event_type="servlet_request",mvindex(parts,2),event_type="method_context_servlet_request",mvindex(parts,5)) |
| 24 | + | eval uri_path=case(event_type="servlet_request",mvindex(parts,3),event_type="method_context_servlet_request",mvindex(parts,8)) |
| 25 | + | eval query_string=if(event_type="servlet_request",mvindex(parts,4),null()) |
| 26 | + | eval http_method=if(event_type="servlet_request",mvindex(parts,5),null()) |
| 27 | + | eval status=if(event_type="servlet_request",tonumber(mvindex(parts,6)),null()) |
| 28 | + | rex field=uri_path "^(?<uri_only>[^\?]+)(?:\?(?<uri_query>.*))?$" |
| 29 | + | eval query_string=if(query_string="-",null(),query_string) |
| 30 | + | eval query_string=coalesce(query_string,uri_query) |
| 31 | + | rex field=query_string "(?i)(?:^|&)(?<query_param>[cp])=(?<query_value>[^&]*)" |
| 32 | + | eval query_param=lower(query_param), query_value=urldecode(replace(query_value,"\+","%20")) |
| 33 | + | where isnotnull(uri_only) AND isnotnull(query_param) |
| 34 | + | where (match(uri_only,"(?i)(^|/)GW/run$") OR match(uri_only,"(?i)/servlet/(WindchillGW|WindchillAuthGW)/GW/run$") OR match(uri_only,"(?i)(^|/)dpr_[0-9a-f]{8}\.jsp$")) AND (query_param="c" OR query_param="p") |
| 35 | + | where NOT (query_param="c" AND match(query_value,"(?i)^echo(\s|20)+GW_READY_OK$")) |
| 36 | + | eval activity=case(query_param="c","command_execution_parameter",query_param="p","file_read_parameter",true(),"unknown") |
| 37 | + | eval src=src_ip |
| 38 | +
|
| 39 | + | stats count min(_time) as firstTime |
| 40 | + max(_time) as lastTime |
| 41 | + values(log_level) as log_level |
| 42 | + values(logger) as logger |
| 43 | + values(http_method) as http_method |
| 44 | + values(status) as status |
| 45 | + values(uri_only) as uri_path |
| 46 | + values(query_string) as query_string |
| 47 | + by src activity query_param query_value |
| 48 | +
|
| 49 | + | `security_content_ctime(firstTime)` |
| 50 | + | `security_content_ctime(lastTime)` |
| 51 | + | `ptc_windchill_gateway_command_execution_filter` |
| 52 | +how_to_implement: | |
| 53 | + To implement this analytic, ingest PTC Windchill MethodServer log4j logs into Splunk with sourcetype `log4j`. |
| 54 | + Update the `windchill_log4j` macro to include the appropriate index constraints for your environment. |
| 55 | + The analytic expects log entries from the `wt.servlet.ServletRequestMonitor.request` and `wt.method.MethodContextMonitor.contexts.servletRequest` loggers and parses embedded servlet URI, query parameter, source IP, HTTP method, and status fields. |
| 56 | +known_false_positives: | |
| 57 | + Requests to `GW/run` or randomly named `dpr_<8 hex>.jsp` files with `c=` or `p=` parameters should not be expected during normal Windchill operation. |
| 58 | + Validate whether red-team testing, vulnerability scanning, or incident response replay generated the activity before closing as benign. |
| 59 | +references: |
| 60 | + - https://www.ptc.com/en/about/trust-center/advisory-center/active-advisories/windchill-flexplm-critical-vulnerability |
| 61 | + - https://nvd.nist.gov/vuln/detail/CVE-2026-4681 |
| 62 | + - https://www.cisa.gov/news-events/ics-advisories/icsa-26-085-03 |
| 63 | +drilldown_searches: |
| 64 | + - name: View the detection results for - "$src$" |
| 65 | + search: '%original_detection_search% | search src = "$src$"' |
| 66 | + earliest_offset: $info_min_time$ |
| 67 | + latest_offset: $info_max_time$ |
| 68 | + - name: View risk events for the last 7 days for - "$src$" |
| 69 | + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$src$") | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' |
| 70 | + earliest_offset: 7d |
| 71 | + latest_offset: "0" |
| 72 | +intermediate_findings: |
| 73 | + entities: |
| 74 | + - field: src |
| 75 | + type: system |
| 76 | + score: 20 |
| 77 | + message: Potential PTC Windchill gateway command or file read activity detected from $src$. |
| 78 | +threat_objects: |
| 79 | + - field: src |
| 80 | + type: ip_address |
| 81 | +analytic_story: |
| 82 | + - PTC Windchill Exploitation |
| 83 | +asset_type: Web Application |
| 84 | +cve: |
| 85 | + - CVE-2026-4681 |
| 86 | +mitre_attack_id: |
| 87 | + - T1190 |
| 88 | + - T1059 |
| 89 | + - T1005 |
| 90 | +product: |
| 91 | + - Splunk Enterprise |
| 92 | + - Splunk Enterprise Security |
| 93 | + - Splunk Cloud |
| 94 | +category: application |
| 95 | +security_domain: network |
| 96 | +tests: |
| 97 | + - name: True Positive Test |
| 98 | + attack_data: |
| 99 | + - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/emerging_threats/windchill/windchill_exploitation.log |
| 100 | + source: not_applicable |
| 101 | + sourcetype: log4j |
| 102 | + test_type: unit |
0 commit comments