| 1 | rules: |
| 2 | |
| 3 | - id: cyc-py-pickle-load |
| 4 | languages: [python] |
| 5 | severity: ERROR |
| 6 | message: > |
| 7 | pickle.load/loads on untrusted data is RCE-by-design (gadget via __reduce__). |
| 8 | IMPACT GATE: Family 1 RCE if the bytes are attacker-controlled. Confirm source->sink in Joern, |
| 9 | then prove with an executing gadget (callback / command output). |
| 10 | metadata: |
| 11 | cwe: ["CWE-502: Deserialization of Untrusted Data"] |
| 12 | confidence: HIGH |
| 13 | archetype: [ml-model-loaders, ai-agent-mcp] |
| 14 | impact: rce |
| 15 | patterns: |
| 16 | - pattern-either: |
| 17 | - pattern: pickle.loads(...) |
| 18 | - pattern: pickle.load(...) |
| 19 | - pattern: cPickle.loads(...) |
| 20 | - pattern: cPickle.load(...) |
| 21 | - pattern: _pickle.loads(...) |
| 22 | - pattern: dill.loads(...) |
| 23 | - pattern: dill.load(...) |
| 24 | - pattern: cloudpickle.loads(...) |
| 25 | - pattern: cloudpickle.load(...) |
| 26 | |
| 27 | - id: cyc-py-yaml-unsafe-load |
| 28 | languages: [python] |
| 29 | severity: ERROR |
| 30 | message: > |
| 31 | yaml.load without SafeLoader (or yaml.unsafe_load/full_load) constructs arbitrary Python |
| 32 | objects -> RCE. IMPACT GATE: Family 1. yaml.safe_load is the safe form and is intentionally |
| 33 | NOT matched. |
| 34 | metadata: |
| 35 | cwe: ["CWE-502: Deserialization of Untrusted Data"] |
| 36 | confidence: HIGH |
| 37 | archetype: [devops-infra, ai-agent-mcp] |
| 38 | impact: rce |
| 39 | patterns: |
| 40 | - pattern-either: |
| 41 | - pattern: yaml.load($X) |
| 42 | - pattern: yaml.unsafe_load(...) |
| 43 | - pattern: yaml.full_load(...) |
| 44 | - pattern-not: yaml.load($X, Loader=yaml.SafeLoader) |
| 45 | - pattern-not: yaml.load($X, Loader=SafeLoader) |
| 46 | |
| 47 | - id: cyc-py-ml-unsafe-load |
| 48 | languages: [python] |
| 49 | severity: ERROR |
| 50 | message: > |
| 51 | torch.load / joblib.load / numpy.load(allow_pickle=True) / pandas.read_pickle deserialize via |
| 52 | pickle under the hood -> RCE from a malicious model/array file. IMPACT GATE: Family 1, the |
| 53 | model-file RCE class (ml-model-loaders archetype). |
| 54 | metadata: |
| 55 | cwe: ["CWE-502: Deserialization of Untrusted Data"] |
| 56 | confidence: HIGH |
| 57 | archetype: [ml-model-loaders] |
| 58 | impact: rce |
| 59 | patterns: |
| 60 | - pattern-either: |
| 61 | - pattern: torch.load(...) |
| 62 | - pattern: joblib.load(...) |
| 63 | - pattern: pandas.read_pickle(...) |
| 64 | - pattern: pd.read_pickle(...) |
| 65 | - pattern: numpy.load(..., allow_pickle=True, ...) |
| 66 | - pattern: np.load(..., allow_pickle=True, ...) |
| 67 | |
| 68 | - id: cyc-py-marshal-shelve |
| 69 | languages: [python] |
| 70 | severity: ERROR |
| 71 | message: > |
| 72 | marshal.loads / shelve.open deserialize untrusted bytes into executable/code objects. |
| 73 | IMPACT GATE: Family 1 if the source is attacker-controlled. |
| 74 | metadata: |
| 75 | cwe: ["CWE-502: Deserialization of Untrusted Data"] |
| 76 | confidence: MEDIUM |
| 77 | archetype: [ml-model-loaders, devops-infra] |
| 78 | impact: rce |
| 79 | patterns: |
| 80 | - pattern-either: |
| 81 | - pattern: marshal.loads(...) |
| 82 | - pattern: marshal.load(...) |
| 83 | - pattern: shelve.open(...) |
| 84 | |
| 85 | - id: cyc-java-objectinputstream |
| 86 | languages: [java] |
| 87 | severity: ERROR |
| 88 | message: > |
| 89 | ObjectInputStream.readObject on untrusted bytes is the canonical Java deserialization RCE |
| 90 | (ysoserial gadget chains). IMPACT GATE: Family 1. |
| 91 | metadata: |
| 92 | cwe: ["CWE-502: Deserialization of Untrusted Data"] |
| 93 | confidence: HIGH |
| 94 | archetype: [devops-infra] |
| 95 | impact: rce |
| 96 | patterns: |
| 97 | - pattern-either: |
| 98 | - pattern: (ObjectInputStream $OIS).readObject() |
| 99 | - pattern: $OIS.readObject() |
| 100 | - pattern: $OIS.readUnshared() |
| 101 | - pattern: new ObjectInputStream(...) |
| 102 | - metavariable-regex: |
| 103 | metavariable: $OIS |
| 104 | regex: "(?i).*(ois|objectinput|in|stream).*" |
| 105 | |
| 106 | - id: cyc-java-other-deser |
| 107 | languages: [java] |
| 108 | severity: ERROR |
| 109 | message: > |
| 110 | XStream.fromXML / SnakeYAML new Yaml().load / Kryo.readObject deserialize untrusted input into |
| 111 | arbitrary types -> RCE. IMPACT GATE: Family 1. |
| 112 | metadata: |
| 113 | cwe: ["CWE-502: Deserialization of Untrusted Data"] |
| 114 | confidence: MEDIUM |
| 115 | archetype: [devops-infra] |
| 116 | impact: rce |
| 117 | patterns: |
| 118 | - pattern-either: |
| 119 | - pattern: $X.fromXML(...) |
| 120 | - pattern: new Yaml().load(...) |
| 121 | - pattern: $K.readClassAndObject(...) |
| 122 | |
| 123 | - id: cyc-js-node-serialize |
| 124 | languages: [javascript, typescript] |
| 125 | severity: ERROR |
| 126 | message: > |
| 127 | node-serialize unserialize() executes an IIFE embedded in the payload -> RCE. IMPACT GATE: |
| 128 | Family 1. |
| 129 | metadata: |
| 130 | cwe: ["CWE-502: Deserialization of Untrusted Data"] |
| 131 | confidence: HIGH |
| 132 | archetype: [ai-agent-mcp, devops-infra] |
| 133 | impact: rce |
| 134 | patterns: |
| 135 | - pattern-either: |
| 136 | - pattern: $S.unserialize(...) |
| 137 | - pattern: serialize.unserialize(...) |
| 138 | - pattern: $F.deepDeserialize(...) |