@intentsolutionsio/penetration-tester 2.0.0 → 3.0.4
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/.claude-plugin/plugin.json +8 -3
- package/README.md +8 -0
- package/commands/pentest.md +5 -0
- package/package.json +8 -3
- package/skills/analyzing-tls-config/SKILL.md +221 -0
- package/skills/analyzing-tls-config/references/AUTHORIZATION.md +133 -0
- package/skills/analyzing-tls-config/references/PLAYBOOK.md +267 -0
- package/skills/analyzing-tls-config/references/THEORY.md +128 -0
- package/skills/analyzing-tls-config/scripts/analyze_tls.py +415 -0
- package/skills/auditing-cors-policy/SKILL.md +186 -0
- package/skills/auditing-cors-policy/references/PLAYBOOK.md +220 -0
- package/skills/auditing-cors-policy/references/THEORY.md +142 -0
- package/skills/auditing-cors-policy/scripts/audit_cors.py +350 -0
- package/skills/auditing-npm-dependencies/SKILL.md +254 -0
- package/skills/auditing-npm-dependencies/references/PLAYBOOK.md +175 -0
- package/skills/auditing-npm-dependencies/references/THEORY.md +122 -0
- package/skills/auditing-npm-dependencies/scripts/audit_npm.py +408 -0
- package/skills/auditing-python-dependencies/SKILL.md +251 -0
- package/skills/auditing-python-dependencies/references/PLAYBOOK.md +193 -0
- package/skills/auditing-python-dependencies/references/THEORY.md +122 -0
- package/skills/auditing-python-dependencies/scripts/audit_python.py +459 -0
- package/skills/checking-http-security-headers/SKILL.md +176 -0
- package/skills/checking-http-security-headers/references/PLAYBOOK.md +212 -0
- package/skills/checking-http-security-headers/references/THEORY.md +137 -0
- package/skills/checking-http-security-headers/scripts/check_headers.py +362 -0
- package/skills/checking-license-compliance/SKILL.md +225 -0
- package/skills/checking-license-compliance/references/PLAYBOOK.md +161 -0
- package/skills/checking-license-compliance/references/THEORY.md +152 -0
- package/skills/checking-license-compliance/scripts/check_licenses.py +461 -0
- package/skills/composing-vulnerability-report/SKILL.md +212 -0
- package/skills/composing-vulnerability-report/references/PLAYBOOK.md +180 -0
- package/skills/composing-vulnerability-report/references/THEORY.md +178 -0
- package/skills/composing-vulnerability-report/scripts/compose_report.py +396 -0
- package/skills/confirming-pentest-authorization/SKILL.md +247 -0
- package/skills/confirming-pentest-authorization/references/PLAYBOOK.md +189 -0
- package/skills/confirming-pentest-authorization/references/THEORY.md +167 -0
- package/skills/confirming-pentest-authorization/scripts/check_authorization.py +457 -0
- package/skills/defining-pentest-scope/SKILL.md +227 -0
- package/skills/defining-pentest-scope/references/PLAYBOOK.md +238 -0
- package/skills/defining-pentest-scope/references/THEORY.md +170 -0
- package/skills/defining-pentest-scope/scripts/define_scope.py +472 -0
- package/skills/detecting-command-injection-patterns/SKILL.md +144 -0
- package/skills/detecting-command-injection-patterns/references/PLAYBOOK.md +302 -0
- package/skills/detecting-command-injection-patterns/references/THEORY.md +206 -0
- package/skills/detecting-command-injection-patterns/scripts/scan_cmdi.py +290 -0
- package/skills/detecting-debug-endpoints/SKILL.md +207 -0
- package/skills/detecting-debug-endpoints/references/PLAYBOOK.md +402 -0
- package/skills/detecting-debug-endpoints/references/THEORY.md +218 -0
- package/skills/detecting-debug-endpoints/scripts/probe_debug.py +518 -0
- package/skills/detecting-directory-listing/SKILL.md +206 -0
- package/skills/detecting-directory-listing/references/PLAYBOOK.md +277 -0
- package/skills/detecting-directory-listing/references/THEORY.md +203 -0
- package/skills/detecting-directory-listing/scripts/probe_directory_listing.py +180 -0
- package/skills/detecting-eval-exec-usage/SKILL.md +128 -0
- package/skills/detecting-eval-exec-usage/references/PLAYBOOK.md +306 -0
- package/skills/detecting-eval-exec-usage/references/THEORY.md +159 -0
- package/skills/detecting-eval-exec-usage/scripts/scan_eval.py +223 -0
- package/skills/detecting-exposed-secrets-files/SKILL.md +179 -0
- package/skills/detecting-exposed-secrets-files/references/PLAYBOOK.md +274 -0
- package/skills/detecting-exposed-secrets-files/references/THEORY.md +174 -0
- package/skills/detecting-exposed-secrets-files/scripts/probe_secrets.py +207 -0
- package/skills/detecting-insecure-deserialization/SKILL.md +148 -0
- package/skills/detecting-insecure-deserialization/references/PLAYBOOK.md +333 -0
- package/skills/detecting-insecure-deserialization/references/THEORY.md +199 -0
- package/skills/detecting-insecure-deserialization/scripts/scan_deserialization.py +250 -0
- package/skills/detecting-sql-injection-patterns/SKILL.md +161 -0
- package/skills/detecting-sql-injection-patterns/references/PLAYBOOK.md +317 -0
- package/skills/detecting-sql-injection-patterns/references/THEORY.md +261 -0
- package/skills/detecting-sql-injection-patterns/scripts/scan_sqli.py +354 -0
- package/skills/detecting-ssl-cert-issues/SKILL.md +182 -0
- package/skills/detecting-ssl-cert-issues/references/PLAYBOOK.md +203 -0
- package/skills/detecting-ssl-cert-issues/references/THEORY.md +133 -0
- package/skills/detecting-ssl-cert-issues/scripts/check_cert_chain.py +481 -0
- package/skills/detecting-weak-cryptography/SKILL.md +147 -0
- package/skills/detecting-weak-cryptography/references/PLAYBOOK.md +466 -0
- package/skills/detecting-weak-cryptography/references/THEORY.md +194 -0
- package/skills/detecting-weak-cryptography/scripts/scan_weak_crypto.py +417 -0
- package/skills/fingerprinting-server-software/SKILL.md +191 -0
- package/skills/fingerprinting-server-software/references/PLAYBOOK.md +337 -0
- package/skills/fingerprinting-server-software/references/THEORY.md +183 -0
- package/skills/fingerprinting-server-software/scripts/fingerprint_server.py +347 -0
- package/skills/generating-executive-summary/SKILL.md +261 -0
- package/skills/generating-executive-summary/references/PLAYBOOK.md +201 -0
- package/skills/generating-executive-summary/references/THEORY.md +195 -0
- package/skills/generating-executive-summary/scripts/exec_summary.py +538 -0
- package/skills/mapping-findings-to-owasp-top10/SKILL.md +235 -0
- package/skills/mapping-findings-to-owasp-top10/references/PLAYBOOK.md +193 -0
- package/skills/mapping-findings-to-owasp-top10/references/THEORY.md +160 -0
- package/skills/mapping-findings-to-owasp-top10/scripts/map_owasp.py +540 -0
- package/skills/performing-penetration-testing/SKILL.md +282 -190
- package/skills/performing-penetration-testing/references/OWASP_TOP_10.md +22 -0
- package/skills/performing-penetration-testing/references/REMEDIATION_PLAYBOOK.md +46 -0
- package/skills/performing-penetration-testing/references/SECURITY_HEADERS.md +41 -0
- package/skills/performing-penetration-testing/scripts/code_security_scanner.py +144 -79
- package/skills/performing-penetration-testing/scripts/dependency_auditor.py +116 -93
- package/skills/performing-penetration-testing/scripts/security_scanner.py +574 -446
- package/skills/probing-dangerous-http-methods/SKILL.md +182 -0
- package/skills/probing-dangerous-http-methods/references/PLAYBOOK.md +234 -0
- package/skills/probing-dangerous-http-methods/references/THEORY.md +145 -0
- package/skills/probing-dangerous-http-methods/scripts/probe_methods.py +263 -0
- package/skills/recording-pentest-engagement/SKILL.md +253 -0
- package/skills/recording-pentest-engagement/references/PLAYBOOK.md +203 -0
- package/skills/recording-pentest-engagement/references/THEORY.md +195 -0
- package/skills/recording-pentest-engagement/scripts/record_engagement.py +461 -0
- package/skills/scanning-for-hardcoded-secrets/SKILL.md +215 -0
- package/skills/scanning-for-hardcoded-secrets/references/PLAYBOOK.md +325 -0
- package/skills/scanning-for-hardcoded-secrets/references/THEORY.md +175 -0
- package/skills/scanning-for-hardcoded-secrets/scripts/scan_secrets.py +395 -0
- package/skills/tracing-transitive-vulnerabilities/SKILL.md +235 -0
- package/skills/tracing-transitive-vulnerabilities/references/PLAYBOOK.md +233 -0
- package/skills/tracing-transitive-vulnerabilities/references/THEORY.md +138 -0
- package/skills/tracing-transitive-vulnerabilities/scripts/trace_vulns.py +484 -0
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
# Debug-Endpoint Remediation Playbook
|
|
2
|
+
|
|
3
|
+
## Spring Boot Actuator
|
|
4
|
+
|
|
5
|
+
### Narrow the exposed endpoints (preferred)
|
|
6
|
+
|
|
7
|
+
Default Spring Boot 3.x already only exposes `health` + `info`. If
|
|
8
|
+
something widened the surface, narrow it back:
|
|
9
|
+
|
|
10
|
+
`application-prod.yml`:
|
|
11
|
+
|
|
12
|
+
```yaml
|
|
13
|
+
management:
|
|
14
|
+
endpoints:
|
|
15
|
+
web:
|
|
16
|
+
exposure:
|
|
17
|
+
include: health, info
|
|
18
|
+
exclude: env, heapdump, jolokia, configprops, beans, mappings, threaddump, loggers
|
|
19
|
+
endpoint:
|
|
20
|
+
health:
|
|
21
|
+
show-details: never # or 'when-authorized'
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Require auth on Actuator endpoints
|
|
25
|
+
|
|
26
|
+
For internal-ops use of Actuator, gate behind authentication:
|
|
27
|
+
|
|
28
|
+
```yaml
|
|
29
|
+
management:
|
|
30
|
+
endpoints:
|
|
31
|
+
web:
|
|
32
|
+
base-path: /internal/actuator # not /actuator
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`SecurityConfig.java`:
|
|
36
|
+
|
|
37
|
+
```java
|
|
38
|
+
@Configuration
|
|
39
|
+
public class ActuatorSecurityConfig {
|
|
40
|
+
@Bean
|
|
41
|
+
SecurityFilterChain actuatorChain(HttpSecurity http) throws Exception {
|
|
42
|
+
http.securityMatcher(EndpointRequest.toAnyEndpoint())
|
|
43
|
+
.authorizeHttpRequests(a -> a.anyRequest().hasRole("ACTUATOR_ADMIN"))
|
|
44
|
+
.httpBasic(Customizer.withDefaults());
|
|
45
|
+
return http.build();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Or restrict by IP in a reverse proxy (nginx):
|
|
51
|
+
|
|
52
|
+
```nginx
|
|
53
|
+
location /actuator/ {
|
|
54
|
+
allow 10.0.0.0/8;
|
|
55
|
+
deny all;
|
|
56
|
+
proxy_pass http://springapp:8080;
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Disable Jolokia entirely (most aggressive)
|
|
61
|
+
|
|
62
|
+
`pom.xml`:
|
|
63
|
+
|
|
64
|
+
```xml
|
|
65
|
+
<!-- Remove the dependency -->
|
|
66
|
+
<dependency>
|
|
67
|
+
<groupId>org.jolokia</groupId>
|
|
68
|
+
<artifactId>jolokia-core</artifactId>
|
|
69
|
+
<!-- ... DELETE THIS BLOCK ... -->
|
|
70
|
+
</dependency>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Or via `application.yml`:
|
|
74
|
+
|
|
75
|
+
```yaml
|
|
76
|
+
management:
|
|
77
|
+
endpoint:
|
|
78
|
+
jolokia:
|
|
79
|
+
access: NONE
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Apache mod_status
|
|
83
|
+
|
|
84
|
+
### Disable globally (preferred for production)
|
|
85
|
+
|
|
86
|
+
`httpd.conf`:
|
|
87
|
+
|
|
88
|
+
```apache
|
|
89
|
+
# Comment out or remove the mod_status load
|
|
90
|
+
# LoadModule status_module modules/mod_status.so
|
|
91
|
+
|
|
92
|
+
# Remove the Location block
|
|
93
|
+
# <Location "/server-status">
|
|
94
|
+
# SetHandler server-status
|
|
95
|
+
# Require local
|
|
96
|
+
# </Location>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### If needed, lock down by IP
|
|
100
|
+
|
|
101
|
+
```apache
|
|
102
|
+
<Location "/server-status">
|
|
103
|
+
SetHandler server-status
|
|
104
|
+
Require ip 10.0.0.0/8
|
|
105
|
+
Require ip 192.168.0.0/16
|
|
106
|
+
# Require local # already includes 127.0.0.1 + IPv6 loopback
|
|
107
|
+
</Location>
|
|
108
|
+
|
|
109
|
+
<Location "/server-info">
|
|
110
|
+
SetHandler server-info
|
|
111
|
+
Require ip 10.0.0.0/8
|
|
112
|
+
</Location>
|
|
113
|
+
|
|
114
|
+
# Disable ExtendedStatus to stop URL disclosure in scoreboard
|
|
115
|
+
ExtendedStatus Off
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## nginx stub_status
|
|
119
|
+
|
|
120
|
+
```nginx
|
|
121
|
+
location = /nginx_status {
|
|
122
|
+
stub_status;
|
|
123
|
+
allow 10.0.0.0/8;
|
|
124
|
+
deny all;
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Or remove the block entirely if not needed.
|
|
129
|
+
|
|
130
|
+
## Prometheus /metrics
|
|
131
|
+
|
|
132
|
+
### Add bearer-token auth
|
|
133
|
+
|
|
134
|
+
`application.yml` (Spring Boot example):
|
|
135
|
+
|
|
136
|
+
```yaml
|
|
137
|
+
management:
|
|
138
|
+
endpoint:
|
|
139
|
+
prometheus:
|
|
140
|
+
enabled: true
|
|
141
|
+
metrics:
|
|
142
|
+
export:
|
|
143
|
+
prometheus:
|
|
144
|
+
enabled: true
|
|
145
|
+
|
|
146
|
+
# Apply security to the metrics path:
|
|
147
|
+
spring:
|
|
148
|
+
security:
|
|
149
|
+
user:
|
|
150
|
+
name: prometheus
|
|
151
|
+
password: ${PROMETHEUS_SCRAPE_PASSWORD}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Or restrict by IP at the reverse proxy
|
|
155
|
+
|
|
156
|
+
```nginx
|
|
157
|
+
location /metrics {
|
|
158
|
+
allow 10.0.10.5/32; # the prometheus server IP
|
|
159
|
+
deny all;
|
|
160
|
+
proxy_pass http://app:8080;
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Audit metric labels for credential leaks
|
|
165
|
+
|
|
166
|
+
Run a sanity scan over the actual metric output:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
curl -s http://localhost:8080/metrics | grep -iE 'token=|password=|key=|secret=' | head -20
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
If any labels contain credentials, fix the metric emission code to
|
|
173
|
+
sanitize / drop those labels before scraping.
|
|
174
|
+
|
|
175
|
+
## Elasticsearch _cat /_search
|
|
176
|
+
|
|
177
|
+
### Enable security (free in 7.x+ Basic license)
|
|
178
|
+
|
|
179
|
+
`elasticsearch.yml`:
|
|
180
|
+
|
|
181
|
+
```yaml
|
|
182
|
+
xpack.security.enabled: true
|
|
183
|
+
xpack.security.authc:
|
|
184
|
+
api_key.enabled: true
|
|
185
|
+
xpack.security.transport.ssl.enabled: true
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Then create users:
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
./bin/elasticsearch-setup-passwords interactive
|
|
192
|
+
# Set passwords for: elastic, apm_system, kibana_system, logstash_system, beats_system
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Update Kibana + Logstash + Beats to authenticate with their new
|
|
196
|
+
credentials.
|
|
197
|
+
|
|
198
|
+
### Restrict to internal network
|
|
199
|
+
|
|
200
|
+
```yaml
|
|
201
|
+
# elasticsearch.yml
|
|
202
|
+
network.host: 10.0.10.42 # internal IP only, NOT 0.0.0.0
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Or at the firewall: block 9200 from public internet entirely.
|
|
206
|
+
|
|
207
|
+
## Kibana / Grafana / Eureka / Consul
|
|
208
|
+
|
|
209
|
+
All four follow the same pattern: gate behind reverse-proxy auth or
|
|
210
|
+
move to internal-network-only.
|
|
211
|
+
|
|
212
|
+
### nginx reverse proxy with basic auth
|
|
213
|
+
|
|
214
|
+
```nginx
|
|
215
|
+
server {
|
|
216
|
+
listen 443 ssl;
|
|
217
|
+
server_name internal-grafana.example.com;
|
|
218
|
+
location / {
|
|
219
|
+
auth_basic "Restricted";
|
|
220
|
+
auth_basic_user_file /etc/nginx/.htpasswd;
|
|
221
|
+
proxy_pass http://grafana:3000;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Consul ACL bootstrap
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
consul acl bootstrap
|
|
230
|
+
# Returns SecretID; store securely
|
|
231
|
+
# Create policy that denies anonymous access:
|
|
232
|
+
consul acl policy create -name "anonymous-deny" -rules='node_prefix "" { policy = "deny" } service_prefix "" { policy = "deny" }'
|
|
233
|
+
consul acl token update -id 00000000-0000-0000-0000-000000000002 -policy-name anonymous-deny
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## phpMyAdmin
|
|
237
|
+
|
|
238
|
+
### Disable / remove entirely (preferred)
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
sudo apt-get remove phpmyadmin
|
|
242
|
+
sudo a2disconf phpmyadmin
|
|
243
|
+
sudo systemctl reload apache2
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
If you can't remove it because legacy ops use it:
|
|
247
|
+
|
|
248
|
+
### Restrict at the web server
|
|
249
|
+
|
|
250
|
+
```apache
|
|
251
|
+
<Directory /usr/share/phpmyadmin/>
|
|
252
|
+
Require ip 10.0.0.0/8
|
|
253
|
+
# Or use IP-based ACL specific to ops jump hosts
|
|
254
|
+
</Directory>
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Or move it to a non-default path:
|
|
258
|
+
|
|
259
|
+
```apache
|
|
260
|
+
Alias /admin-tools-only-internal-do-not-share/phpmyadmin /usr/share/phpmyadmin
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
(Security by obscurity is not security, but combined with IP
|
|
264
|
+
restriction it's defense-in-depth.)
|
|
265
|
+
|
|
266
|
+
## GraphQL Playground / GraphiQL
|
|
267
|
+
|
|
268
|
+
### Apollo Server 4.x
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
const server = new ApolloServer({
|
|
272
|
+
typeDefs,
|
|
273
|
+
resolvers,
|
|
274
|
+
// Playground is OFF by default in production (NODE_ENV=production)
|
|
275
|
+
introspection: process.env.NODE_ENV !== 'production',
|
|
276
|
+
});
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### graphql-yoga
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
import { createYoga } from 'graphql-yoga';
|
|
283
|
+
const yoga = createYoga({
|
|
284
|
+
schema,
|
|
285
|
+
graphiql: process.env.NODE_ENV !== 'production',
|
|
286
|
+
landingPage: false,
|
|
287
|
+
});
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Express + express-graphql (legacy)
|
|
291
|
+
|
|
292
|
+
```javascript
|
|
293
|
+
app.use('/graphql', graphqlHTTP({
|
|
294
|
+
schema,
|
|
295
|
+
graphiql: false, // disable in production
|
|
296
|
+
}));
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## Swagger UI / OpenAPI
|
|
300
|
+
|
|
301
|
+
### Spring (springdoc-openapi)
|
|
302
|
+
|
|
303
|
+
```yaml
|
|
304
|
+
springdoc:
|
|
305
|
+
swagger-ui:
|
|
306
|
+
enabled: false # disable in production
|
|
307
|
+
api-docs:
|
|
308
|
+
enabled: false
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Or via profile:
|
|
312
|
+
|
|
313
|
+
```yaml
|
|
314
|
+
# application-prod.yml
|
|
315
|
+
springdoc:
|
|
316
|
+
swagger-ui:
|
|
317
|
+
enabled: false
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### FastAPI
|
|
321
|
+
|
|
322
|
+
```python
|
|
323
|
+
app = FastAPI(
|
|
324
|
+
docs_url=None if PRODUCTION else "/docs",
|
|
325
|
+
redoc_url=None if PRODUCTION else "/redoc",
|
|
326
|
+
openapi_url=None if PRODUCTION else "/openapi.json",
|
|
327
|
+
)
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Express + swagger-ui-express
|
|
331
|
+
|
|
332
|
+
Wrap with auth or skip mounting in production:
|
|
333
|
+
|
|
334
|
+
```javascript
|
|
335
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
336
|
+
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(spec));
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Django
|
|
341
|
+
|
|
342
|
+
### Production settings
|
|
343
|
+
|
|
344
|
+
```python
|
|
345
|
+
# settings/production.py
|
|
346
|
+
DEBUG = False # CRITICAL — never True in prod
|
|
347
|
+
|
|
348
|
+
# Remove debug toolbar from INSTALLED_APPS and MIDDLEWARE for prod
|
|
349
|
+
if DEBUG:
|
|
350
|
+
INSTALLED_APPS.append('debug_toolbar')
|
|
351
|
+
MIDDLEWARE.append('debug_toolbar.middleware.DebugToolbarMiddleware')
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
## Go expvar / pprof
|
|
355
|
+
|
|
356
|
+
Don't import `_ "net/http/pprof"` in production. Pull the import into
|
|
357
|
+
a build tag:
|
|
358
|
+
|
|
359
|
+
```go
|
|
360
|
+
// +build !production
|
|
361
|
+
|
|
362
|
+
package main
|
|
363
|
+
|
|
364
|
+
import _ "net/http/pprof"
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Or expose pprof on a separate internal-only mux:
|
|
368
|
+
|
|
369
|
+
```go
|
|
370
|
+
go func() {
|
|
371
|
+
// Internal-network-only listener
|
|
372
|
+
log.Fatal(http.ListenAndServe("127.0.0.1:6060", nil))
|
|
373
|
+
}()
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
## CI integration
|
|
377
|
+
|
|
378
|
+
```yaml
|
|
379
|
+
- name: Debug-endpoint posture gate
|
|
380
|
+
run: |
|
|
381
|
+
python3 plugins/security/penetration-tester/skills/detecting-debug-endpoints/scripts/probe_debug.py \
|
|
382
|
+
"${{ secrets.STAGING_URL }}" \
|
|
383
|
+
--authorized \
|
|
384
|
+
--min-severity high \
|
|
385
|
+
--format json \
|
|
386
|
+
--output debug-endpoint-report.json
|
|
387
|
+
- run: |
|
|
388
|
+
if jq 'any(.severity == "critical" or .severity == "high")' debug-endpoint-report.json | grep -q true; then
|
|
389
|
+
echo "::error::Debug endpoint posture regression"
|
|
390
|
+
exit 1
|
|
391
|
+
fi
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
## Verification after remediation
|
|
395
|
+
|
|
396
|
+
```bash
|
|
397
|
+
python3 ${CLAUDE_PLUGIN_ROOT}/skills/detecting-debug-endpoints/scripts/probe_debug.py \
|
|
398
|
+
https://example.com --authorized --min-severity medium
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
Expected: exit 0, zero MEDIUM-or-higher findings. INFO findings on
|
|
402
|
+
deliberately-public Swagger / OpenAPI docs are operational choices.
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Debug-Endpoint Theory
|
|
2
|
+
|
|
3
|
+
## The recurring pattern
|
|
4
|
+
|
|
5
|
+
Modern application frameworks ship rich introspection by default
|
|
6
|
+
because developers need it to debug locally. The framework defaults
|
|
7
|
+
assume "this is running on my laptop in development mode." The
|
|
8
|
+
production deploy inherits the same defaults unless someone
|
|
9
|
+
explicitly disables them. The result is a class of finding that's
|
|
10
|
+
almost universal: any non-trivial Spring Boot / Apache / Prometheus /
|
|
11
|
+
ELK / Consul deployment exposes at least one of these endpoints
|
|
12
|
+
unless an operator went out of their way to disable them.
|
|
13
|
+
|
|
14
|
+
Each framework deserves its own short explanation.
|
|
15
|
+
|
|
16
|
+
## Spring Boot Actuator
|
|
17
|
+
|
|
18
|
+
Actuator is Spring Boot's introspection module. By default (in
|
|
19
|
+
Spring Boot 2.x with `management.endpoints.web.exposure.include=*`),
|
|
20
|
+
it exposes a JSON-shaped tree at `/actuator/` with endpoints like:
|
|
21
|
+
|
|
22
|
+
- `/actuator/env` — every Spring configuration property and every
|
|
23
|
+
environment variable, including database URLs, API keys read from
|
|
24
|
+
env, and JVM args.
|
|
25
|
+
- `/actuator/heapdump` — a live JVM heap dump (HPROF binary).
|
|
26
|
+
Contains all in-memory state: connection pool internals, recent
|
|
27
|
+
request data, cached credentials, JWT signing keys if they're
|
|
28
|
+
string-allocated.
|
|
29
|
+
- `/actuator/jolokia` — JMX-over-HTTP. With write permissions, this
|
|
30
|
+
is pre-auth RCE: an attacker can invoke arbitrary JMX MBeans
|
|
31
|
+
including `javax.management.Diagnostic.threadDump` and (with the
|
|
32
|
+
right MBean) execute arbitrary code.
|
|
33
|
+
- `/actuator/loggers` — POST changes log level for any package at
|
|
34
|
+
runtime. An attacker can silence logs of their own activity by
|
|
35
|
+
setting `org.springframework.security` to OFF.
|
|
36
|
+
- `/actuator/threaddump` — current thread state. Reveals which
|
|
37
|
+
business operations are in flight + stack traces with method
|
|
38
|
+
names (informs targeted attacks).
|
|
39
|
+
- `/actuator/health` — dependency state ("UP" / "DOWN" per
|
|
40
|
+
database / cache / external service). Maps the internal topology.
|
|
41
|
+
- `/actuator/mappings` — full route table including paths the public
|
|
42
|
+
app doesn't expose anywhere else.
|
|
43
|
+
|
|
44
|
+
Spring Boot 2.x default was exposure-everything; Spring Boot 3.x
|
|
45
|
+
default narrowed to `/health` + `/info` only. Many deployments still
|
|
46
|
+
have legacy 2.x-era `management.endpoints.web.exposure.include=*`
|
|
47
|
+
overrides in `application-prod.yml` that wasn't audited at the 3.x
|
|
48
|
+
upgrade.
|
|
49
|
+
|
|
50
|
+
The combined finding chain: `/actuator/env` exposes the database
|
|
51
|
+
connection string + creds. Connect to the database directly. End.
|
|
52
|
+
|
|
53
|
+
## Apache mod_status
|
|
54
|
+
|
|
55
|
+
`mod_status` exposes server runtime stats at `/server-status`:
|
|
56
|
+
|
|
57
|
+
- Apache version + OS
|
|
58
|
+
- Active worker count + idle workers
|
|
59
|
+
- **The URL of every active request** (the "Scoreboard" extended
|
|
60
|
+
view, enabled by `ExtendedStatus On`)
|
|
61
|
+
- The client IP of every active connection
|
|
62
|
+
|
|
63
|
+
The URL-disclosure axis is the operational risk: any session token,
|
|
64
|
+
OAuth code, or password-in-query-string that flows through Apache is
|
|
65
|
+
visible to anyone polling `/server-status`. A scraper hitting it
|
|
66
|
+
once a second can collect tokens at scale.
|
|
67
|
+
|
|
68
|
+
The default in older Apache versions was to allow `/server-status`
|
|
69
|
+
from localhost; many configs were edited to allow internal IP ranges,
|
|
70
|
+
then forgotten about as networks evolved.
|
|
71
|
+
|
|
72
|
+
## nginx stub_status
|
|
73
|
+
|
|
74
|
+
`/nginx_status` exposes connection counts and request counts. Less
|
|
75
|
+
sensitive than Apache's mod_status (doesn't show URLs) but still
|
|
76
|
+
operational telemetry the attacker shouldn't have. Common on
|
|
77
|
+
deployments that copy-pasted `stub_status on;` from a tutorial.
|
|
78
|
+
|
|
79
|
+
## Prometheus /metrics
|
|
80
|
+
|
|
81
|
+
Prometheus's `/metrics` endpoint is the data source for monitoring.
|
|
82
|
+
The exposed surface is the application's own metric series.
|
|
83
|
+
|
|
84
|
+
The non-obvious risk: metric LABELS often contain credentials by
|
|
85
|
+
accident. A "request count by endpoint" series might label requests
|
|
86
|
+
by full URL including query string. A "database connection state"
|
|
87
|
+
metric might label connections by full connection string. The
|
|
88
|
+
labels are designed for Grafana queries; they're not designed to be
|
|
89
|
+
adversarially scrutinized.
|
|
90
|
+
|
|
91
|
+
Common label-leak categories:
|
|
92
|
+
|
|
93
|
+
- `http_requests_total{path="/api/users?token=...", ...}`
|
|
94
|
+
- `database_connections_active{dsn="postgresql://user:pass@..."}`
|
|
95
|
+
- `external_api_calls_total{key="sk-..."}`
|
|
96
|
+
|
|
97
|
+
The fix is twofold: bring the endpoint behind auth (a static bearer
|
|
98
|
+
token is the conventional Prometheus pattern), AND audit the label
|
|
99
|
+
cardinality for accidental credential disclosure.
|
|
100
|
+
|
|
101
|
+
## Elasticsearch _cat /_search
|
|
102
|
+
|
|
103
|
+
Elasticsearch endpoints under `/_cat/` and `/_cluster/` expose
|
|
104
|
+
cluster state, indices, document counts, and node metadata. The
|
|
105
|
+
`/_search` endpoint serves arbitrary queries against any visible
|
|
106
|
+
index without authentication if the cluster is unconfigured.
|
|
107
|
+
|
|
108
|
+
The historical pattern: Elasticsearch shipped with no auth defaults
|
|
109
|
+
until version 7.x (the Basic license added security in late 2020).
|
|
110
|
+
A cluster set up before that date and never upgraded with security
|
|
111
|
+
enabled is fully open to anyone who can reach port 9200.
|
|
112
|
+
|
|
113
|
+
The Shodan-style internet scan of exposed Elasticsearch clusters is
|
|
114
|
+
a recurring "another data breach" story; the underlying issue is
|
|
115
|
+
always this misconfiguration.
|
|
116
|
+
|
|
117
|
+
## Kibana / Grafana / Eureka / Consul
|
|
118
|
+
|
|
119
|
+
Service-discovery and observability panels. Each can expose:
|
|
120
|
+
|
|
121
|
+
- Kibana: full search of Elasticsearch indices through the UI
|
|
122
|
+
- Grafana: dashboards that may include connection strings in
|
|
123
|
+
variable definitions
|
|
124
|
+
- Eureka: service registry with internal IPs + ports of every
|
|
125
|
+
microservice
|
|
126
|
+
- Consul: service catalog + agent checks with full topology
|
|
127
|
+
|
|
128
|
+
For Eureka and Consul specifically, the API is designed to be
|
|
129
|
+
read-mostly from inside the service mesh. Exposing it externally
|
|
130
|
+
hands an attacker the network map.
|
|
131
|
+
|
|
132
|
+
## phpMyAdmin
|
|
133
|
+
|
|
134
|
+
GUI for MySQL/MariaDB administration. Many shared-hosting
|
|
135
|
+
installations include it at `/phpmyadmin/` by default; legacy LAMP
|
|
136
|
+
stacks deploy it next to the app. If reachable AND unauthenticated
|
|
137
|
+
(or with default credentials), it's full DB access via web GUI.
|
|
138
|
+
|
|
139
|
+
Fingerprint: HTML body containing "phpMyAdmin" + a login form with
|
|
140
|
+
`name="pma_username"`.
|
|
141
|
+
|
|
142
|
+
## GraphQL Playground / GraphiQL
|
|
143
|
+
|
|
144
|
+
Interactive GraphQL UI for developers. Enabled by default in many
|
|
145
|
+
GraphQL server setups (Apollo Server, graphql-yoga). The risk has
|
|
146
|
+
two layers:
|
|
147
|
+
|
|
148
|
+
1. **Schema introspection** — the playground reveals the full
|
|
149
|
+
schema, which informs every subsequent query/mutation attack.
|
|
150
|
+
2. **Query execution** — if the GraphQL endpoint is publicly
|
|
151
|
+
reachable AND the playground is too, the attacker has both a
|
|
152
|
+
schema map AND an interactive interface to craft attacks against
|
|
153
|
+
it.
|
|
154
|
+
|
|
155
|
+
Best practice: disable introspection in production, disable the
|
|
156
|
+
playground in production. Apollo Server 4.x has both off by default
|
|
157
|
+
in `NODE_ENV=production`; older versions need explicit config.
|
|
158
|
+
|
|
159
|
+
## Swagger UI / OpenAPI
|
|
160
|
+
|
|
161
|
+
Same pattern as GraphQL: the schema (OpenAPI spec) tells an attacker
|
|
162
|
+
what endpoints exist, what parameters they accept, what response
|
|
163
|
+
shapes look like. Exposing `/swagger-ui` or `/openapi.json` on
|
|
164
|
+
production isn't a critical vulnerability but it's free recon.
|
|
165
|
+
|
|
166
|
+
Some defensive postures keep Swagger publicly reachable as a feature
|
|
167
|
+
(public API docs). That's a deliberate choice; the finding is
|
|
168
|
+
informational unless the API itself has auth issues.
|
|
169
|
+
|
|
170
|
+
## Django debug toolbar
|
|
171
|
+
|
|
172
|
+
If `DEBUG=True` shipped to production, the Django debug toolbar
|
|
173
|
+
exposes SQL queries, request env, settings, template context.
|
|
174
|
+
Effectively equivalent to `/actuator/env` for Django stacks.
|
|
175
|
+
|
|
176
|
+
## Go expvar / pprof
|
|
177
|
+
|
|
178
|
+
Built-in Go stdlib endpoints:
|
|
179
|
+
|
|
180
|
+
- `/debug/vars` — JSON dump of expvar package: cmdline, memstats,
|
|
181
|
+
custom-registered metrics
|
|
182
|
+
- `/debug/pprof/` — pprof profiles for CPU, heap, goroutines
|
|
183
|
+
|
|
184
|
+
The pprof endpoints can be triggered to start CPU profiling that
|
|
185
|
+
slows the server, AND can be used to download heap profiles that
|
|
186
|
+
contain live memory state (similar risk to Spring Boot heapdump).
|
|
187
|
+
|
|
188
|
+
If Go's `net/http/pprof` package is imported anywhere in the app
|
|
189
|
+
(it auto-registers handlers on the default mux), and the default
|
|
190
|
+
mux is exposed externally, pprof is reachable.
|
|
191
|
+
|
|
192
|
+
## Why fingerprint-checking matters
|
|
193
|
+
|
|
194
|
+
Same logic as `detecting-exposed-secrets-files` (#6). SPAs return
|
|
195
|
+
their `index.html` for any unknown route. Without fingerprinting,
|
|
196
|
+
every `/actuator/*` probe returns 200 against an SPA, all false
|
|
197
|
+
positives.
|
|
198
|
+
|
|
199
|
+
Each framework has a distinctive body fingerprint:
|
|
200
|
+
|
|
201
|
+
- Actuator: JSON `{"_links":...}` or `"propertySources"`
|
|
202
|
+
- mod_status: HTML containing "Apache Server Status"
|
|
203
|
+
- Prometheus: text starting with `# HELP` or `# TYPE`
|
|
204
|
+
- Elasticsearch: tabular text with status indicators
|
|
205
|
+
- phpMyAdmin: HTML containing "phpMyAdmin" + `pma_username`
|
|
206
|
+
- Jolokia: JSON `{"agent":"jolokia"}`
|
|
207
|
+
|
|
208
|
+
The fingerprint check is what separates a real finding from SPA
|
|
209
|
+
noise.
|
|
210
|
+
|
|
211
|
+
## Primary sources
|
|
212
|
+
|
|
213
|
+
- [OWASP WSTG-CONF-05 — Enumerate Infrastructure and Application Admin Interfaces](https://owasp.org/www-project-web-security-testing-guide/v42/4-Web_Application_Security_Testing/02-Configuration_and_Deployment_Management_Testing/05-Enumerate_Infrastructure_and_Application_Admin_Interfaces)
|
|
214
|
+
- [Spring Boot Actuator docs — Production-ready features](https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html)
|
|
215
|
+
- [Apache mod_status docs](https://httpd.apache.org/docs/2.4/mod/mod_status.html)
|
|
216
|
+
- [CWE-749 — Exposed Dangerous Method or Function](https://cwe.mitre.org/data/definitions/749.html)
|
|
217
|
+
- [CWE-285 — Improper Authorization](https://cwe.mitre.org/data/definitions/285.html)
|
|
218
|
+
- [Jolokia security advisory background](https://jolokia.org/reference/html/security.html)
|