Techniques
Sample rules
PTC Windchill Gateway Command Execution
- source: splunk
- technicques:
Description
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.
PTC identifies run?c=, run?p=, .jsp?c=, and .jsp?p= request patterns as indicators to monitor during Windchill and FlexPLM exploitation response.
Successful activity may allow an unauthenticated attacker to execute operating system commands or read files through a weaponized gateway or JSP component.
Detection logic
`windchill_log4j`
("WindchillGW/GW/run" OR "WindchillAuthGW/GW/run" OR "/GW/run?" OR "run?c=" OR "run?p=" OR ".jsp?c=" OR ".jsp?p=" OR "dpr_")
| 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>.*)$"
| search logger IN ("wt.servlet.ServletRequestMonitor.request", "wt.method.MethodContextMonitor.contexts.servletRequest")
| 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>.*)$"
| eval parts=split(rest,", ")
| eval event_type=case(logger="wt.servlet.ServletRequestMonitor.request","servlet_request",logger="wt.method.MethodContextMonitor.contexts.servletRequest","method_context_servlet_request",true(),"other")
| eval src_ip=case(event_type="servlet_request",mvindex(parts,2),event_type="method_context_servlet_request",mvindex(parts,5))
| eval uri_path=case(event_type="servlet_request",mvindex(parts,3),event_type="method_context_servlet_request",mvindex(parts,8))
| eval query_string=if(event_type="servlet_request",mvindex(parts,4),null())
| eval http_method=if(event_type="servlet_request",mvindex(parts,5),null())
| eval status=if(event_type="servlet_request",tonumber(mvindex(parts,6)),null())
| rex field=uri_path "^(?<uri_only>[^\?]+)(?:\?(?<uri_query>.*))?$"
| eval query_string=if(query_string="-",null(),query_string)
| eval query_string=coalesce(query_string,uri_query)
| rex field=query_string "(?i)(?:^
|&)(?<query_param>[cp])=(?<query_value>[^&]*)"
| eval query_param=lower(query_param), query_value=urldecode(replace(query_value,"\+","%20"))
| where isnotnull(uri_only) AND isnotnull(query_param)
| 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")
| where NOT (query_param="c" AND match(query_value,"(?i)^echo(\s
|20)+GW_READY_OK$"))
| eval activity=case(query_param="c","command_execution_parameter",query_param="p","file_read_parameter",true(),"unknown")
| eval src=src_ip
| stats count min(_time) as firstTime
max(_time) as lastTime
values(log_level) as log_level
values(logger) as logger
values(http_method) as http_method
values(status) as status
values(uri_only) as uri_path
values(query_string) as query_string
by src activity query_param query_value
| `security_content_ctime(firstTime)`
| `security_content_ctime(lastTime)`
| `ptc_windchill_gateway_command_execution_filter`