@intentsolutionsio/penetration-tester 2.0.0
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 +19 -0
- package/LICENSE +21 -0
- package/README.md +160 -0
- package/commands/pentest.md +84 -0
- package/commands/scan-headers.md +43 -0
- package/package.json +40 -0
- package/skills/performing-penetration-testing/SKILL.md +266 -0
- package/skills/performing-penetration-testing/references/OWASP_TOP_10.md +284 -0
- package/skills/performing-penetration-testing/references/REMEDIATION_PLAYBOOK.md +452 -0
- package/skills/performing-penetration-testing/references/SECURITY_HEADERS.md +365 -0
- package/skills/performing-penetration-testing/scripts/code_security_scanner.py +780 -0
- package/skills/performing-penetration-testing/scripts/dependency_auditor.py +777 -0
- package/skills/performing-penetration-testing/scripts/requirements.txt +4 -0
- package/skills/performing-penetration-testing/scripts/security_scanner.py +1166 -0
- package/skills/performing-penetration-testing/scripts/setup_pentest_env.sh +199 -0
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
# Remediation Playbook
|
|
2
|
+
|
|
3
|
+
Copy-paste fix templates for common security vulnerabilities. Each entry includes
|
|
4
|
+
the vulnerable pattern, the fix, and a verification command.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## SQL Injection
|
|
9
|
+
|
|
10
|
+
**Vulnerable pattern (Python):**
|
|
11
|
+
```python
|
|
12
|
+
query = "SELECT * FROM users WHERE username = '" + username + "'"
|
|
13
|
+
cursor.execute(query)
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
**Fix (Python - parameterized query):**
|
|
17
|
+
```python
|
|
18
|
+
cursor.execute("SELECT * FROM users WHERE username = %s", (username,))
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Fix (Python - SQLAlchemy):**
|
|
22
|
+
```python
|
|
23
|
+
from sqlalchemy import text
|
|
24
|
+
result = session.execute(text("SELECT * FROM users WHERE username = :name"),
|
|
25
|
+
{"name": username})
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Vulnerable pattern (Node.js):**
|
|
29
|
+
```javascript
|
|
30
|
+
const query = `SELECT * FROM users WHERE username = '${username}'`;
|
|
31
|
+
db.query(query);
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Fix (Node.js - parameterized):**
|
|
35
|
+
```javascript
|
|
36
|
+
db.query("SELECT * FROM users WHERE username = $1", [username]);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Fix (Node.js - Knex.js):**
|
|
40
|
+
```javascript
|
|
41
|
+
knex("users").where("username", username).first();
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Verification:**
|
|
45
|
+
```bash
|
|
46
|
+
python3 code_security_scanner.py /path/to/code --tools regex
|
|
47
|
+
# Check that no SQL string concatenation findings remain
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Cross-Site Scripting (XSS)
|
|
53
|
+
|
|
54
|
+
**Vulnerable pattern (Python/Jinja2):**
|
|
55
|
+
```python
|
|
56
|
+
# Marking user input as safe bypasses auto-escaping
|
|
57
|
+
return Markup(f"<p>Hello {user_input}</p>")
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Fix (Python/Jinja2):**
|
|
61
|
+
```python
|
|
62
|
+
# Let the template engine auto-escape (default in Jinja2)
|
|
63
|
+
return render_template("greeting.html", name=user_input)
|
|
64
|
+
```
|
|
65
|
+
```html
|
|
66
|
+
<!-- greeting.html - auto-escaped by default -->
|
|
67
|
+
<p>Hello {{ name }}</p>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Vulnerable pattern (Node.js/Express):**
|
|
71
|
+
```javascript
|
|
72
|
+
res.send(`<p>Search results for: ${req.query.q}</p>`);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Fix (Node.js - use template engine with auto-escaping):**
|
|
76
|
+
```javascript
|
|
77
|
+
// With EJS (auto-escapes by default with <%= %>)
|
|
78
|
+
res.render("search", { query: req.query.q });
|
|
79
|
+
```
|
|
80
|
+
```html
|
|
81
|
+
<!-- search.ejs -->
|
|
82
|
+
<p>Search results for: <%= query %></p>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Fix (React - auto-escapes by default):**
|
|
86
|
+
```jsx
|
|
87
|
+
// React auto-escapes variables in JSX
|
|
88
|
+
return <p>Search results for: {query}</p>;
|
|
89
|
+
// NEVER use dangerouslySetInnerHTML with user input
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Additional protection - CSP header:**
|
|
93
|
+
```
|
|
94
|
+
Content-Security-Policy: default-src 'self'; script-src 'self'
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Verification:**
|
|
98
|
+
```bash
|
|
99
|
+
python3 security_scanner.py https://your-site.com --checks headers
|
|
100
|
+
# Verify CSP header is present and configured
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Hardcoded Secrets
|
|
106
|
+
|
|
107
|
+
**Vulnerable pattern:**
|
|
108
|
+
```python
|
|
109
|
+
API_KEY = "sk-abc123def456ghi789"
|
|
110
|
+
DATABASE_URL = "postgresql://admin:password123@db.example.com/prod"
|
|
111
|
+
AWS_SECRET_KEY = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Fix (Python - environment variables):**
|
|
115
|
+
```python
|
|
116
|
+
import os
|
|
117
|
+
|
|
118
|
+
API_KEY = os.environ["API_KEY"]
|
|
119
|
+
DATABASE_URL = os.environ["DATABASE_URL"]
|
|
120
|
+
AWS_SECRET_KEY = os.environ["AWS_SECRET_ACCESS_KEY"]
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Fix (Python - dotenv for development):**
|
|
124
|
+
```python
|
|
125
|
+
from dotenv import load_dotenv
|
|
126
|
+
import os
|
|
127
|
+
|
|
128
|
+
load_dotenv() # loads from .env file (NEVER commit .env)
|
|
129
|
+
API_KEY = os.environ["API_KEY"]
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Fix (Node.js):**
|
|
133
|
+
```javascript
|
|
134
|
+
// npm install dotenv
|
|
135
|
+
require("dotenv").config();
|
|
136
|
+
|
|
137
|
+
const apiKey = process.env.API_KEY;
|
|
138
|
+
const dbUrl = process.env.DATABASE_URL;
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Prevention - .gitignore:**
|
|
142
|
+
```gitignore
|
|
143
|
+
.env
|
|
144
|
+
.env.local
|
|
145
|
+
.env.production
|
|
146
|
+
*.pem
|
|
147
|
+
*.key
|
|
148
|
+
credentials.json
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Verification:**
|
|
152
|
+
```bash
|
|
153
|
+
python3 code_security_scanner.py /path/to/code --tools regex
|
|
154
|
+
# Check for hardcoded-secret findings
|
|
155
|
+
# Also verify .env is in .gitignore:
|
|
156
|
+
grep -q '.env' .gitignore && echo "OK" || echo "MISSING"
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Missing Security Headers
|
|
162
|
+
|
|
163
|
+
**Vulnerable:** No security headers configured (defaults to none).
|
|
164
|
+
|
|
165
|
+
**Fix (Express.js - Helmet):**
|
|
166
|
+
```javascript
|
|
167
|
+
const helmet = require("helmet");
|
|
168
|
+
app.use(helmet());
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
**Fix (Django):**
|
|
172
|
+
```python
|
|
173
|
+
# settings.py
|
|
174
|
+
MIDDLEWARE = [
|
|
175
|
+
"django.middleware.security.SecurityMiddleware",
|
|
176
|
+
# ... other middleware
|
|
177
|
+
]
|
|
178
|
+
|
|
179
|
+
SECURE_HSTS_SECONDS = 31536000
|
|
180
|
+
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
|
|
181
|
+
SECURE_CONTENT_TYPE_NOSNIFF = True
|
|
182
|
+
X_FRAME_OPTIONS = "DENY"
|
|
183
|
+
SECURE_REFERRER_POLICY = "strict-origin-when-cross-origin"
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
**Fix (Flask):**
|
|
187
|
+
```python
|
|
188
|
+
from flask_talisman import Talisman
|
|
189
|
+
|
|
190
|
+
Talisman(app, content_security_policy={
|
|
191
|
+
"default-src": "'self'",
|
|
192
|
+
"script-src": "'self'",
|
|
193
|
+
"frame-ancestors": "'none'",
|
|
194
|
+
})
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Verification:**
|
|
198
|
+
```bash
|
|
199
|
+
python3 security_scanner.py https://your-site.com --checks headers
|
|
200
|
+
# All headers should show as present
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Weak TLS Configuration
|
|
206
|
+
|
|
207
|
+
**Vulnerable:** TLS 1.0/1.1 enabled, weak cipher suites, expired certificates.
|
|
208
|
+
|
|
209
|
+
**Fix (Nginx):**
|
|
210
|
+
```nginx
|
|
211
|
+
ssl_protocols TLSv1.2 TLSv1.3;
|
|
212
|
+
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
|
|
213
|
+
ssl_prefer_server_ciphers off;
|
|
214
|
+
ssl_session_timeout 1d;
|
|
215
|
+
ssl_session_cache shared:SSL:10m;
|
|
216
|
+
ssl_session_tickets off;
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Fix (Apache):**
|
|
220
|
+
```apache
|
|
221
|
+
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
|
|
222
|
+
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
|
|
223
|
+
SSLHonorCipherOrder off
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Certificate renewal (Let's Encrypt):**
|
|
227
|
+
```bash
|
|
228
|
+
# Install certbot
|
|
229
|
+
sudo certbot renew --dry-run
|
|
230
|
+
|
|
231
|
+
# Auto-renewal cron
|
|
232
|
+
0 12 * * * /usr/bin/certbot renew --quiet
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**Verification:**
|
|
236
|
+
```bash
|
|
237
|
+
python3 security_scanner.py https://your-site.com --checks ssl
|
|
238
|
+
# Check certificate expiry and protocol version
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## Vulnerable Dependencies
|
|
244
|
+
|
|
245
|
+
**Vulnerable:** Outdated packages with known CVEs.
|
|
246
|
+
|
|
247
|
+
**Fix (npm):**
|
|
248
|
+
```bash
|
|
249
|
+
# View vulnerabilities
|
|
250
|
+
npm audit
|
|
251
|
+
|
|
252
|
+
# Auto-fix compatible updates
|
|
253
|
+
npm audit fix
|
|
254
|
+
|
|
255
|
+
# Force major version updates (review changes!)
|
|
256
|
+
npm audit fix --force
|
|
257
|
+
|
|
258
|
+
# Update specific package
|
|
259
|
+
npm install package-name@latest
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
**Fix (Python/pip):**
|
|
263
|
+
```bash
|
|
264
|
+
# Audit installed packages
|
|
265
|
+
pip-audit
|
|
266
|
+
|
|
267
|
+
# Update specific package
|
|
268
|
+
pip install --upgrade package-name
|
|
269
|
+
|
|
270
|
+
# Update all packages (use with caution)
|
|
271
|
+
pip list --outdated --format=json | python3 -c "
|
|
272
|
+
import json, sys
|
|
273
|
+
for pkg in json.load(sys.stdin):
|
|
274
|
+
print(pkg['name'])
|
|
275
|
+
" | xargs -n1 pip install --upgrade
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
**Fix (Lock file hygiene):**
|
|
279
|
+
```bash
|
|
280
|
+
# npm - regenerate lock file
|
|
281
|
+
rm package-lock.json && npm install
|
|
282
|
+
|
|
283
|
+
# pip - regenerate requirements
|
|
284
|
+
pip freeze > requirements.txt
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
**Verification:**
|
|
288
|
+
```bash
|
|
289
|
+
python3 dependency_auditor.py /path/to/project
|
|
290
|
+
# Should show no critical/high vulnerabilities
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## Command Injection
|
|
296
|
+
|
|
297
|
+
**Vulnerable pattern (Python):**
|
|
298
|
+
```python
|
|
299
|
+
import os
|
|
300
|
+
os.system("ping " + user_host)
|
|
301
|
+
|
|
302
|
+
import subprocess
|
|
303
|
+
subprocess.run(f"grep {pattern} {filename}", shell=True)
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
**Fix (Python):**
|
|
307
|
+
```python
|
|
308
|
+
import subprocess
|
|
309
|
+
import shlex
|
|
310
|
+
|
|
311
|
+
# Use list arguments (no shell interpretation)
|
|
312
|
+
subprocess.run(["ping", "-c", "1", validated_host], shell=False, timeout=10)
|
|
313
|
+
|
|
314
|
+
# If shell is truly needed, use shlex.quote
|
|
315
|
+
subprocess.run(f"grep {shlex.quote(pattern)} {shlex.quote(filename)}",
|
|
316
|
+
shell=True, timeout=10)
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
**Vulnerable pattern (Node.js):**
|
|
320
|
+
```javascript
|
|
321
|
+
const { exec } = require("child_process");
|
|
322
|
+
exec(`ls ${userInput}`);
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
**Fix (Node.js):**
|
|
326
|
+
```javascript
|
|
327
|
+
const { execFile } = require("child_process");
|
|
328
|
+
// execFile does not invoke a shell
|
|
329
|
+
execFile("ls", [validatedPath], (error, stdout) => {
|
|
330
|
+
// ...
|
|
331
|
+
});
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
**Verification:**
|
|
335
|
+
```bash
|
|
336
|
+
python3 code_security_scanner.py /path/to/code --tools bandit,regex
|
|
337
|
+
# Check for command-injection category findings
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## Insecure Deserialization
|
|
343
|
+
|
|
344
|
+
**Vulnerable pattern (Python):**
|
|
345
|
+
```python
|
|
346
|
+
import pickle
|
|
347
|
+
data = pickle.loads(request.data) # Arbitrary code execution
|
|
348
|
+
|
|
349
|
+
import yaml
|
|
350
|
+
config = yaml.load(user_input) # Arbitrary code execution
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
**Fix (Python):**
|
|
354
|
+
```python
|
|
355
|
+
# Use safe data formats
|
|
356
|
+
import json
|
|
357
|
+
data = json.loads(request.data)
|
|
358
|
+
|
|
359
|
+
# Use SafeLoader for YAML
|
|
360
|
+
import yaml
|
|
361
|
+
config = yaml.safe_load(user_input)
|
|
362
|
+
|
|
363
|
+
# If pickle is required (trusted data only), use hmac verification
|
|
364
|
+
import hmac
|
|
365
|
+
import hashlib
|
|
366
|
+
|
|
367
|
+
def verify_and_load(data, signature, secret_key):
|
|
368
|
+
expected = hmac.new(secret_key, data, hashlib.sha256).hexdigest()
|
|
369
|
+
if not hmac.compare_digest(signature, expected):
|
|
370
|
+
raise ValueError("Invalid signature")
|
|
371
|
+
return pickle.loads(data) # Only after verification
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
**Verification:**
|
|
375
|
+
```bash
|
|
376
|
+
python3 code_security_scanner.py /path/to/code --tools bandit,regex
|
|
377
|
+
# Check for insecure-deserialization category findings
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
## CORS Misconfiguration
|
|
383
|
+
|
|
384
|
+
**Vulnerable:**
|
|
385
|
+
```javascript
|
|
386
|
+
app.use(cors({ origin: "*", credentials: true }));
|
|
387
|
+
// Or reflecting any origin:
|
|
388
|
+
res.setHeader("Access-Control-Allow-Origin", req.headers.origin);
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
**Fix (Express.js):**
|
|
392
|
+
```javascript
|
|
393
|
+
const allowedOrigins = ["https://app.example.com", "https://admin.example.com"];
|
|
394
|
+
|
|
395
|
+
app.use(cors({
|
|
396
|
+
origin: (origin, callback) => {
|
|
397
|
+
if (!origin || allowedOrigins.includes(origin)) {
|
|
398
|
+
callback(null, true);
|
|
399
|
+
} else {
|
|
400
|
+
callback(new Error("Not allowed by CORS"));
|
|
401
|
+
}
|
|
402
|
+
},
|
|
403
|
+
credentials: true,
|
|
404
|
+
methods: ["GET", "POST", "PUT", "DELETE"],
|
|
405
|
+
allowedHeaders: ["Content-Type", "Authorization"],
|
|
406
|
+
}));
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
**Fix (Django):**
|
|
410
|
+
```python
|
|
411
|
+
# pip install django-cors-headers
|
|
412
|
+
CORS_ALLOWED_ORIGINS = [
|
|
413
|
+
"https://app.example.com",
|
|
414
|
+
"https://admin.example.com",
|
|
415
|
+
]
|
|
416
|
+
CORS_ALLOW_CREDENTIALS = True
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
**Verification:**
|
|
420
|
+
```bash
|
|
421
|
+
python3 security_scanner.py https://your-api.com --checks cors
|
|
422
|
+
# Should not show wildcard or reflected origin with credentials
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
## Checklist: Post-Remediation
|
|
428
|
+
|
|
429
|
+
After applying fixes, run the full scan suite to verify:
|
|
430
|
+
|
|
431
|
+
```bash
|
|
432
|
+
# 1. Check security headers
|
|
433
|
+
python3 security_scanner.py https://your-site.com --checks headers,ssl,cors
|
|
434
|
+
|
|
435
|
+
# 2. Check dependencies
|
|
436
|
+
python3 dependency_auditor.py /path/to/project --min-severity high
|
|
437
|
+
|
|
438
|
+
# 3. Check code
|
|
439
|
+
python3 code_security_scanner.py /path/to/code --severity high
|
|
440
|
+
|
|
441
|
+
# 4. Verify no regressions
|
|
442
|
+
# Run your application's test suite
|
|
443
|
+
npm test # or pytest, etc.
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
## Further Reading
|
|
449
|
+
|
|
450
|
+
- [OWASP Cheat Sheet Series](https://cheatsheetseries.owasp.org/)
|
|
451
|
+
- [CWE/SANS Top 25](https://cwe.mitre.org/top25/)
|
|
452
|
+
- [Mozilla Web Security Guidelines](https://infosec.mozilla.org/guidelines/web_security)
|