### Local Install and Usage Source: https://github.com/carlospolop/purplepanda/blob/master/README.md Instructions for cloning the repository, setting up a virtual environment, installing dependencies, exporting environment variables for Neo4j, and running the main script. ```bash git clone https://github.com/carlospolop/PurplePanda cd PurplePanda python3 -m venv . source bin/activate python3 -m pip install -r requirements.txt export PURPLEPANDA_NEO4J_URL="bolt://neo4j@localhost:7687" export PURPLEPANDA_PWD="s3cr3tpassword" # Install this if you are going to use GCP gcloud components install gke-gcloud-auth-plugin python3 main.py -h # Get help python3 main.py -e -p google,github,k8s --github-only-org --k8s-get-secret-values --gcp-get-secret-values # Enumerate google, github and k8s ``` -------------------------------- ### Minimal CircleCI Configuration Example Source: https://github.com/carlospolop/purplepanda/blob/master/intel/circleci/README.md A minimal configuration example for the CIRCLECI_DISCOVERY environment variable, showing only the mandatory 'token' attribute. ```yaml circleci: - token: 2047681a0c123e4f3cfea12301933768771db123 ``` -------------------------------- ### K8s Discovery Module Usage Example Source: https://github.com/carlospolop/purplepanda/blob/master/intel/k8s/README.md Example of how to export the K8S_DISCOVERY environment variable with a base64 encoded configuration and run the PurplePanda k8s module. ```bash export K8S_DISCOVERY="azhzOgogIC0gZmlsZV9wYXRoOiAvcGF0aC90by9rdWJlY29uZi50eHQK" python3 main.py -p k8s --k8s-get-secret-values -e ``` -------------------------------- ### Example Concourse Configuration Source: https://github.com/carlospolop/purplepanda/blob/master/intel/concourse/README.md An example of a Concourse configuration snippet, showing the 'uri' and 'token' parameters. ```yaml concourse: - uri: http://127.0.0.1:8080 token: 9rEpFtDcE1234aIuwVNEn+jok24E9jNiAAAAAA ``` -------------------------------- ### Google Discovery Configuration Example Source: https://github.com/carlospolop/purplepanda/blob/master/intel/google/README.md Example YAML configuration for the GOOGLE_DISCOVERY environment variable, showing options for file path, quota project ID, service account ID, and scopes. ```yaml google: - file_path: "string" quota_project_id: "string" service_account_id: "string" scopes: - "https://www.googleapis.com/auth/cloud-platform" # file_path is the path to the JSON file containing the user/service account credentials. It's mandatory, if empty default file is used. # quota_project_id is optional, provide the name of the project. # service_account_id is optional, provide the same of the service account to impersonate to enumerate the environment. # scopes is optional, provide a list of scopes if you whish. ``` -------------------------------- ### Github Discovery Configuration Example Source: https://github.com/carlospolop/purplepanda/blob/master/intel/github/README.md Example of the YAML configuration structure for the GITHUB_DISCOVERY environment variable, specifying token, username, password, URL, and organization name. ```yaml github: - token: "string" username: "string" password: "string" url: "https://api.github.com/graphql" org_name: "string" # At least token or username+password need to be configured # URL is optional, provide it ONLY if needed # org_name can be empty or unexistent (but better if provided to use more specific options) ``` -------------------------------- ### Install GCP Dependency Source: https://github.com/carlospolop/purplepanda/blob/master/intel/google/README.md Command to install the necessary GCP dependency for the module. ```bash gcloud components install gke-gcloud-auth-plugin ``` -------------------------------- ### K8s Discovery Configuration Example Source: https://github.com/carlospolop/purplepanda/blob/master/intel/k8s/README.md Example of the configuration structure for the K8s Discovery module, specifying token, URL, or file path. ```yaml k8s: - token: "string" url: "string" cluster_id: "Home_Clusters" - file_path: "string" cluster_id: "GCP-GKE-Name" # token + url or file_path is mandatory ``` -------------------------------- ### CircleCI Discovery Configuration Example Source: https://github.com/carlospolop/purplepanda/blob/master/intel/circleci/README.md Example of the configuration structure for the CIRCLECI_DISCOVERY environment variable, specifying CircleCI URL, token, organization slug, and projects. ```yaml circleci: - url: "string" token: "https://ci.example.com" org_slug: "github/org_name" projects: ["repo1", "repo2"] # token is manadory, url, org_slug and projects are optional ``` -------------------------------- ### Exporting GITHUB_DISCOVERY and Running PurplePanda Source: https://github.com/carlospolop/purplepanda/blob/master/intel/github/README.md Example bash command to export the base64 encoded GITHUB_DISCOVERY environment variable and then run the PurplePanda github module. ```bash export GITHUB_DISCOVERY="Z2l0aHViOgotIHRva2VuOiAiQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBIgogIG9yZ19uYW1lOiAib3JnX25hbWUiCg==" python3 main.py -p github --github-only-org -e ``` -------------------------------- ### Google Discovery Configuration Examples with Service Account Source: https://github.com/carlospolop/purplepanda/blob/master/intel/google/README.md Examples of Google Discovery configuration, one with a default file path and another specifying a service account ID to impersonate. ```yaml google: - file_path: "" - file_path: "" service_account_id: "some-sa-email@sidentifier.iam.gserviceaccount.com" ``` -------------------------------- ### Exporting CIRCLECI_DISCOVERY and Running PurplePanda Source: https://github.com/carlospolop/purplepanda/blob/master/intel/circleci/README.md Example of how to export the base64 encoded CIRCLECI_DISCOVERY environment variable and then run the PurplePanda tool to discover CircleCI resources. ```bash export CIRCLECI_DISCOVERY="Y2lyY2xlY2k6CiAgLSB0b2tlbjogMjA0NzY4MWEwYzEyM2U0ZjNjZmVhMTIzMDE5MzM3Njg3NzFkYjEyMwo=" python3 main.py -p circleci -e ``` -------------------------------- ### K8s - get assets in default ns Source: https://github.com/carlospolop/purplepanda/blob/master/intel/k8s/HOW_TO_USE.md Show assets inside the default namespace. Example: K8s - get assets in default ns ```cypher match(a:K8sNamespace{ns_name:"default"})<-[r:PART_OF]-(b) return a,r,b ``` -------------------------------- ### Minimal K8s Discovery Configuration Source: https://github.com/carlospolop/purplepanda/blob/master/intel/k8s/README.md A minimal configuration example for the K8s Discovery module using only the file_path attribute. ```yaml k8s: - file_path: /path/to/kubeconf.txt ``` -------------------------------- ### Gcp - full privesc path to resource Source: https://github.com/carlospolop/purplepanda/blob/master/intel/google/HOW_TO_USE.md Get the full privilege escalation path to a resource. ```cypher MATCH (res:GcpResource) WHERE res.email = $res OR res.domain = $res OR res.name = $res WITH res MATCH (res)<-[r1:PRIVESC]-(ppal1) OPTIONAL MATCH r = (ppal1)<-[:MEMBER_OF*..]-(ppal2) WITH *, relationships(r) as rels RETURN res,r1,ppal1,ppal2 ``` -------------------------------- ### Github Discovery Configuration with Token and Org Name Source: https://github.com/carlospolop/purplepanda/blob/master/intel/github/README.md A specific example of the YAML configuration for GITHUB_DISCOVERY, using a token and an organization name. ```yaml github: - token: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" org_name: "org_name" ``` -------------------------------- ### Gcp - Open Resources Source: https://github.com/carlospolop/purplepanda/blob/master/intel/google/HOW_TO_USE.md Show all the resources open to all users. Example: Gcp - Open Resources ```cypher MATCH(u:GcpUserAccount)-[role:HAS_ROLE]->(resource) WHERE u.name="allUsers" OR u.name="allAuthenticatedUsers" RETURN u,role,resource ``` -------------------------------- ### K8s - full external privesc to ns $ns Source: https://github.com/carlospolop/purplepanda/blob/master/intel/k8s/HOW_TO_USE.md Show the full privilege escalation paths from principals to principals in different namespaces. Example: K8s - external privesc to ns default ```cypher MATCH (ns:K8sNamespace)<-[:PART_OF]-(ppal1:K8sPrincipal)<-[r:PRIVESC]-(ppal2:K8sPrincipal) WHERE ns.name CONTAINS $ns AND NOT EXISTS( (ppal2)-[:PART_OF]->(ns) ) RETURN ns,ppal1,r,ppal2 ``` -------------------------------- ### K8s - sa from $ns1 privesc to ns $ns2 Source: https://github.com/carlospolop/purplepanda/blob/master/intel/k8s/HOW_TO_USE.md Show the privilege escalation paths from principals to principals in different namespaces. Example: K8s - external privesc to ns default ```cypher MATCH (ns1:K8sNamespace)<-[:PART_OF]-(ppal1:K8sPrincipal)-[r:PRIVESC]->(ppal2:K8sServiceAccount)-[:PART_OF]->(ns2:K8sNamespace) WHERE ns1.name CONTAINS $ns1 AND ns2.name CONTAINS $ns2 RETURN ppal1,r,ppal2 ``` -------------------------------- ### K8s - privesc in ns $ns Source: https://github.com/carlospolop/purplepanda/blob/master/intel/k8s/HOW_TO_USE.md Show the internal privilege escalation paths inside a namespace (depth 1 to avoid eternal queries). Example: K8s - privesc in ns default ```cypher MATCH (ns:K8sNamespace)<-[:PART_OF]-(ppal1:K8sPrincipal)-[r:PRIVESC]->(ppal2:K8sPrincipal)-[:PART_OF]->(ns) WHERE ns.name CONTAINS $ns RETURN ns,ppal1,r,ppal2 ``` -------------------------------- ### Gcp - full privesc path to organizations Source: https://github.com/carlospolop/purplepanda/blob/master/intel/google/HOW_TO_USE.md Get the full privilege escalation path to all organizations. ```cypher MATCH (res:GcpOrganization) MATCH (res)<-[r1:PRIVESC]-(ppal1) OPTIONAL MATCH r = (ppal1)<-[:MEMBER_OF*..]-(ppal2) WITH *, relationships(r) as rels RETURN res,r1,ppal1,ppal2,rels ``` -------------------------------- ### K8s - sa external privesc to ns $ns Source: https://github.com/carlospolop/purplepanda/blob/master/intel/k8s/HOW_TO_USE.md Show the privilege escalation paths from principals to principals in different namespaces. Example: K8s - external privesc to ns default ```cypher MATCH (ns:K8sNamespace)<-[r1:PART_OF]-(ppal1:K8sPrincipal)<-[r2:PRIVESC]-(ppal2:K8sServiceAccount) WHERE ns.name CONTAINS $ns AND NOT EXISTS( (ppal2)-[:PART_OF]->(ns) ) RETURN ns,r1,ppal1,r2,ppal2 ``` -------------------------------- ### Gcp - full privesc path from principal Source: https://github.com/carlospolop/purplepanda/blob/master/intel/google/HOW_TO_USE.md Get the full privilege escalation path of a principal. ```cypher MATCH (ppal:GcpPrincipal) WHERE ppal.email = $ppal OR ppal.domain = $ppal OR ppal.name = $ppal WITH ppal OPTIONAL MATCH r1 =(ppal)-[:PRIVESC*..]->(res1) OPTIONAL MATCH r2 = (ppal)-[:MEMBER_OF*..]->(g)-[:PRIVESC*..]->(res2) WITH *, relationships(r1) as rels1, relationships(r2) as rels2 RETURN ppal,res1,rels1,rels2,g,res2 ``` -------------------------------- ### Gcp - open FW rules and public VMs $verbose Source: https://github.com/carlospolop/purplepanda/blob/master/intel/google/HOW_TO_USE.md Show all the firewall rules open to the internet and all the machines with public IPs. Use verbose > 1 for a more specific search. Example: Gcp - open FW rules and public VMs 2 ```cypher OPTIONAL MATCH (fw:GcpFirewallRule)-[fw_r:PROTECT]-(fw_n:GcpNetwork) WHERE fw.direction = "INGRESS" AND ( ($verbose > 2 OR fw.allowed <> ["icmp:*"]) AND ( any(iprange in fw.sourceRanges WHERE iprange CONTAINS "0.0.0.0" OR iprange CONTAINS "::/0") OR ($verbose > 1 AND ( any(iprange in fw.sourceRanges WHERE NOT any(priv_reg in ["^127\..*","^10\..*", "^172\.1[6-9]\..*", "^172\.2[0-9]\..*", "^172\.3[0-1]\..*", "^192\.168\..*"] WHERE iprange =~ priv_reg) ) )) ) ) OPTIONAL MATCH (c:GcpComputeInstance)-[c_r:CONNECTED]-(sn:GcpSubnetwork)-[pn:PART_OF]-(n:GcpNetwork) WHERE any(ip_addr IN c_r.accessConfigs_natIPs WHERE NOT any(priv_reg in ["^127\..*","^10\..*", "^172\.1[6-9]\..*", "^172\.2[0-9]\..*", "^172\.3[0-1]\..*", "^192\.168\..*"] WHERE ip_addr =~ priv_reg) ) OPTIONAL MATCH (composer:GcpComposerEnv)<-[p_o:PART_OF]-(cluster:GcpCluster) WHERE any(iprange IN composer.allowedIpRanges WHERE NOT any(priv_reg in ["^127\..*","^10\..*", "^172\.1[6-9]\..*", "^172\.2[0-9]\..*", "^172\.3[0-1]\..*", "^192\.168\..*"] WHERE iprange =~ priv_reg) ) RETURN fw, fw_r, fw_n, c, c_r, sn, pn, n, composer, p_o, cluster ``` -------------------------------- ### K8s - privesc to pod aws Source: https://github.com/carlospolop/purplepanda/blob/master/intel/k8s/HOW_TO_USE.md Show the privilege escalation path to pods with aws permissions (depth just 1 or it could never end). Example: K8s - privesc to pod aws ```cypher MATCH (ns:K8sNamespace)<-[:PART_OF]-(p:K8sPod) WHERE p.iam_amazonaws_com_role <> "" MATCH (ns)<-[:PART_OF]-(ppal1:K8sPrincipal)<-[r:PRIVESC]-(ppal2:K8sPrincipal) WHERE r.title CONTAINS "pod creation" RETURN p,ppal2 ``` -------------------------------- ### K8s - privesc to sa gcp Source: https://github.com/carlospolop/purplepanda/blob/master/intel/k8s/HOW_TO_USE.md Show the privilege escalation path to service accounts with gcp permissions (depth just 1 or it could never end). Example: K8s - privesc to sa gcp ```cypher MATCH (ppal:K8sPrincipal)-[r:PRIVESC]->(gcpsa:GcpServiceAccount) RETURN ppal,r,gcpsa ``` -------------------------------- ### K8s - default sas with privesc Source: https://github.com/carlospolop/purplepanda/blob/master/intel/k8s/HOW_TO_USE.md Show default SAs that can escalate privileges. Example: K8s - default sas with privesc ```cypher MATCH (ppal:K8sPrincipal)-[r:PRIVESC]->(u) WHERE ppal.name =~ ".*:default" RETURN ppal,r,u ``` -------------------------------- ### K8s - privesc to sa aws Source: https://github.com/carlospolop/purplepanda/blob/master/intel/k8s/HOW_TO_USE.md Show the privilege escalation path to service accounts with aws permissions (depth just 1 or it could never end). Example: K8s - privesc to sa aws ```cypher MATCH r = (sa:K8sServiceAccount)<-[:PRIVESC]-(ppal:K8sPrincipal) WHERE sa.iam_amazonaws_role_arn <> "" WITH *, relationships(r) as privescs RETURN ppal,privescs,sa ``` -------------------------------- ### K8s - node escape from $ppal with depth 2 Source: https://github.com/carlospolop/purplepanda/blob/master/intel/k8s/HOW_TO_USE.md Show ways to escape to the node from a principal with a max depth of 2. Example: K8s - node escape from default:default with depth 2 ```cypher MATCH (ppal:K8sPrincipal) WHERE ppal.name CONTAINS $ppal OPTIONAL MATCH r = (ppal)-[:PRIVESC*..2]->(ppal2:K8sPrincipal) WHERE NOT ppal.potential_escape_to_node AND ppal2.potential_escape_to_node WITH *, relationships(r) as privescs RETURN ppal,privescs,ppal2 ``` -------------------------------- ### Gcp - permissions in $identity1 but not in $identity2 filtered by $filter Source: https://github.com/carlospolop/purplepanda/blob/master/intel/google/HOW_TO_USE.md Get permissions that the first identity has and not the second and contains some string. Example: Gcp - permissions in user1@domain.com but not in user2@domain.com filtered by bigquery ```cypher MATCH (identity2:GcpPrincipal) WHERE identity2.email = identity2.email = $identity2 OR identity2.domain = $identity2 OR identity2.name = $identity2 OPTIONAL MATCH (identity2)-[h21:HAS_ROLE]->(r21) OPTIONAL MATCH (identity2)-[:MEMBER_OF*..]->()-[h22:HAS_ROLE]->(r22) OPTIONAL MATCH (perm2:GcpPermission)<-[:CONTAINS]-(role2:GcpRole) WITH perm2, role2, collect(h21) as h21_list, collect(h22) as h22_list WHERE toLower(perm2.name) CONTAINS toLower($filter) AND any(rel in h21_list WHERE role2.name IN rel.roles) OR any(rel in h22_list WHERE role2.name in rel.roles) WITH DISTINCT collect(perm2.name) AS perms_names_2 MATCH (identity1:GcpPrincipal) WHERE identity1.email = $identity1 OR identity1.domain = $identity1 OR identity1.name = $identity1 OPTIONAL MATCH (identity1)-[h11:HAS_ROLE]->(r11) OPTIONAL MATCH (identity1)-[:MEMBER_OF*..]->()-[h12:HAS_ROLE]->(r12) OPTIONAL MATCH (perm1:GcpPermission)<-[:CONTAINS]-(role1:GcpRole) WITH perm1, role1, collect(h11) as h11_list, collect(h12) as h12_list, perms_names_2 WHERE toLower(perm1.name) CONTAINS toLower($filter) AND NOT perm1.name IN perms_names_2 AND (any(rel in h11_list WHERE role1.name IN rel.roles) OR any(rel in h12_list WHERE role1.name in rel.roles)) RETURN perm1 ``` -------------------------------- ### Docker Usage Source: https://github.com/carlospolop/purplepanda/blob/master/README.md Instructions for building the Docker image and running the PurplePanda container, including setting environment variables for Neo4j and other services. ```bash # Consider adding the API keys in the Dockerfile docker rm -f purplepanda docker build --tag=purplepanda . # Execute -h ## CHange -h for the params you want to run purplepanda with docker run -t \ -e PURPLEPANDA_NEO4J_URL="bolt://neo4j@host.docker.internal:7687" \ -e PURPLEPANDA_PWD="s3cr3tpassword" \ -e GOOGLE_DISCOVERY=... \ -e GITHUB_DISCOVERY=... \ -e K8S_DISCOVERY=... \ -e CONCOURSE_DISCOVERY=... \ -e CIRCLECI_DISCOVERY=... \ purplepanda python3 main.py -h ## -t is needed to see the output properly ## If you are using Neo4Desktop to connec to the DB use the domain host.docker.internal ## You might need to use the option '-v' to mount files with configurations ``` -------------------------------- ### List images in registry Source: https://github.com/carlospolop/purplepanda/blob/master/intel/google/TODO.md This snippet shows how to list images inside a registry using the Docker API library, and how to authenticate if public access is not possible. ```bash gcloud container images list --repository=eu.gcr.io/name_repo --log-htt ``` -------------------------------- ### Export GOOGLE_DISCOVERY and Run Module Source: https://github.com/carlospolop/purplepanda/blob/master/intel/google/README.md Commands to log in to GCP, set the GOOGLE_DISCOVERY environment variable with a base64 encoded configuration, and run the Purple Panda Google module. ```bash gcloud auth login gcloud auth application-default login export GOOGLE_DISCOVERY="Z29vZ2xlOgotIGZpbGVfcGF0aDogIiIKCi0gZmlsZV9wYXRoOiAiIgogIHNlcnZpY2VfYWNjb3VudF9pZDogInNvbWUtc2EtZW1haWxAc2lkZW50aWZpZXIuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iCg==" python3 main.py -p google --gcp-get-secret-values --gcp-get-kms -e ``` -------------------------------- ### Gcp - privesc outside gcp Source: https://github.com/carlospolop/purplepanda/blob/master/intel/google/HOW_TO_USE.md Get the privescs from GCP principal to external platforms. ```cypher MATCH(attacker:Gcp)-[r:PRIVESC]->(victim) WHERE not "Gcp" in labels(victim) RETURN attacker, r, victim ``` -------------------------------- ### Exporting Concourse Discovery Environment Variable and Running Module Source: https://github.com/carlospolop/purplepanda/blob/master/intel/concourse/README.md Demonstrates how to export the base64 encoded Concourse configuration to the CONCOURSE_DISCOVERY environment variable and then run the PurplePanda concourse module. ```bash export CONCOURSE_DISCOVERY="Y29uY291cnNlOgogIC0gdXJpOiBodHRwOi8vMTI3LjAuMC4xOjgwODAKICAgIHRva2VuOiA5ckVwRnREY0UxMjM0YUl1d1ZORW4ram9rMjRFOWpOaUFBQUFBQQo=" python3 main.py -p concourse -e ``` -------------------------------- ### Gh - repos $user can write Source: https://github.com/carlospolop/purplepanda/blob/master/intel/github/QUERIES.md Show all branches and repos where a user can merge code alone. ```cypher MATCH(u:GithubUser{name:$user})-[r:HAS_PERMS]->(repo:GithubRepo) WHERE r.admin = True OR r.maintain = True OR r.push = True RETURN u,r,repo ``` -------------------------------- ### Gcp - roles in $identity1 but not in $identity2 filtered by $filter Source: https://github.com/carlospolop/purplepanda/blob/master/intel/google/HOW_TO_USE.md Get roles that the first identity has and not the second filtered by a string. ```cypher MATCH (identity2:GcpPrincipal) WHERE identity2.email = identity2.email = $identity2 OR identity2.domain = $identity2 OR identity2.name = $identity2 OPTIONAL MATCH (identity2)-[h21:HAS_ROLE]->(r21) OPTIONAL MATCH (identity2)-[:MEMBER_OF*..]->()-[h22:HAS_ROLE]->(r22) OPTIONAL MATCH (role2:GcpRole) WITH role2, collect(h21) as h21_list, collect(h22) as h22_list WHERE toLower(role2.name) CONTAINS toLower($filter) AND (any(rel in h21_list WHERE role2.name IN rel.roles) OR any(rel in h22_list WHERE role2.name in rel.roles)) WITH DISTINCT collect(role2.name) AS roles_identity_2 MATCH (identity1:GcpPrincipal) WHERE identity1.email = $identity1 OR identity1.domain = $identity1 OR identity1.name = $identity1 OPTIONAL MATCH (identity1)-[h11:HAS_ROLE]->(r11) OPTIONAL MATCH (identity1)-[:MEMBER_OF*..]->()-[h12:HAS_ROLE]->(r12) OPTIONAL MATCH (role1:GcpRole) WITH role1, collect(h11) as h11_list, collect(h12) as h12_list, roles_identity_2 WHERE toLower(role1.name) CONTAINS toLower($filter) AND NOT role1.name IN roles_identity_2 AND (any(rel in h11_list WHERE role1.name IN rel.roles) OR any(rel in h12_list WHERE role1.name in rel.roles)) RETURN role1 ``` -------------------------------- ### Show all the env vars from github actions Source: https://github.com/carlospolop/purplepanda/blob/master/intel/github/HOW_TO_USE.md This query retrieves GithubAction nodes that have non-empty environment variables defined. ```cypher MATCH(action:GithubAction) WHERE action.env_vars <> [] RETURN action ``` -------------------------------- ### Gh - actions Source: https://github.com/carlospolop/purplepanda/blob/master/intel/github/QUERIES.md Show all the github actions. ```cypher MATCH(action:GithubAction) RETURN action ``` -------------------------------- ### Gh - repos $team can write Source: https://github.com/carlospolop/purplepanda/blob/master/intel/github/QUERIES.md Show all branches and repos where a team can write. ```cypher MATCH(t:GithubTeam{name:$team})-[r:HAS_PERMS]->(repo:GithubRepo) WHERE r.admin = True OR r.maintain = True OR r.push = True RETURN t,r,repo ``` -------------------------------- ### Show all the webhooks and their associated repos Source: https://github.com/carlospolop/purplepanda/blob/master/intel/github/HOW_TO_USE.md This query retrieves all GithubWebhooks and their associated GithubRepos, along with the relationship between them. ```cypher MATCH(webhook:GithubWebhook)<-[r:HAS_WEBHOOK]-(repo:GithubRepo) RETURN webhook,r,repo ``` -------------------------------- ### Gh - mirrored repos $user can merge Source: https://github.com/carlospolop/purplepanda/blob/master/intel/github/QUERIES.md Show all branches of mirrored repos where a user can merge on his own. ```cypher MATCH(u:GithubUser{name:$user})-[r1:CAN_MERGE]->(b:GithubBranch)<-[r2:HAS_BRANCH]-(repo:GithubRepo)<-[r3:IS_MIRROR]-(s) RETURN u,r1,b,r2,repo,r3,s ``` -------------------------------- ### Gcp - SAs with cross-project permissions Source: https://github.com/carlospolop/purplepanda/blob/master/intel/google/HOW_TO_USE.md Show all the SAs with a parent project that can access resources outside of their parent project. ```cypher MATCH (sa:GcpServiceAccount)-[rel:HAS_ROLE]->(res:Gcp) WHERE EXISTS((sa)-[:PART_OF]->(:GcpProject)) AND NOT EXISTS((sa)-[:PART_OF]->(res)) AND NOT EXISTS((sa)-[:PART_OF]->(:GcpProject)<-[:PART_OF]-(res)) RETURN sa, rel, res ``` -------------------------------- ### Randomize GCP Data Source: https://github.com/carlospolop/purplepanda/blob/master/core/other_info/Randomize some data/README.md Cypher queries to randomize various properties of GCP nodes, such as name, email, domain, displayName, uniqueId, source, targetTags, projectNumber, and description. It also includes examples for updating interesting_permissions, reasons for privilege escalation, and roles. ```cypher MATCH (g:Gcp) WHERE EXISTS(g.name) SET g.name = apoc.text.random(10, "A-Z0-9.$") RETURN g.name MATCH (g:Gcp) WHERE EXISTS(g.email) SET g.email = apoc.text.random(10, "A-Z0-9.$")+"@email.com" RETURN g.email MATCH (g:Gcp) WHERE EXISTS(g.domain) SET g.domain = apoc.text.random(10, "a-z0-9")+".com" RETURN g.domain MATCH (g:Gcp) WHERE EXISTS(g.displayName) SET g.displayName = apoc.text.random(10, "A-Z0-9.$") RETURN g.displayName MATCH (g:Gcp) WHERE EXISTS(g.uniqueId) SET g.uniqueId = apoc.text.random(10, "0-9") RETURN g.uniqueId MATCH (g:Gcp) WHERE EXISTS(g.source) SET g.source = apoc.text.random(10, "A-Z0-9") RETURN g.source MATCH (g:Gcp) WHERE EXISTS(g.targetTags) SET g.targetTags = "https://www.googleapis.com/compute/v1/projects/blahblahblah" RETURN g.targetTags MATCH (g:Gcp) WHERE EXISTS(g.projectNumber) SET g.projectNumber = apoc.text.random(10, "0-9") RETURN g.projectNumber MATCH (g:Gcp) WHERE EXISTS(g.description) SET g.description = apoc.text.random(10, "0-9") RETURN g.description MATCH (g:Gcp) WHERE EXISTS(g.interesting_permissions) SET g.interesting_permissions = apoc.text.regreplace(g.interesting_permissions[0], " over [^ ]+", " over projects/FAKE_PROJECT") RETURN g.interesting_permissions MATCH (g:Gcp)-[r:PRIVESC]->(b) SET r.reasons = apoc.text.regreplace(r.reasons[0], " to .*, " to ") RETURN r.reasons MATCH (g:Gcp)-[r:HAS_ROLE]->(b) SET r.roles = [apoc.text.regreplace(r.roles[0], "projects/[^/]+/", "")] RETURN r.roles ``` -------------------------------- ### Gh - mirrored repos $ppal can merge Source: https://github.com/carlospolop/purplepanda/blob/master/intel/github/HOW_TO_USE.md Show all branches of mirrored repos where a principal can merge on his own. ```cypher MATCH(u:GithubUser{name:$ppal})-[r1:CAN_MERGE]->(b:GithubBranch)<-[r2:HAS_BRANCH]-(repo:GithubRepo)<-[r3:IS_MIRROR]-(s) RETURN u,r1,b,r2,repo,r3,s ``` -------------------------------- ### K8s - ppals with privesc Source: https://github.com/carlospolop/purplepanda/blob/master/intel/k8s/HOW_TO_USE.md Show all the principals that can escalate privileges. ```cypher MATCH (ppal:K8sPrincipal)-[r:PRIVESC]->(u) RETURN ppal ``` -------------------------------- ### Concourse - params Source: https://github.com/carlospolop/purplepanda/blob/master/intel/concourse/HOW_TO_USE.md Show all the plans with some param or var declared. ```cypher MATCH(plan:ConcoursePlan) WHERE ((plan.params IS NOT NULL AND plan.params <> "null") OR (plan.runparams IS NOT NULL AND plan.runparams <> "null") OR (plan.vars IS NOT NULL AND plan.vars <> "null")) RETURN plan.name,plan.params,plan.runparams,plan.vars ``` -------------------------------- ### Gh - repos Source: https://github.com/carlospolop/purplepanda/blob/master/intel/github/QUERIES.md Show all the repos. ```Cypher MATCH(repo:GithubRepo) RETURN repo ``` -------------------------------- ### K8s - sas running in exposed pods Source: https://github.com/carlospolop/purplepanda/blob/master/intel/k8s/HOW_TO_USE.md Show all the service accounts running in a pod accesible externally. ```cypher OPTIONAL MATCH(svc1:K8sService)-[:HAS_IP]-(ip:PublicIP) OPTIONAL MATCH(svc2:K8sService)-[:HAS_DOMAIN]-(dom:PublicDomain) WHERE dom.is_external = True OPTIONAL MATCH(i:K8sIngress)-[:TO_SERVICE]-(svc3:K8sService) WITH *,collect(svc1)+collect(svc2)+collect(svc3) as svcs UNWIND svcs as svc OPTIONAL MATCH (svc)<-[:HAS_SERVICE]-(p:K8sPod)-[r:RUN_IN]-(sa:K8sServiceAccount) RETURN p,r,sa ``` -------------------------------- ### CircleCI Variables with Projects Source: https://github.com/carlospolop/purplepanda/blob/master/intel/circleci/HOW_TO_USE.md Query to show all projects with some variable defined. ```cypher MATCH(var:CircleCIVar)<-[r:HAS_VAR]-(project:CircleCIProject) RETURN var,r,project ``` -------------------------------- ### Show all the leaks Source: https://github.com/carlospolop/purplepanda/blob/master/intel/github/HOW_TO_USE.md This query retrieves all entries identified as GithubLeaks. ```cypher MATCH(l:GithubLeak) RETURN l ``` -------------------------------- ### K8s - $ppal gcp privesc with depth $depth Source: https://github.com/carlospolop/purplepanda/blob/master/intel/k8s/QUERIES.md Show the privilege escalation path of the indicated ppal. ```cypher MATCH r = (ppal:K8sPrincipal{name:$ppal})-[:PRIVESC*..$depth]->(ppal2:K8sPrincipal)-[:HAS_CLOUD_PERMS]-(gcp_sa:GcpServiceAccount) WITH *, relationships(r) as privescs RETURN ppal,privescs,ppal2,gcp_sa ``` -------------------------------- ### Gh - mirrored repos $team can merge Source: https://github.com/carlospolop/purplepanda/blob/master/intel/github/QUERIES.md Show all branches of mirrored repos where a team can merge on his own. ```cypher MATCH(t:GithubTeam{name:$user})-[r1:CAN_MERGE]->(b:GithubBranch)<-[r2:HAS_BRANCH]-(repo:GithubRepo)<-[r3:IS_MIRROR]-(s) RETURN t,r1,b,r2,repo,r3,s ``` -------------------------------- ### K8s - privileged containers Source: https://github.com/carlospolop/purplepanda/blob/master/intel/k8s/HOW_TO_USE.md Show all the potential privileged pods. ```cypher MATCH(p:K8sContainer) WHERE p.sc_privileged = True OR size(p.sc_capabilities_add) > 0 OR p.sc_runAsUser = 0 OR p.sc_runAsNonRoot <> True AND p.sc_runAsUser = "" OR p.sc_runAsGroup < 50 OR p.sc_allowPrivilegeEscalation = True RETURN p ``` -------------------------------- ### Gh - repos $user can dismiss Source: https://github.com/carlospolop/purplepanda/blob/master/intel/github/QUERIES.md Show all branches and repos where a user can dismiss. ```cypher MATCH(u:GithubUser{name:$user})-[r1:CAN_DISMISS]->(b:GithubBranch)<-[r2:HAS_BRANCH]-(repo:GithubRepo) RETURN u,r1,b,r2,repo ``` -------------------------------- ### Gh - $user admin repos Source: https://github.com/carlospolop/purplepanda/blob/master/intel/github/QUERIES.md Show all the repos where a user is admin. ```cypher MATCH(u:GithubUser{name:$user})-[r:HAS_PERMS{admin:True}]-(repo:GithubRepo) RETURN u,r,repo ``` -------------------------------- ### Gh - ppals can merge in mirrored repos Source: https://github.com/carlospolop/purplepanda/blob/master/intel/github/QUERIES.md Show all the principals with merge access to mirrored repos. ```cypher MATCH (s)-[r1:IS_MIRROR]->(repo:GithubRepo)-[r2:HAS_BRANCH]->(b:GithubBranch)<-[r3:CAN_MERGE]-(ppal:GithubPrincipal) RETURN s,r1,repo,r2,b,r3,ppal ``` -------------------------------- ### K8s - $ppal privesc with depth $depth Source: https://github.com/carlospolop/purplepanda/blob/master/intel/k8s/QUERIES.md Show the privilege escalation path of the indicated ppal. ```cypher MATCH r = (ppal:K8sPrincipal{name:$ppal})-[:PRIVESC*..]->(b) WITH *, relationships(r) as privescs RETURN ppal,privescs,b ``` -------------------------------- ### Gcp - SA with API keys Source: https://github.com/carlospolop/purplepanda/blob/master/intel/google/HOW_TO_USE.md Shows all Service Accounts (SAs) with API keys generated. ```cypher MATCH(sa:GcpServiceAccount)-[r:HAS_KEY]->(k) RETURN sa,r,k ``` -------------------------------- ### Gh - actions with injection points Source: https://github.com/carlospolop/purplepanda/blob/master/intel/github/HOW_TO_USE.md Show all the github actions with injection points. ```cypher MATCH(action:GithubAction) WHERE action.injection_points <> [] RETURN action ``` -------------------------------- ### Gcp - users with direct roles $verbose Source: https://github.com/carlospolop/purplepanda/blob/master/intel/google/HOW_TO_USE.md Show all the users with direct roles over resources. Use $verbose > 1 to show also "roles/owner", "roles/editor" and "roles/viewer" roles. ```cypher MATCH (u:GcpUserAccount)-[rel:HAS_ROLE]->(res) WHERE ($verbose <= 1 AND rel.roles <> ["roles/owner"] AND rel.roles <> ["roles/editor"] AND rel.roles <> ["roles/viewer"]) OR $verbose > 1 RETURN u, rel, res ``` -------------------------------- ### K8s - unauthenticated ppals with privesc Source: https://github.com/carlospolop/purplepanda/blob/master/intel/k8s/HOW_TO_USE.md Show default SAs that can escalate privileges. ```cypher MATCH (ppal:K8sPrincipal)-[r:PRIVESC]->(u) WHERE ppal.name CONTAINS "system:unauthenticated" OR ppal.name CONTAINS "system:anonymous" RETURN ppal,r,u ``` -------------------------------- ### Gh - repos $user can merge Source: https://github.com/carlospolop/purplepanda/blob/master/intel/github/HOW_TO_USE.md Show all branches and repos where a user can merge on his own. ```cypher MATCH(u:GithubUser{name:$user})-[r1:CAN_MERGE]->(s:GithubBranch)<-[r2:HAS_BRANCH]-(repo:GithubRepo) RETURN u,r1,s,r2,repo ``` -------------------------------- ### Gcp - principals with permission Source: https://github.com/carlospolop/purplepanda/blob/master/intel/google/HOW_TO_USE.md Show all the principals with a permission. ```cypher MATCH(r:GcpRole)-[c:CONTAINS]->(p:GcpPermission{name: $permission}) WITH r.name as role_names OPTIONAL MATCH(p:GcpPrincipal)-[h:HAS_ROLE]->(r) WHERE any(x IN h.roles WHERE x IN role_names) WITH p as principals, h as rol_rel, r as resources OPTIONAL MATCH x = (p2)-[:MEMBER_OF*..]->(principals) WITH *, relationships(x) as rels RETURN principals,rol_rel,resources,p2,rels ``` -------------------------------- ### Show all the secrets Source: https://github.com/carlospolop/purplepanda/blob/master/intel/github/HOW_TO_USE.md This query retrieves all entries identified as GithubSecrets. ```cypher MATCH(s:GithubSecret) RETURN s ``` -------------------------------- ### K8s - privileged exposed pods Source: https://github.com/carlospolop/purplepanda/blob/master/intel/k8s/QUERIES.md Show all the privileged pods accesible externally ```cypher OPTIONAL MATCH(svc1:K8sService)-[:HAS_IP]-(ip:PublicIP) OPTIONAL MATCH(svc2:K8sService)-[:HAS_DOMAIN]-(dom:PublicDomain) WHERE dom.is_external = True OPTIONAL MATCH(i:K8sIngress)-[r1:TO_SERVICE]-(svc3:K8sService) WITH *,collect(svc1)+collect(svc2)+collect(svc3) as svcs UNWIND svcs as svc MATCH (svc)<-[r2:HAS_SERVICE]-(p:K8sPod) WHERE p.sc_runAsUser = 0 OR p.sc_runAsNonRoot <> True AND p.sc_runAsUser = "" OR p.sc_runAsGroup < 50 OR any(grp in p.sc_supplemental_groups WHERE grp < 50) OR p.host_network OR p.host_pid OR p.host_ipc OR any(path IN p.host_path WHERE any( regex IN ["/", "/proc.*", "/sys.*", "/dev.*", "/var", "/var/", "/var/log.*", "/var/run.*", ".*docker.sock", ".*crio.sock", ".*/kubelet.*", ".*/pki.*", "/home/admin.*", "/etc.*", ".*/kubernetes.*", ".*/manifests.*", "/root.*"] WHERE regex =~ replace(path,"\\", "\\\\ "))) RETURN i,r1,svc,r2,p ``` -------------------------------- ### Show all the principals with merge access to mirrored repos Source: https://github.com/carlospolop/purplepanda/blob/master/intel/github/HOW_TO_USE.md This query finds all principals with merge access to branches within repositories that are mirrors of other resources. ```cypher MATCH (ppal)-[r3:CAN_MERGE]->(b:GithubBranch)<-[r2:HAS_BRANCH]-(repo:GithubRepo)<-[r1:IS_MIRROR]-(resource) RETURN ppal,r3,b,r2,repo,r1,resource ``` -------------------------------- ### Show all the github principals that can escalate privileges into other clouds Source: https://github.com/carlospolop/purplepanda/blob/master/intel/github/HOW_TO_USE.md This query identifies all GithubPrincipals that have a privilege escalation path to other cloud resources. ```cypher MATCH(ppal:GithubPrincipal)-[r:PRIVESC]->(res) RETURN ppal,r,res ``` -------------------------------- ### K8s - authenticated ppals with privesc Source: https://github.com/carlospolop/purplepanda/blob/master/intel/k8s/HOW_TO_USE.md Show default SAs that can escalate privileges. ```cypher MATCH (ppal:K8sPrincipal)-[r:PRIVESC]->(u) WHERE ppal.name CONTAINS "system:authenticated" RETURN ppal,r,u ``` -------------------------------- ### K8s - paths from $ppal1 to $ppal2 with depth $depth Source: https://github.com/carlospolop/purplepanda/blob/master/intel/k8s/QUERIES.md Show the path from the principal $ppal1 to the principal $ppal2. ```cypher MATCH r = (p1:K8sPrincipal{name:$ppal1})-[:PRIVESC*..$depth]-(p2:K8sPrincipal{name:$ppal2}) WITH *, relationships(r) as privescs RETURN p1,privescs,p2 ``` -------------------------------- ### Gcp - projects with permissions over others resources Source: https://github.com/carlospolop/purplepanda/blob/master/intel/google/HOW_TO_USE.md Shows GCP projects that have basic roles over other resources. ```cypher MATCH (p:GcpProject)-[hbr:HAS_BASIC_ROLES]->(r) WHERE NOT EXISTS((r)-[:PART_OF]->(p)) RETURN p,hbr,r ``` -------------------------------- ### Gh - $ppal privesc Source: https://github.com/carlospolop/purplepanda/blob/master/intel/github/HOW_TO_USE.md Show all the paths a github principal (user or team) can escalate privileges into other clouds. ```cypher MATCH(ppal:GithubPrincipal{name:$ppal})-[r:PRIVESC]->(res) RETURN ppal,r,res ``` -------------------------------- ### Gh - writers of $repo Source: https://github.com/carlospolop/purplepanda/blob/master/intel/github/QUERIES.md Show all principals with write permission over the indicated repo. ```cypher MATCH(repo:GithubRepo{name:$repo})<-[r:HAS_PERMS]-(u:Github) WHERE r.admin = True OR r.maintain = True OR r.push = True RETURN repo,r,u ``` -------------------------------- ### Gh - actions supporting pull_request_target Source: https://github.com/carlospolop/purplepanda/blob/master/intel/github/HOW_TO_USE.md Show all the github actions supporting pull_request_target. ```cypher MATCH(action:GithubAction) WHERE action.has_pull_request_target RETURN action ``` -------------------------------- ### K8s - root pods Source: https://github.com/carlospolop/purplepanda/blob/master/intel/k8s/HOW_TO_USE.md Show all the potential privileged pods. ```cypher MATCH(p:K8sPod) WHERE p.sc_runAsUser = 0 OR p.sc_runAsNonRoot <> True AND p.sc_runAsUser = "" OR p.sc_runAsGroup < 50 OR any(grp in p.sc_supplemental_groups WHERE grp < 50) OR p.host_network OR p.host_pid OR p.host_ipc OR any(path IN p.host_path WHERE any( regex IN ["/", "/proc.*", "/sys.*", "/dev.*", "/var/run.*", ".*docker.sock", ".*crio.sock", ".*/kubelet.*", ".*/pki.*", "/home/admin.*", "/etc.*", ".*/kubernetes.*", ".*/manifests.*", "/root.*"] WHERE regex =~ replace(path,"\\", "\\\\"))) RETURN p ``` -------------------------------- ### K8s - envars Source: https://github.com/carlospolop/purplepanda/blob/master/intel/k8s/QUERIES.md Show all the environment variables. ```cypher MATCH(e:K8sEnvVar) RETURN e ``` -------------------------------- ### Gh - repos $team can dismiss Source: https://github.com/carlospolop/purplepanda/blob/master/intel/github/QUERIES.md Show all branches and repos where a team can dismiss. ```cypher MATCH(t:GithubTeam{name:$team})-[r1:CAN_DISMISS]->(b:GithubBranch)<-[r2:HAS_BRANCH]-(repo:GithubRepo) RETURN t,r1,b,r2,repo ``` -------------------------------- ### K8s - pods exposed via svcs and ingresses Source: https://github.com/carlospolop/purplepanda/blob/master/intel/k8s/HOW_TO_USE.md Show all the services with an external IP or domain and all the ingresses. ```cypher OPTIONAL MATCH(svc1:K8sService)-[r1:HAS_IP]-(ip:PublicIP) OPTIONAL MATCH(svc2:K8sService)-[r2:HAS_DOMAIN]-(dom:PublicDomain) WHERE dom.is_external = True OPTIONAL MATCH(i:K8sIngress)-[r3:TO_SERVICE]-(svc3:K8sService) WITH *,collect(svc1)+collect(svc2)+collect(svc3) as svcs UNWIND svcs as svc OPTIONAL MATCH(p:K8sPod)-[r4:HAS_SERVICE]-(svc:K8sService) RETURN i,svc,p,r1,r2,r3,r4,ip,dom ``` -------------------------------- ### Gh - repos with missconfigured codeowners Source: https://github.com/carlospolop/purplepanda/blob/master/intel/github/QUERIES.md Show all the repos with unknown or empty codeowners. ```cypher MATCH(repo:GithubRepo) WHERE repo.unkown_codeowners <> [] OR repo.no_codeowners RETURN repo ``` -------------------------------- ### K8s - privileged exposed pods Source: https://github.com/carlospolop/purplepanda/blob/master/intel/k8s/HOW_TO_USE.md Show all the privileged pods accesible externally. ```cypher OPTIONAL MATCH(svc1:K8sService)-[:HAS_IP]-(ip:PublicIP) OPTIONAL MATCH(svc2:K8sService)-[:HAS_DOMAIN]-(dom:PublicDomain) WHERE dom.is_external = True OPTIONAL MATCH(i:K8sIngress)-[r1:TO_SERVICE]-(svc3:K8sService) WITH *,collect(svc1)+collect(svc2)+collect(svc3) as svcs UNWIND svcs as svc MATCH (svc)<-[r2:HAS_SERVICE]-(p:K8sPod) WHERE p.sc_runAsUser = 0 OR p.sc_runAsNonRoot <> True AND p.sc_runAsUser = "" OR p.sc_runAsGroup < 50 OR any(grp in p.sc_supplemental_groups WHERE grp < 50) OR p.host_network OR p.host_pid OR p.host_ipc OR any(path IN p.host_path WHERE any( regex IN ["/", "/proc.*", "/sys.*", "/dev.*", "/var", "/var/", "/var/log.*", "/var/run.*", ".*docker.sock", ".*crio.sock", ".*/kubelet.*", ".*/pki.*", "/home/admin.*", "/etc.*", ".*/kubernetes.*", ".*/manifests.*", "/root.*"] WHERE regex =~ replace(path,"\\", "\\\\"))) RETURN i,r1,svc,r2,p ``` -------------------------------- ### Concourse Configuration Structure Source: https://github.com/carlospolop/purplepanda/blob/master/intel/concourse/README.md Defines the structure for the Concourse configuration, specifying required attributes like token or username/password along with the URL. ```yaml concourse: - token: "string" url: "string" - username: "string" password: "string" url: "string" # (token || (username + password)) + url is mandatory ``` -------------------------------- ### K8s - $ppal iam privesc with depth $depth Source: https://github.com/carlospolop/purplepanda/blob/master/intel/k8s/QUERIES.md Show the privilege escalation path of the indicated ppal to a Pod with AWS IAM role. ```cypher MATCH r = (ppal:K8sPrincipal{name:$ppal})-[:PRIVESC*..$depth]->(p:K8sPod) WHERE p.iam_amazonaws_com_role <> "" WITH *, relationships(r) as privescs RETURN ppal,privescs,p ``` -------------------------------- ### Show all branches and repos where teams can merge code alone Source: https://github.com/carlospolop/purplepanda/blob/master/intel/github/HOW_TO_USE.md This query lists all GithubTeams that have the ability to merge code into branches, along with the associated branches and repositories. ```cypher MATCH(t:GithubTeam)-[r1:CAN_MERGE]-(b:GithubBranch)-[r2:HAS_BRANCH]-(repo:GithubRepo) RETURN t,r1,b,r2,repo ```