carlospolop / PurplePanda

Identify privilege escalation paths within and across different clouds
Other
658 stars 82 forks source link

Crash during init of GcpWorkloadIdentityPool #31

Closed msecrfe closed 1 week ago

msecrfe commented 2 weeks ago

Hi Carlos,

I ran PurplePanda on our GCP environment. During the disc_projects step, when PurplePanda tries to instantiate a GcpWorkloadIdentityPool object, it crashes with an IndexError (out of range) on the following line: name_part = name.split("/projects/")[1]

Full trace:

[16:02:41] disc_folders took 586s                                                                                                                        purplepanda.py:75
[16:14:42] ERROR    ERROR:intel.google.discovery.disc_projects:The role projects/<CENSORED>/roles/custom.deploy from                    gcp_disc_client.py:369
                    projects/<CENSORED> wasn't found                                                                                                          
Google disc_projects ━━━━╸━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.2% • 814 • 0:22:16 ⠧
Traceback (most recent call last):
  File "/opt/PurplePanda/main.py", line 216, in <module>
    main()
  File "/opt/PurplePanda/main.py", line 188, in main
    PurplePanda().start_discovery(functions)
  File "/opt/PurplePanda/core/utils/purplepanda.py", line 197, in start_discovery
    t.result()
  File "/usr/lib/python3.11/concurrent/futures/_base.py", line 449, in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/concurrent/futures/_base.py", line 401, in __get_result
    raise self._exception
  File "/usr/lib/python3.11/concurrent/futures/thread.py", line 58, in run
    result = self.fn(*self.args, **self.kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/PurplePanda/intel/google/purplepanda_google.py", line 80, in discover
    ).do_discovery()
      ^^^^^^^^^^^^^^
  File "/opt/PurplePanda/core/utils/discover_saas.py", line 17, in do_discovery
    self._call_f(f)
  File "/opt/PurplePanda/core/utils/discover_saas.py", line 44, in _call_f
    func()
  File "/opt/PurplePanda/core/utils/discover_saas.py", line 17, in do_discovery
    self._call_f(f)
  File "/opt/PurplePanda/core/utils/discover_saas.py", line 44, in _call_f
    func()
  File "/opt/PurplePanda/core/utils/purplepanda.py", line 56, in discover
    self._disc()
  File "/opt/PurplePanda/intel/google/discovery/disc_projects.py", line 29, in _disc
    self._disc_loop(projects, self._disc_projects, __name__.split(".")[-1])
  File "/opt/PurplePanda/core/utils/purplepanda.py", line 68, in _disc_loop
    func(item, **kwargs) # Here the keyworded arguments are being passed to the function
    ^^^^^^^^^^^^^^^^^^^^
  File "/opt/PurplePanda/intel/google/discovery/disc_projects.py", line 73, in _disc_projects
    self.get_iam_policy(p_obj, self.service.projects(), p_obj.name.split("/")[1])
  File "/opt/PurplePanda/intel/google/discovery/gcp_disc_client.py", line 312, in get_iam_policy
    m_obj: GcpWorkloadIdentityPool = GcpWorkloadIdentityPool(name=name).save()
                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/PurplePanda/intel/google/models/gcp_workload_identity_pool.py", line 19, in __init__
    name_part = name.split("/projects/")[1]
                ~~~~~~~~~~~~~~~~~~~~~~~~^^^
IndexError: list index out of range

I added a few print statements to see which name triggers this error. The name looks as follows: //iam.googleapis.com/locations/global/workforcePools/<CENSORED>-workforce-pool/subject/<CENSORED>@<CENSORED>.<CENSORED>

It seems that the constructor of GcpWorkloadIdentityPool cannot deal with a name that is formatted this way. It seems to expect one that has the string /projects/ in it.

carlospolop commented 2 weeks ago

Hi @rf-mgb! If you would mind opening a PR to fix it I'll gladly approve. If not you could share the details of why is it failing and I could check it at some point

msecrfe commented 2 weeks ago

I have a small patch that just prevents crashes and ignores malformed resources. This likely means that some resources will be ignored by PurplePanda but at least the scan manages to continue:

--- a/intel/google/discovery/gcp_disc_client.py
+++ b/intel/google/discovery/gcp_disc_client.py
@@ -308,9 +308,12 @@ class GcpDiscClient(PurplePanda):
                     m_objs = p_obj.get_basic_owners()

                 elif member.startswith("principal:") or member.startswith("principalSet:"):
-                    name = member.replace("principal:", "").replace("principalSet:", "")
-                    m_obj: GcpWorkloadIdentityPool = GcpWorkloadIdentityPool(name=name).save()
-                    m_objs = [m_obj]
+                    if "/projects/" in member:
+                        name = member.replace("principal:", "").replace("principalSet:", "")
+                        m_obj: GcpWorkloadIdentityPool = GcpWorkloadIdentityPool(name=name).save()
+                        m_objs = [m_obj]
+                    else:
+                        continue

                 elif member.startswith("deleted:"):
                     continue

Do you want me to open a PR for this small patch or do you want to quickly add this little diff yourself?

Just to reiterate, with this patch PurplePanda will likely not gather certain resources (like the example above, //iam.googleapis.com/locations/global/workforcePools/<CENSORED>-workforce-pool/subject/<CENSORED>@<CENSORED>.<CENSORED>).

carlospolop commented 1 week ago

I merged some code that hopefully will fix this, please, retry it and reopen this or any issue if you note that some issue is still not fixed!