@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,466 @@
|
|
|
1
|
+
# Weak-Cryptography Remediation Playbook
|
|
2
|
+
|
|
3
|
+
Per-language migrations from the canonical weak primitives to the
|
|
4
|
+
modern replacements.
|
|
5
|
+
|
|
6
|
+
## Python — `hashlib` + `cryptography` + `secrets`
|
|
7
|
+
|
|
8
|
+
### MD5 / SHA-1 → SHA-256
|
|
9
|
+
|
|
10
|
+
```python
|
|
11
|
+
# Before
|
|
12
|
+
import hashlib
|
|
13
|
+
h = hashlib.md5(data).hexdigest()
|
|
14
|
+
|
|
15
|
+
# After
|
|
16
|
+
h = hashlib.sha256(data).hexdigest()
|
|
17
|
+
# Even better: BLAKE2b
|
|
18
|
+
h = hashlib.blake2b(data).hexdigest()
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### DES / 3DES / RC4 → AES-256-GCM
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
# Before (Cryptodome DES)
|
|
25
|
+
from Cryptodome.Cipher import DES
|
|
26
|
+
c = DES.new(key, DES.MODE_CBC, iv)
|
|
27
|
+
|
|
28
|
+
# After (cryptography library AES-GCM)
|
|
29
|
+
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
30
|
+
key = AESGCM.generate_key(bit_length=256)
|
|
31
|
+
aesgcm = AESGCM(key)
|
|
32
|
+
nonce = os.urandom(12)
|
|
33
|
+
ct = aesgcm.encrypt(nonce, plaintext, associated_data=None)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### ECB → GCM
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
# Before
|
|
40
|
+
cipher = AES.new(key, AES.MODE_ECB)
|
|
41
|
+
|
|
42
|
+
# After (AEAD, no separate HMAC needed)
|
|
43
|
+
aesgcm = AESGCM(key)
|
|
44
|
+
ct = aesgcm.encrypt(nonce, plaintext, None)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Password hashing
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
# Before (insecure)
|
|
51
|
+
import hashlib
|
|
52
|
+
hash_ = hashlib.sha256(password.encode()).hexdigest()
|
|
53
|
+
|
|
54
|
+
# After (argon2id, recommended)
|
|
55
|
+
from argon2 import PasswordHasher
|
|
56
|
+
ph = PasswordHasher()
|
|
57
|
+
hash_ = ph.hash(password) # includes salt + work factor in output
|
|
58
|
+
# Verify:
|
|
59
|
+
try:
|
|
60
|
+
ph.verify(hash_, password) # raises on mismatch
|
|
61
|
+
except VerifyMismatchError:
|
|
62
|
+
...
|
|
63
|
+
|
|
64
|
+
# Alternative: bcrypt (still acceptable)
|
|
65
|
+
import bcrypt
|
|
66
|
+
hash_ = bcrypt.hashpw(password.encode(), bcrypt.gensalt(12))
|
|
67
|
+
bcrypt.checkpw(password.encode(), hash_)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Crypto random
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
# Before
|
|
74
|
+
import random
|
|
75
|
+
token = ''.join(random.choices('abc...', k=32))
|
|
76
|
+
|
|
77
|
+
# After
|
|
78
|
+
import secrets
|
|
79
|
+
token = secrets.token_urlsafe(32)
|
|
80
|
+
# Or for raw bytes:
|
|
81
|
+
random_bytes = secrets.token_bytes(32)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### TLS verification
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
# Before
|
|
88
|
+
resp = requests.get(url, verify=False)
|
|
89
|
+
|
|
90
|
+
# After (default — verify=True is implicit)
|
|
91
|
+
resp = requests.get(url)
|
|
92
|
+
|
|
93
|
+
# If you need a custom CA:
|
|
94
|
+
resp = requests.get(url, verify="/path/to/ca-bundle.crt")
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Node.js — `crypto` module
|
|
98
|
+
|
|
99
|
+
### Hash migration
|
|
100
|
+
|
|
101
|
+
```javascript
|
|
102
|
+
// Before
|
|
103
|
+
const md5 = crypto.createHash('md5').update(data).digest('hex');
|
|
104
|
+
|
|
105
|
+
// After
|
|
106
|
+
const sha256 = crypto.createHash('sha256').update(data).digest('hex');
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Cipher migration
|
|
110
|
+
|
|
111
|
+
```javascript
|
|
112
|
+
// Before
|
|
113
|
+
const c = crypto.createCipheriv('des-ede3-cbc', key, iv);
|
|
114
|
+
|
|
115
|
+
// After (AES-256-GCM AEAD)
|
|
116
|
+
const iv = crypto.randomBytes(12);
|
|
117
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
|
|
118
|
+
const encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
119
|
+
const authTag = cipher.getAuthTag();
|
|
120
|
+
// Store [iv, encrypted, authTag] together
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Crypto random
|
|
124
|
+
|
|
125
|
+
```javascript
|
|
126
|
+
// Before
|
|
127
|
+
const sessionId = Math.random().toString(36); // NOT crypto-grade
|
|
128
|
+
|
|
129
|
+
// After
|
|
130
|
+
const sessionId = crypto.randomBytes(16).toString('hex');
|
|
131
|
+
// Or:
|
|
132
|
+
const sessionId = crypto.randomUUID();
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Password hashing
|
|
136
|
+
|
|
137
|
+
```javascript
|
|
138
|
+
// Before
|
|
139
|
+
const hash = crypto.createHash('sha256').update(password).digest('hex');
|
|
140
|
+
|
|
141
|
+
// After (bcrypt)
|
|
142
|
+
const bcrypt = require('bcrypt');
|
|
143
|
+
const hash = await bcrypt.hash(password, 12);
|
|
144
|
+
const ok = await bcrypt.compare(password, hash);
|
|
145
|
+
|
|
146
|
+
// After (argon2 — preferred)
|
|
147
|
+
const argon2 = require('argon2');
|
|
148
|
+
const hash = await argon2.hash(password, { type: argon2.argon2id });
|
|
149
|
+
const ok = await argon2.verify(hash, password);
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### TLS verification (Node)
|
|
153
|
+
|
|
154
|
+
```javascript
|
|
155
|
+
// Before
|
|
156
|
+
const https = require('https');
|
|
157
|
+
const agent = new https.Agent({ rejectUnauthorized: false });
|
|
158
|
+
fetch(url, { agent });
|
|
159
|
+
|
|
160
|
+
// After: just don't pass the agent
|
|
161
|
+
fetch(url); // verification on by default
|
|
162
|
+
|
|
163
|
+
// For custom CA:
|
|
164
|
+
const agent = new https.Agent({ ca: fs.readFileSync('/path/to/ca.crt') });
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Env var TLS bypass — REMOVE
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
# Before — anywhere in startup scripts
|
|
171
|
+
export NODE_TLS_REJECT_UNAUTHORIZED=0
|
|
172
|
+
|
|
173
|
+
# After: remove the line entirely
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Java — `java.security` + Bouncy Castle
|
|
177
|
+
|
|
178
|
+
### MessageDigest migration
|
|
179
|
+
|
|
180
|
+
```java
|
|
181
|
+
// Before
|
|
182
|
+
MessageDigest md = MessageDigest.getInstance("MD5");
|
|
183
|
+
|
|
184
|
+
// After
|
|
185
|
+
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Cipher migration
|
|
189
|
+
|
|
190
|
+
```java
|
|
191
|
+
// Before
|
|
192
|
+
Cipher c = Cipher.getInstance("DES/CBC/PKCS5Padding");
|
|
193
|
+
|
|
194
|
+
// After (AES-256-GCM)
|
|
195
|
+
SecureRandom random = new SecureRandom();
|
|
196
|
+
byte[] iv = new byte[12];
|
|
197
|
+
random.nextBytes(iv);
|
|
198
|
+
GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv);
|
|
199
|
+
Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
|
|
200
|
+
c.init(Cipher.ENCRYPT_MODE, key, gcmSpec);
|
|
201
|
+
byte[] ciphertext = c.doFinal(plaintext);
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### SecureRandom
|
|
205
|
+
|
|
206
|
+
```java
|
|
207
|
+
// Before
|
|
208
|
+
Random rand = new Random();
|
|
209
|
+
byte[] keyBytes = new byte[32];
|
|
210
|
+
rand.nextBytes(keyBytes);
|
|
211
|
+
|
|
212
|
+
// After
|
|
213
|
+
SecureRandom rand = new SecureRandom();
|
|
214
|
+
byte[] keyBytes = new byte[32];
|
|
215
|
+
rand.nextBytes(keyBytes);
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Password hashing (use Spring Security or library)
|
|
219
|
+
|
|
220
|
+
```java
|
|
221
|
+
// Spring Security 5.0+
|
|
222
|
+
PasswordEncoder encoder = new BCryptPasswordEncoder(12);
|
|
223
|
+
String hash = encoder.encode(rawPassword);
|
|
224
|
+
boolean ok = encoder.matches(rawPassword, hash);
|
|
225
|
+
|
|
226
|
+
// Argon2id via password4j
|
|
227
|
+
import com.password4j.Password;
|
|
228
|
+
String hash = Password.hash(rawPassword).withArgon2().getResult();
|
|
229
|
+
boolean ok = Password.check(rawPassword, hash).withArgon2();
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### TrustManager — never trust everything
|
|
233
|
+
|
|
234
|
+
```java
|
|
235
|
+
// Before (DANGEROUS)
|
|
236
|
+
TrustManager[] trustAll = new TrustManager[] {
|
|
237
|
+
new X509TrustManager() {
|
|
238
|
+
public void checkClientTrusted(X509Certificate[] chain, String authType) {}
|
|
239
|
+
public void checkServerTrusted(X509Certificate[] chain, String authType) {}
|
|
240
|
+
public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
// After: just use the default
|
|
245
|
+
SSLContext ctx = SSLContext.getInstance("TLS");
|
|
246
|
+
ctx.init(null, null, null); // null = JVM default trust store
|
|
247
|
+
|
|
248
|
+
// For custom CA: load it into a KeyStore and pass to TrustManagerFactory.
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Go — `crypto/aes` + `crypto/rand` + `golang.org/x/crypto`
|
|
252
|
+
|
|
253
|
+
### Hash migration
|
|
254
|
+
|
|
255
|
+
```go
|
|
256
|
+
// Before
|
|
257
|
+
import "crypto/md5"
|
|
258
|
+
h := md5.Sum(data)
|
|
259
|
+
|
|
260
|
+
// After
|
|
261
|
+
import "crypto/sha256"
|
|
262
|
+
h := sha256.Sum256(data)
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Cipher migration
|
|
266
|
+
|
|
267
|
+
```go
|
|
268
|
+
// Before
|
|
269
|
+
import "crypto/des"
|
|
270
|
+
block, _ := des.NewTripleDESCipher(key)
|
|
271
|
+
|
|
272
|
+
// After (AES-GCM)
|
|
273
|
+
import (
|
|
274
|
+
"crypto/aes"
|
|
275
|
+
"crypto/cipher"
|
|
276
|
+
"crypto/rand"
|
|
277
|
+
)
|
|
278
|
+
block, _ := aes.NewCipher(key) // key: 16, 24, or 32 bytes
|
|
279
|
+
gcm, _ := cipher.NewGCM(block)
|
|
280
|
+
nonce := make([]byte, gcm.NonceSize())
|
|
281
|
+
rand.Read(nonce)
|
|
282
|
+
ciphertext := gcm.Seal(nil, nonce, plaintext, nil)
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Crypto random
|
|
286
|
+
|
|
287
|
+
```go
|
|
288
|
+
// Before
|
|
289
|
+
import "math/rand"
|
|
290
|
+
b := make([]byte, 32)
|
|
291
|
+
rand.Read(b) // PREDICTABLE
|
|
292
|
+
|
|
293
|
+
// After
|
|
294
|
+
import "crypto/rand"
|
|
295
|
+
b := make([]byte, 32)
|
|
296
|
+
rand.Read(b) // crypto-grade
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Password hashing
|
|
300
|
+
|
|
301
|
+
```go
|
|
302
|
+
// bcrypt
|
|
303
|
+
import "golang.org/x/crypto/bcrypt"
|
|
304
|
+
hash, _ := bcrypt.GenerateFromPassword([]byte(password), 12)
|
|
305
|
+
err := bcrypt.CompareHashAndPassword(hash, []byte(password))
|
|
306
|
+
|
|
307
|
+
// argon2id
|
|
308
|
+
import "golang.org/x/crypto/argon2"
|
|
309
|
+
salt := make([]byte, 16)
|
|
310
|
+
rand.Read(salt)
|
|
311
|
+
hash := argon2.IDKey([]byte(password), salt, 1, 64*1024, 4, 32)
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### TLS verification
|
|
315
|
+
|
|
316
|
+
```go
|
|
317
|
+
// Before
|
|
318
|
+
tr := &http.Transport{
|
|
319
|
+
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// After: just don't set TLSClientConfig at all (defaults are safe)
|
|
323
|
+
client := &http.Client{}
|
|
324
|
+
// Or for a custom CA:
|
|
325
|
+
caCert, _ := os.ReadFile("ca.crt")
|
|
326
|
+
caPool := x509.NewCertPool()
|
|
327
|
+
caPool.AppendCertsFromPEM(caCert)
|
|
328
|
+
tr := &http.Transport{TLSClientConfig: &tls.Config{RootCAs: caPool}}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## PHP — `openssl_*` + `password_hash` + `random_bytes`
|
|
332
|
+
|
|
333
|
+
### Cipher migration
|
|
334
|
+
|
|
335
|
+
```php
|
|
336
|
+
// Before
|
|
337
|
+
$ct = openssl_encrypt($pt, 'des-ede-cbc', $key, 0, $iv);
|
|
338
|
+
|
|
339
|
+
// After
|
|
340
|
+
$iv = random_bytes(12);
|
|
341
|
+
$tag = '';
|
|
342
|
+
$ct = openssl_encrypt($pt, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag);
|
|
343
|
+
// Store [iv, ct, tag]
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Password hashing
|
|
347
|
+
|
|
348
|
+
```php
|
|
349
|
+
// Before
|
|
350
|
+
$hash = md5($password);
|
|
351
|
+
|
|
352
|
+
// After
|
|
353
|
+
$hash = password_hash($password, PASSWORD_BCRYPT);
|
|
354
|
+
// PHP 7.2+: argon2i; PHP 7.3+: argon2id
|
|
355
|
+
$hash = password_hash($password, PASSWORD_ARGON2ID);
|
|
356
|
+
$ok = password_verify($password, $hash);
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Crypto random
|
|
360
|
+
|
|
361
|
+
```php
|
|
362
|
+
// Before
|
|
363
|
+
$token = bin2hex(mt_rand(0, PHP_INT_MAX));
|
|
364
|
+
|
|
365
|
+
// After
|
|
366
|
+
$token = bin2hex(random_bytes(32));
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### cURL TLS verification — REMOVE the disable
|
|
370
|
+
|
|
371
|
+
```php
|
|
372
|
+
// Before (DANGEROUS)
|
|
373
|
+
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
|
374
|
+
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
|
|
375
|
+
|
|
376
|
+
// After: omit those calls. Defaults are verify=true.
|
|
377
|
+
// For custom CA:
|
|
378
|
+
curl_setopt($ch, CURLOPT_CAINFO, '/path/to/ca-bundle.crt');
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
## C# / .NET
|
|
382
|
+
|
|
383
|
+
### Hash migration
|
|
384
|
+
|
|
385
|
+
```csharp
|
|
386
|
+
// Before
|
|
387
|
+
using (var md5 = MD5.Create()) { var h = md5.ComputeHash(data); }
|
|
388
|
+
|
|
389
|
+
// After
|
|
390
|
+
using (var sha = SHA256.Create()) { var h = sha.ComputeHash(data); }
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### Cipher migration
|
|
394
|
+
|
|
395
|
+
```csharp
|
|
396
|
+
// Before — DESCryptoServiceProvider deprecated
|
|
397
|
+
using var des = new DESCryptoServiceProvider();
|
|
398
|
+
|
|
399
|
+
// After — AES-GCM
|
|
400
|
+
using var aes = new AesGcm(key);
|
|
401
|
+
byte[] nonce = RandomNumberGenerator.GetBytes(12);
|
|
402
|
+
byte[] tag = new byte[16];
|
|
403
|
+
byte[] ciphertext = new byte[plaintext.Length];
|
|
404
|
+
aes.Encrypt(nonce, plaintext, ciphertext, tag);
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### Crypto random
|
|
408
|
+
|
|
409
|
+
```csharp
|
|
410
|
+
// Before
|
|
411
|
+
var rand = new Random();
|
|
412
|
+
byte[] key = new byte[32];
|
|
413
|
+
rand.NextBytes(key); // NOT crypto-grade
|
|
414
|
+
|
|
415
|
+
// After
|
|
416
|
+
byte[] key = RandomNumberGenerator.GetBytes(32);
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### Password hashing
|
|
420
|
+
|
|
421
|
+
```csharp
|
|
422
|
+
// .NET: use BCrypt.Net or Konscious.Security.Cryptography for argon2id
|
|
423
|
+
using BCrypt.Net;
|
|
424
|
+
string hash = BCrypt.HashPassword(password, workFactor: 12);
|
|
425
|
+
bool ok = BCrypt.Verify(password, hash);
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
### ServerCertificateValidation — never always-true
|
|
429
|
+
|
|
430
|
+
```csharp
|
|
431
|
+
// Before (DANGEROUS)
|
|
432
|
+
ServicePointManager.ServerCertificateValidationCallback = (s, c, ch, e) => true;
|
|
433
|
+
|
|
434
|
+
// After: remove. Defaults verify.
|
|
435
|
+
// For custom CA:
|
|
436
|
+
HttpClientHandler handler = new HttpClientHandler();
|
|
437
|
+
handler.ServerCertificateCustomValidationCallback = (s, c, ch, e) => {
|
|
438
|
+
// Real validation — verify against your own CA, not blind-true
|
|
439
|
+
};
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
## CI integration
|
|
443
|
+
|
|
444
|
+
```yaml
|
|
445
|
+
- name: Weak-crypto scan
|
|
446
|
+
run: |
|
|
447
|
+
python3 plugins/security/penetration-tester/skills/detecting-weak-cryptography/scripts/scan_weak_crypto.py \
|
|
448
|
+
. --min-severity high --format json --output crypto-scan.json
|
|
449
|
+
- run: |
|
|
450
|
+
if jq 'length > 0' crypto-scan.json | grep -q true; then
|
|
451
|
+
echo "::error::Weak cryptography detected"
|
|
452
|
+
cat crypto-scan.json
|
|
453
|
+
exit 1
|
|
454
|
+
fi
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
## Verification after remediation
|
|
458
|
+
|
|
459
|
+
```bash
|
|
460
|
+
python3 ${CLAUDE_PLUGIN_ROOT}/skills/detecting-weak-cryptography/scripts/scan_weak_crypto.py \
|
|
461
|
+
/path/to/repo --min-severity medium
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
Expected: exit 0, zero MEDIUM-or-higher findings. LOW findings on
|
|
465
|
+
MD5/SHA-1 used in legitimate non-security contexts (content
|
|
466
|
+
addressing, dedup) are acceptable; document each.
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# Weak-Cryptography Theory
|
|
2
|
+
|
|
3
|
+
## Why "use the modern primitive" is the rule
|
|
4
|
+
|
|
5
|
+
Cryptographic primitives have a known break timeline. MD5 was
|
|
6
|
+
broken in 2004 (collisions in seconds on commodity hardware).
|
|
7
|
+
SHA-1 was broken in 2017 (shattered.io collision). DES has been
|
|
8
|
+
brute-forceable on dedicated hardware since the 1990s. ECB was
|
|
9
|
+
demonstrably insecure since the moment it was first analyzed.
|
|
10
|
+
|
|
11
|
+
Once a primitive is broken, two things follow:
|
|
12
|
+
|
|
13
|
+
1. **Attacks improve over time, never get worse.** Collision-finding
|
|
14
|
+
complexity for MD5 went from 2^64 (theoretical, 2004) to 2^16
|
|
15
|
+
(chosen-prefix collisions, 2010s) to "instant on a laptop"
|
|
16
|
+
(multi-collision tools today).
|
|
17
|
+
|
|
18
|
+
2. **Migration becomes free.** Every modern library has the
|
|
19
|
+
replacement primitive available. There's no engineering cost to
|
|
20
|
+
using SHA-256 instead of MD5; both are one library call.
|
|
21
|
+
|
|
22
|
+
The fix is universally available. The only reason these
|
|
23
|
+
primitives still appear in source is operator inertia.
|
|
24
|
+
|
|
25
|
+
## Per-primitive break status
|
|
26
|
+
|
|
27
|
+
### MD5
|
|
28
|
+
|
|
29
|
+
Collision attacks are trivial. Length-extension attacks predate
|
|
30
|
+
SHA-2. Practical break in any security context: signing, integrity
|
|
31
|
+
verification against adversaries, password hashing.
|
|
32
|
+
|
|
33
|
+
OK uses: content-addressable storage where collisions only need to
|
|
34
|
+
be unlikely against benign input (not adversarial), checksums in
|
|
35
|
+
non-security protocols, debugging hashes.
|
|
36
|
+
|
|
37
|
+
Use SHA-256 (still no practical attacks) or BLAKE2b / SHA-3 (newer
|
|
38
|
+
primitives) instead.
|
|
39
|
+
|
|
40
|
+
### SHA-1
|
|
41
|
+
|
|
42
|
+
Shattered.io (2017) demonstrated chosen-prefix collisions. SHA-1
|
|
43
|
+
should be considered broken for all security uses.
|
|
44
|
+
|
|
45
|
+
OK uses: same as MD5 — non-security checksums.
|
|
46
|
+
|
|
47
|
+
Migrate to SHA-256.
|
|
48
|
+
|
|
49
|
+
### DES / 3DES
|
|
50
|
+
|
|
51
|
+
DES is brute-forceable on commodity hardware (effective key size:
|
|
52
|
+
56 bits). 3DES is computationally feasible to break with
|
|
53
|
+
billion-block plaintext (Sweet32 attack against 64-bit block size).
|
|
54
|
+
NIST disallowed 3DES for new applications in 2017.
|
|
55
|
+
|
|
56
|
+
Migrate to AES-256-GCM. Same library, different constructor.
|
|
57
|
+
|
|
58
|
+
### RC4
|
|
59
|
+
|
|
60
|
+
Multiple statistical biases in keystream make plaintext recovery
|
|
61
|
+
practical given enough ciphertext. Deprecated by RFC 7465 (2015).
|
|
62
|
+
|
|
63
|
+
Migrate to AES-GCM or ChaCha20-Poly1305.
|
|
64
|
+
|
|
65
|
+
### AES-ECB
|
|
66
|
+
|
|
67
|
+
Electronic Codebook mode encrypts each block independently with
|
|
68
|
+
the same key. Identical plaintext blocks produce identical
|
|
69
|
+
ciphertext blocks. The structure of the plaintext leaks through
|
|
70
|
+
the ciphertext (the canonical "ECB penguin" image demonstration).
|
|
71
|
+
|
|
72
|
+
Migrate to AES-GCM (authenticated encryption, single primitive),
|
|
73
|
+
or AES-CBC + HMAC-SHA-256 (encrypt-then-MAC pattern). GCM is
|
|
74
|
+
preferred for new code.
|
|
75
|
+
|
|
76
|
+
### Hardcoded IVs
|
|
77
|
+
|
|
78
|
+
CBC mode and CTR mode both require a random IV per encryption.
|
|
79
|
+
Reusing an IV with the same key:
|
|
80
|
+
|
|
81
|
+
- CBC: leaks whether two plaintexts share a prefix
|
|
82
|
+
- CTR: leaks the XOR of the two plaintexts (catastrophic)
|
|
83
|
+
- GCM: REPEATED IV BREAKS THE CIPHER — attacker can recover the
|
|
84
|
+
authentication key
|
|
85
|
+
|
|
86
|
+
Generate a fresh random IV per encryption. Store the IV alongside
|
|
87
|
+
the ciphertext; it's not secret, but it must be unique-per-key.
|
|
88
|
+
|
|
89
|
+
### Custom XOR loops
|
|
90
|
+
|
|
91
|
+
The classic "I'll write my own crypto" trap. XOR-based "encryption"
|
|
92
|
+
with a repeating key is the Vigenère cipher with shorter alphabet;
|
|
93
|
+
breakable by frequency analysis with enough ciphertext.
|
|
94
|
+
|
|
95
|
+
There is no legitimate use case for hand-rolled crypto in 2026.
|
|
96
|
+
Use the language's library.
|
|
97
|
+
|
|
98
|
+
### Non-crypto random
|
|
99
|
+
|
|
100
|
+
`Math.random()` (JS), `random.random()` (Python without `secrets`
|
|
101
|
+
module), `java.util.Random` (Java), `math/rand` (Go), `rand()` /
|
|
102
|
+
`mt_rand()` (PHP), `new Random()` (.NET) all use deterministic
|
|
103
|
+
pseudo-random generators with predictable output given a small
|
|
104
|
+
amount of observed state.
|
|
105
|
+
|
|
106
|
+
Using these to generate:
|
|
107
|
+
|
|
108
|
+
- Session tokens → attacker predicts future tokens from one observed token
|
|
109
|
+
- Password reset tokens → attacker requests a reset on victim, predicts the token
|
|
110
|
+
- Encryption keys / IVs → attacker recreates the key from minimal observation
|
|
111
|
+
- CSRF tokens → attacker predicts a valid token
|
|
112
|
+
|
|
113
|
+
The fix: use the crypto-grade random API.
|
|
114
|
+
|
|
115
|
+
| Language | Crypto random |
|
|
116
|
+
|---|---|
|
|
117
|
+
| Python | `secrets.token_bytes(n)`, `secrets.token_urlsafe(n)` |
|
|
118
|
+
| Node | `crypto.randomBytes(n)`, `crypto.randomUUID()` |
|
|
119
|
+
| Java | `java.security.SecureRandom` |
|
|
120
|
+
| Go | `crypto/rand` (NOT `math/rand`) |
|
|
121
|
+
| PHP | `random_bytes(n)` |
|
|
122
|
+
| .NET | `RandomNumberGenerator.Create()` |
|
|
123
|
+
| Ruby | `SecureRandom.bytes(n)`, `SecureRandom.hex(n)` |
|
|
124
|
+
| Rust | `rand::thread_rng()` is NOT crypto-safe; use `rand::rngs::OsRng` or `getrandom` |
|
|
125
|
+
|
|
126
|
+
### Disabled certificate verification
|
|
127
|
+
|
|
128
|
+
`verify=False` (Python requests), `rejectUnauthorized: false`
|
|
129
|
+
(Node), `InsecureSkipVerify: true` (Go), custom-trust-everything
|
|
130
|
+
`X509TrustManager` (Java), `ServerCertificateValidationCallback`
|
|
131
|
+
returning true (.NET).
|
|
132
|
+
|
|
133
|
+
All of these turn HTTPS into HTTP — a network attacker can MITM
|
|
134
|
+
without the application noticing. There are roughly zero
|
|
135
|
+
legitimate production use cases for these flags.
|
|
136
|
+
|
|
137
|
+
The legitimate development use cases (testing against a self-
|
|
138
|
+
signed cert):
|
|
139
|
+
|
|
140
|
+
- Install the dev cert in the OS trust store (one-time setup)
|
|
141
|
+
- Use a tool like mkcert to generate a locally-trusted cert
|
|
142
|
+
- For ephemeral test environments, use a dedicated test CA
|
|
143
|
+
|
|
144
|
+
Production code with `verify=False` is a failure of the upstream
|
|
145
|
+
ops team to provision proper TLS, hardcoded as an application
|
|
146
|
+
choice. Fix the TLS setup, not the verification flag.
|
|
147
|
+
|
|
148
|
+
### Password hashing
|
|
149
|
+
|
|
150
|
+
`hashlib.sha256(password)` is a general-purpose hash function.
|
|
151
|
+
It's:
|
|
152
|
+
|
|
153
|
+
- Fast (designed to be fast — bad for password hashing)
|
|
154
|
+
- Unsalted by default (vulnerable to rainbow-table attacks)
|
|
155
|
+
- Single-iteration (no work factor — no way to slow down brute force)
|
|
156
|
+
|
|
157
|
+
Modern password hashing uses purpose-built KDFs:
|
|
158
|
+
|
|
159
|
+
- **bcrypt** — 1999, widely supported, decent
|
|
160
|
+
- **scrypt** — 2009, memory-hard (resistant to GPU/ASIC)
|
|
161
|
+
- **argon2id** — 2015, current PHC-recommended
|
|
162
|
+
|
|
163
|
+
All three have configurable work factors that tune to your hardware
|
|
164
|
+
and user-acceptable login latency. They incorporate salts
|
|
165
|
+
automatically; the salt is stored alongside the hash.
|
|
166
|
+
|
|
167
|
+
Modern recommendation: argon2id. Acceptable fallback: bcrypt with
|
|
168
|
+
cost factor ≥ 12.
|
|
169
|
+
|
|
170
|
+
## Why static-pattern scanning misses some weak crypto
|
|
171
|
+
|
|
172
|
+
This scanner detects API-level misuse. Some weaknesses require
|
|
173
|
+
deeper analysis:
|
|
174
|
+
|
|
175
|
+
- Custom protocol design errors (e.g., using a hash where a MAC
|
|
176
|
+
is required) require semantic analysis the scanner can't do
|
|
177
|
+
- Insufficient key sizes (RSA-1024, etc.) detected via library
|
|
178
|
+
API form (`RSA.generate(1024)`) but not always
|
|
179
|
+
- Padding-oracle vulnerabilities in CBC mode require behavioral
|
|
180
|
+
testing, not static analysis
|
|
181
|
+
|
|
182
|
+
Treat the scanner as the obvious-cases pass. A full crypto audit
|
|
183
|
+
needs domain expertise + tools like Semgrep + cryptography-
|
|
184
|
+
specific reviewers.
|
|
185
|
+
|
|
186
|
+
## Primary sources
|
|
187
|
+
|
|
188
|
+
- [CWE-327 Use of Broken Cryptographic Algorithm](https://cwe.mitre.org/data/definitions/327.html)
|
|
189
|
+
- [CWE-330 Use of Insufficiently Random Values](https://cwe.mitre.org/data/definitions/330.html)
|
|
190
|
+
- [CWE-916 Insufficient Computational Effort](https://cwe.mitre.org/data/definitions/916.html)
|
|
191
|
+
- [CWE-295 Improper Certificate Validation](https://cwe.mitre.org/data/definitions/295.html)
|
|
192
|
+
- [NIST SP 800-131A Transitions of Cryptographic Algorithms](https://csrc.nist.gov/publications/detail/sp/800-131a/rev-2/final)
|
|
193
|
+
- [OWASP Cryptographic Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html)
|
|
194
|
+
- [Password Hashing Competition (PHC) — argon2 winner](https://www.password-hashing.net/)
|