capat 0.1.0__tar.gz

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.
Files changed (64) hide show
  1. capat-0.1.0/.github/workflows/ci.yml +28 -0
  2. capat-0.1.0/.github/workflows/release.yml +22 -0
  3. capat-0.1.0/LICENSE +21 -0
  4. capat-0.1.0/PKG-INFO +464 -0
  5. capat-0.1.0/README.md +426 -0
  6. capat-0.1.0/docs/GUIDE.md +580 -0
  7. capat-0.1.0/docs/img/audit.png +0 -0
  8. capat-0.1.0/pyproject.toml +90 -0
  9. capat-0.1.0/src/capat/__init__.py +30 -0
  10. capat-0.1.0/src/capat/__main__.py +4 -0
  11. capat-0.1.0/src/capat/cli.py +813 -0
  12. capat-0.1.0/src/capat/collector.py +109 -0
  13. capat-0.1.0/src/capat/core/__init__.py +0 -0
  14. capat-0.1.0/src/capat/core/config.py +55 -0
  15. capat-0.1.0/src/capat/core/discovery.py +101 -0
  16. capat-0.1.0/src/capat/core/engine.py +85 -0
  17. capat-0.1.0/src/capat/core/inline.py +390 -0
  18. capat-0.1.0/src/capat/core/matching.py +241 -0
  19. capat-0.1.0/src/capat/core/profile.py +585 -0
  20. capat-0.1.0/src/capat/core/result.py +51 -0
  21. capat-0.1.0/src/capat/core/target.py +25 -0
  22. capat-0.1.0/src/capat/core/template.py +135 -0
  23. capat-0.1.0/src/capat/core/throttle.py +127 -0
  24. capat-0.1.0/src/capat/corpus.py +157 -0
  25. capat-0.1.0/src/capat/http/__init__.py +0 -0
  26. capat-0.1.0/src/capat/http/client.py +135 -0
  27. capat-0.1.0/src/capat/http/rate_limiter.py +43 -0
  28. capat-0.1.0/src/capat/modules/__init__.py +43 -0
  29. capat-0.1.0/src/capat/modules/base.py +38 -0
  30. capat-0.1.0/src/capat/modules/credential_audit.py +416 -0
  31. capat-0.1.0/src/capat/modules/gate_enforcement.py +478 -0
  32. capat-0.1.0/src/capat/modules/login_flow.py +245 -0
  33. capat-0.1.0/src/capat/modules/solve_rate.py +359 -0
  34. capat-0.1.0/src/capat/py.typed +0 -0
  35. capat-0.1.0/src/capat/reporting/__init__.py +0 -0
  36. capat-0.1.0/src/capat/reporting/banner.py +248 -0
  37. capat-0.1.0/src/capat/reporting/reporter.py +175 -0
  38. capat-0.1.0/src/capat/solvers/__init__.py +117 -0
  39. capat-0.1.0/src/capat/solvers/base.py +153 -0
  40. capat-0.1.0/src/capat/solvers/ddddocr_solver.py +137 -0
  41. capat-0.1.0/src/capat/solvers/easyocr_solver.py +76 -0
  42. capat-0.1.0/src/capat/solvers/ensemble.py +163 -0
  43. capat-0.1.0/src/capat/solvers/preprocess.py +94 -0
  44. capat-0.1.0/src/capat/solvers/tesseract_solver.py +64 -0
  45. capat-0.1.0/src/capat/trace.py +110 -0
  46. capat-0.1.0/src/capat/wizard.py +171 -0
  47. capat-0.1.0/tests/__init__.py +0 -0
  48. capat-0.1.0/tests/conftest.py +58 -0
  49. capat-0.1.0/tests/test_banner.py +222 -0
  50. capat-0.1.0/tests/test_captcha_source.py +240 -0
  51. capat-0.1.0/tests/test_collector.py +145 -0
  52. capat-0.1.0/tests/test_custom_model.py +121 -0
  53. capat-0.1.0/tests/test_engine_and_corpus.py +200 -0
  54. capat-0.1.0/tests/test_ensemble.py +110 -0
  55. capat-0.1.0/tests/test_gate_criteria.py +128 -0
  56. capat-0.1.0/tests/test_inline.py +328 -0
  57. capat-0.1.0/tests/test_modules.py +870 -0
  58. capat-0.1.0/tests/test_profile.py +78 -0
  59. capat-0.1.0/tests/test_request_shaping.py +302 -0
  60. capat-0.1.0/tests/test_rotating_captcha_url.py +272 -0
  61. capat-0.1.0/tests/test_run_integrity.py +172 -0
  62. capat-0.1.0/tests/test_solvers.py +101 -0
  63. capat-0.1.0/tests/test_throttling.py +138 -0
  64. capat-0.1.0/tests/test_wizard.py +136 -0
@@ -0,0 +1,28 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: actions/setup-python@v5
18
+ with:
19
+ python-version: ${{ matrix.python-version }}
20
+ cache: pip
21
+ - run: python -m pip install --upgrade pip
22
+ - run: pip install -e ".[dev]"
23
+ - run: ruff check .
24
+ - run: ruff format --check .
25
+ - run: mypy src
26
+ # The OCR extras are deliberately not installed: the suite must pass
27
+ # without them, which is what proves the engines stay optional.
28
+ - run: pytest -q
@@ -0,0 +1,22 @@
1
+ name: release
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ permissions:
8
+ contents: read
9
+ id-token: write # required for PyPI Trusted Publishing
10
+
11
+ jobs:
12
+ build-and-publish:
13
+ runs-on: ubuntu-latest
14
+ environment: pypi
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: actions/setup-python@v5
18
+ with:
19
+ python-version: "3.12"
20
+ - run: python -m pip install --upgrade build
21
+ - run: python -m build
22
+ - uses: pypa/gh-action-pypi-publish@release/v1
capat-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Your Name
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
capat-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,464 @@
1
+ Metadata-Version: 2.5
2
+ Name: capat
3
+ Version: 0.1.0
4
+ Summary: CAPtcha ATtacking tool: measure how well an image CAPTCHA resists automation. For authorized testing.
5
+ Project-URL: Homepage, https://github.com/goblensec/capat
6
+ Project-URL: Issues, https://github.com/goblensec/capat/issues
7
+ Author: goblensec
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: appsec,bot-detection,captcha,ocr,pentest,security
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Information Technology
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Topic :: Security
17
+ Requires-Python: >=3.10
18
+ Requires-Dist: httpx>=0.27
19
+ Requires-Dist: pillow>=10.0
20
+ Requires-Dist: rich>=13.0
21
+ Provides-Extra: all-solvers
22
+ Requires-Dist: ddddocr>=1.5; extra == 'all-solvers'
23
+ Requires-Dist: easyocr>=1.7; extra == 'all-solvers'
24
+ Requires-Dist: pytesseract>=0.3.10; extra == 'all-solvers'
25
+ Provides-Extra: ddddocr
26
+ Requires-Dist: ddddocr>=1.5; extra == 'ddddocr'
27
+ Provides-Extra: dev
28
+ Requires-Dist: mypy>=1.11; extra == 'dev'
29
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
30
+ Requires-Dist: pytest>=8; extra == 'dev'
31
+ Requires-Dist: respx>=0.21; extra == 'dev'
32
+ Requires-Dist: ruff>=0.6; extra == 'dev'
33
+ Provides-Extra: easyocr
34
+ Requires-Dist: easyocr>=1.7; extra == 'easyocr'
35
+ Provides-Extra: tesseract
36
+ Requires-Dist: pytesseract>=0.3.10; extra == 'tesseract'
37
+ Description-Content-Type: text/markdown
38
+
39
+ ```
40
+ __
41
+ _________ _____ ____ _/ /_
42
+ / ___/ __ `/ __ \/ __ `/ __/
43
+ / /__/ /_/ / /_/ / /_/ / /_
44
+ \___/\__,_/ .___/\__,_/\__/
45
+ /_/
46
+ ```
47
+ # capat - CAPtcha ATtacking Tool
48
+
49
+ [![CI](https://github.com/goblensec/capat/actions/workflows/ci.yml/badge.svg)](https://github.com/goblensec/capat/actions/workflows/ci.yml)
50
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/downloads/)
51
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
52
+
53
+ **Does your login page's CAPTCHA actually stop bots? capat tells you, with a number.**
54
+
55
+ An image CAPTCHA - the distorted letters you type to prove you are human - was
56
+ designed for a time when software could not read distorted text. Neural
57
+ networks ended that. A text-recognition model of about 10 MB, free to download,
58
+ reads most CAPTCHAs in milliseconds on an ordinary laptop CPU: no training, no
59
+ GPU, no service to pay. And where a stock model struggles, one trained on that
60
+ specific CAPTCHA will not - which is exactly what a motivated attacker does.
61
+
62
+ Point capat at a login page you have the right to test. It answers three
63
+ questions:
64
+
65
+ **1. Can a model read the CAPTCHA?**
66
+ It pulls real challenges from your site, runs them through a trained
67
+ recognition network, and reports the percentage it got right. At 70%, an
68
+ attacker spends about 1.4 attempts per guess - the CAPTCHA is barely slowing
69
+ anything down.
70
+
71
+ **2. Is the CAPTCHA even being checked?**
72
+ Many sites never enforce it properly. capat tries the simple ways around it:
73
+ leaving the field out entirely, sending it empty, and reusing an answer that
74
+ already worked. None of these need OCR at all.
75
+
76
+ **3. What is actually protecting the page?**
77
+ If the site starts refusing requests, capat stops and reports where that limit
78
+ began - because rate limiting, not the CAPTCHA, is usually the control doing
79
+ the real work.
80
+
81
+ Output is a table, JSON or Markdown.
82
+
83
+ **What makes it different:**
84
+
85
+ - **It never claims more than it proved.** If a check could not run, the report
86
+ says so, instead of showing a clean result. A tool that quietly reports "no
87
+ problems" for a test it skipped is worse than no tool at all.
88
+ - **It gives credit, not just blame.** A site with working rate limiting is
89
+ reported as having it - most scanners only tell you what is missing.
90
+ - **It is safe by default.** The solve-rate measurement uses an invented
91
+ username and a fresh random password each time, so it cannot lock anyone out
92
+ or log in by accident. It never tries real passwords unless you give it a
93
+ wordlist.
94
+ - **It runs your model, not just its own.** Every engine is swappable, and a
95
+ network you trained yourself loads as a file path: `-s model.onnx`.
96
+ - **It fits real login pages.** Plain HTML forms, JSON APIs, CSRF tokens,
97
+ CAPTCHA URLs that change on every load, and images embedded directly in the
98
+ page.
99
+
100
+ ![capat auditing a login page](https://raw.githubusercontent.com/goblensec/capat/main/docs/img/audit.png)
101
+
102
+ *A deliberately broken test application: three enforcement failures and a 58%
103
+ solve rate, with the reasoning printed under each finding.*
104
+
105
+ - [Installation](#installation)
106
+ - [Quick start](#quick-start)
107
+ - [Help](#usage)
108
+ - [The other commands](#the-other-commands)
109
+ - [Guided setup](#guided-setup)
110
+ - [Which engine?](#which-engine)
111
+ - [Exit codes](#exit-codes)
112
+ - [Every audit flag](#every-audit-flag)
113
+ - [Full guide](#full-guide)
114
+ - [Contributing](#contributing)
115
+ - [License](#license)
116
+
117
+ ## Installation
118
+
119
+ Python 3.10 or newer. Two ways, both fine.
120
+
121
+ ### Option 1 - from PyPI
122
+
123
+ The short one. Nothing to clone:
124
+
125
+ ```bash
126
+ pip install "capat[ddddocr]"
127
+ ```
128
+
129
+ The quotes matter - most shells eat the square brackets without them.
130
+
131
+ **The part in brackets is the recognition engine, and it is a choice.** capat
132
+ ships no engine of its own: the tool is the measurement harness, and the
133
+ network that reads the image is installed separately so you can pick one, swap
134
+ it, or bring your own. Four are supported, three of them neural:
135
+
136
+ | Install | Engine | Why you would pick it |
137
+ | --- | --- | --- |
138
+ | `pip install "capat[ddddocr]"` | **ddddocr** - CRNN+CTC, ONNX runtime | The default. Reads the whole image as one sequence, so deliberate glyph overlap costs it little. ~18 ms a solve, installs in seconds, no torch. **Start here.** |
139
+ | `pip install "capat[easyocr]"` | **EasyOCR** - CRAFT detector + CRNN, PyTorch | A general scene-text model. Large - it pulls in torch - and its detection stage splits touching glyphs, which is why it scores worse here than its reputation suggests. |
140
+ | `pip install "capat[tesseract]"` | **Tesseract** - LSTM | Classic, and already present on many machines. Note the `tesseract` binary must also be installed and on `PATH`; pip only installs the Python wrapper. |
141
+ | `pip install "capat[all-solvers]"` | all three | Required for `-s ensemble` and `-s ensemble-fast`, which run several engines and select by mean confidence. Highest accuracy, slowest. |
142
+ | `pip install capat` | none | Core only. The enforcement checks, rate-limit detection and reporting all still work; the solve rate reports that it has no solver rather than printing a misleading 0%. |
143
+
144
+ A model you trained yourself needs no extra install of its own: `-s model.onnx`
145
+ loads a custom CRNN+CTC network through the ddddocr runtime, so
146
+ `"capat[ddddocr]"` covers it. See [Which engine?](#which-engine) for measured
147
+ accuracy and speed, and why the default is what it is.
148
+
149
+ ### Option 2 - from GitHub
150
+
151
+ Use this if you want the source to change something:
152
+
153
+ ```bash
154
+ git clone https://github.com/goblensec/capat && cd capat
155
+ pip install -e ".[ddddocr]"
156
+ ```
157
+
158
+ `-e` installs in editable mode, so edits to the source take effect without
159
+ reinstalling. The same extras apply: `".[all-solvers]"`, or `-e .` for core
160
+ only.
161
+
162
+ To install from the repository without cloning it:
163
+
164
+ ```bash
165
+ pip install "capat[ddddocr] @ git+https://github.com/goblensec/capat.git"
166
+ ```
167
+
168
+ ### Either way
169
+
170
+ The recognition engine is an optional extra, never bundled: pip fetches
171
+ `ddddocr` from PyPI itself, and capat ships no model of its own. That keeps the
172
+ core install small and means you can swap in `easyocr`, `tesseract`, or your own
173
+ `.onnx` model instead.
174
+
175
+ Check the install with `capat --version`. If `capat` is not on your `PATH`,
176
+ `python -m capat` works identically everywhere below.
177
+
178
+ ## Quick start
179
+
180
+ Point it at a login page you are authorized to test. The two phrases are the
181
+ measurement: telling "wrong CAPTCHA" apart from "wrong password" is what makes
182
+ a solve rate mean anything.
183
+
184
+ ```bash
185
+ capat audit -u https://app.example.com/login -cf "invalid captcha" -af "invalid username or password"
186
+ ```
187
+
188
+ Leave `-c` out and the login page is fetched and the CAPTCHA image found on it.
189
+
190
+ If you do not know the flags yet, `capat setup` asks for the facts, writes a
191
+ profile and runs it.
192
+
193
+ ```bash
194
+ capat setup
195
+ ```
196
+
197
+ Save a working configuration with `-P app.json` and re-run it with `-p
198
+ app.json`; any flag still overrides the file.
199
+
200
+
201
+ ## Usage
202
+
203
+ `capat audit` is the command; everything below is how to drive it. The full
204
+ flag reference is at the end of this section, folded so it stays out of the way.
205
+
206
+ ### The other commands
207
+
208
+ `audit` is the one with the flags. The rest are small enough to read from their
209
+ own `-h`:
210
+
211
+ | Command | What it does | Network |
212
+ |---|---|---|
213
+ | `capat setup` | Asks for the URLs, the field names and the two phrases, writes a profile, prints the command it built, then runs it | yes |
214
+ | `capat collect -c URL -n 25 -o corpus` | Downloads CAPTCHA images for a corpus. Never submits a login | yes |
215
+ | `capat bench corpus -s ddddocr -l 5` | Scores a solver against a labelled corpus | no |
216
+ | `capat solve img.png --save-processed out.png` | Solves one image and shows the preprocessing in force. How a recipe gets tuned | no |
217
+
218
+ ```bash
219
+ capat setup -h capat collect -h capat bench -h capat solve -h
220
+ ```
221
+
222
+ Every command prints the banner and its configuration before it starts;
223
+ `--no-banner` turns that off for CI.
224
+
225
+ ### Guided setup
226
+
227
+ If you do not know the flags yet, answer questions instead - `capat setup` is
228
+ the wizard, and after it has written a profile you never need it again.
229
+
230
+ ```bash
231
+ capat setup
232
+ ```
233
+
234
+ ### Which engine?
235
+
236
+ | `--solver` | Exact | Per solve | Solves/hour |
237
+ |---|---|---|---|
238
+ | `easyocr` | 1/6 (17%) | 1497 ms | ~400 |
239
+ | `ddddocr` | 4/6 (67%) | **18 ms** | **~131,000** |
240
+ | `ensemble-fast` | 4/6 (67%) | 143 ms | ~16,800 |
241
+ | `ensemble` | **5/6 (83%)** | 1936 ms | ~1,550 |
242
+
243
+ Measured on six CAPTCHAs from one production login - indicative, not a
244
+ published rate. `ddddocr` is the default: it reads the whole image as one
245
+ sequence, so deliberate glyph overlap costs it far less than a general
246
+ scene-text engine, and it installs in seconds without torch.
247
+
248
+ `-s model.onnx` loads a custom CRNN+CTC model instead of a registered engine.
249
+ A companion module for training one - graded synthetic corpora, exporting ONNX
250
+ that plugs straight in - is in development and lands in a later release.
251
+
252
+ ### Exit codes
253
+
254
+ | Code | Meaning |
255
+ |---|---|
256
+ | `0` | Ran clean, nothing at MEDIUM or above |
257
+ | `1` | Findings at MEDIUM or above |
258
+ | `2` | Usage error, or the target could not be reached |
259
+ | `130` | Interrupted |
260
+
261
+ ### Every audit flag
262
+
263
+ <details>
264
+ <summary><code>capat audit -h</code> &mdash; the complete flag reference</summary>
265
+
266
+ ```
267
+ usage: capat audit [-h] [-p PROFILE] [-n N] [--skip-solve-rate] [-M MODULE] [-d] [-r N] [-t N]
268
+ [-T SEC] [-x URL] [-k] [-A UA] [-w WORDLIST] [-U WORDLIST]
269
+ [--order {spray,brute}] [--max-attempts N] [--captcha-retries N]
270
+ [--lockout-threshold N] [--ignore-lockout] [-f FMT] [-o FILE] [--no-banner]
271
+ [-m LEVEL] [-s NAME|PATH] [--charset CHARS] [--charset-file PATH] [-l N]
272
+ [--scale N] [--threshold N] [--median N] [--min-saturation N] [--dilate N]
273
+ [--invert] [--case-sensitive] [-u URL] [-c URL] [-fu URL] [-X M] [-e ENC]
274
+ [-H 'Name: Value'] [-b 'N=V; N2=V2'] [-F name=value] [-E name=src:key]
275
+ [--username NAME] [--username-field NAME] [--password-field NAME]
276
+ [--csrf-field NAME] [-cr KIND] [-cm M] [-ci PATH] [-cd PATH] [-cn NAME]
277
+ [-cv NAME] [-cH 'Name: Value'] [-cf MATCH] [-af MATCH] [-ok MATCH] [-ou FRAGMENT]
278
+ [-P PATH]
279
+
280
+ capat - evidence that image CAPTCHAs are not a bot control.
281
+ Use only against systems you are authorized to test.
282
+
283
+ options:
284
+ -h, --help show this help message and exit
285
+
286
+ GENERAL OPTIONS:
287
+ -p, --profile PROFILE JSON file describing the login form (optional; -u alone also
288
+ works)
289
+ -n, --samples N CAPTCHAs to measure (default 25)
290
+ --skip-solve-rate only run the enforcement checks (no OCR)
291
+ -M, --only MODULE run only these checks. Repeatable. See MODULES
292
+ -d, --debug print every request, including live credentials. Keep it on a
293
+ terminal
294
+
295
+ HTTP OPTIONS:
296
+ -r, --rps N max requests/second (default 2)
297
+ -t, --threads N parallel checks in flight (default 5)
298
+ -T, --timeout SEC request timeout (default 15s)
299
+ -x, --proxy URL proxy, e.g. http://127.0.0.1:8080 for Burp/ZAP
300
+ -k, --insecure disable TLS verification (warns)
301
+ -A, --user-agent UA override the User-Agent header
302
+
303
+ CREDENTIAL TEST OPTIONS (only run when -w is given):
304
+ -w, --passwords WORDLIST password list. Giving one ALSO submits real logins - off unless
305
+ given
306
+ -U, --users WORDLIST file of real accounts to guess against, one per line. Overrides
307
+ --username. Without either, -w has nothing to run against
308
+ --order {spray,brute} spray: every identity per password, safest (default). brute: one
309
+ at a time
310
+ --max-attempts N cap credential-test attempts (default: the whole wordlist)
311
+ --captcha-retries N retries when a CAPTCHA was misread, so no password goes untested
312
+ (default 3)
313
+ --lockout-threshold N wrong-password budget PER ACCOUNT, not for the whole run (default
314
+ 4). CAPTCHA misreads do not count against it
315
+ --ignore-lockout keep guessing at an account after it spends its budget. OFF by
316
+ default: capat drops that account and carries on with the rest.
317
+ Turning it on CAN LOCK A REAL PERSON OUT
318
+
319
+ OUTPUT OPTIONS:
320
+ -f, --format FMT report format: table (default), json, markdown
321
+ -o, --output FILE write the report to a file
322
+ --no-banner skip the start-of-run banner and configuration
323
+ -m, --min-severity LEVEL hide findings below this level, e.g. -m medium (default info)
324
+
325
+ SOLVER OPTIONS (tuning; save with -P and reuse):
326
+ -s, --solver NAME|PATH engine: ddddocr, easyocr, ensemble, ensemble-fast,
327
+ tesseract (default ddddocr), or the path to a custom .onnx model
328
+ --charset CHARS characters the CAPTCHA can contain
329
+ --charset-file PATH labels for a custom -s model.onnx (default: the model path with
330
+ .json)
331
+ -l, --length N how many characters the answer has. Used by easyocr, tesseract and
332
+ ensemble only - ddddocr and -s model.onnx read the image as one
333
+ sequence and ignore it, so train the length in instead
334
+ --scale N upscale factor (default 3.0)
335
+ --threshold N binarization cut-off 0-255, -1 keeps grayscale (default 140)
336
+ --median N median denoise kernel, 0 disables (default 3)
337
+ --min-saturation N drop pixels below this saturation 0-255, killing grey noise (try
338
+ 60)
339
+ --dilate N thicken strokes, odd kernel (default 0, try 3)
340
+ --invert invert after thresholding
341
+ --case-sensitive compare answers case-sensitively
342
+
343
+ TARGET (instead of, or on top of, --profile):
344
+ -u, --url URL login URL the form posts to
345
+ -c, --captcha-url URL CAPTCHA endpoint. Discovered if omitted; 'auto' re-reads it every
346
+ attempt; 'base64' takes it from a data: URI in the page;
347
+ {{placeholders}} are filled by -E
348
+ -fu, --form-url URL page holding the form, if it differs from -u
349
+ -X, --method M login request method (default POST)
350
+ -e, --encoding ENC login body encoding: form (default), json, multipart
351
+ -H, --header 'Name: Value' header on every request. Repeatable
352
+ -b, --cookie 'N=V; N2=V2' cookie data, for copy-as-curl. Same as -H 'Cookie: ...'
353
+ -F, --field name=value extra login form field. Repeatable
354
+ -E, --extract name=src:key pull a per-request value out of the page first, e.g.
355
+ 'token=hidden:_token'. Repeatable. See EXTRACT SYNTAX
356
+ --username NAME the ONE real account -w guesses against. Ignored when -U is given;
357
+ the other checks never use it, they invent a throwaway name
358
+ --username-field NAME form field for the username (default username)
359
+ --password-field NAME form field for the password (default password)
360
+ --csrf-field NAME hidden field to carry over, e.g. _token
361
+ -P, --save-profile PATH write the assembled profile as JSON and carry on, so a run that
362
+ worked repeats
363
+
364
+ CAPTCHA ENDPOINT:
365
+ -cr, --captcha-response KIND 'image' for a raw image body (default), 'json' for a base64
366
+ payload
367
+ -cm, --captcha-method M method for the CAPTCHA request (default GET)
368
+ -ci, --captcha-image-key PATH dotted path to the base64 image
369
+ -cd, --captcha-id-key PATH dotted path to the challenge id
370
+ -cn, --captcha-id-field NAME field the challenge id is submitted back in
371
+ -cv, --captcha-field NAME form field for the answer (default captcha)
372
+ -cH, --captcha-header 'Name: Value'
373
+ header on the CAPTCHA request only. Repeatable
374
+
375
+ MATCHER OPTIONS (telling these apart IS the measurement):
376
+ -cf, --captcha-fail MATCH how the app rejects a wrong CAPTCHA. Repeatable, OR-ed. Without it
377
+ a CAPTCHA rejection cannot be told from a credential one, so the
378
+ gate checks report inconclusive rather than guess
379
+ -af, --auth-fail MATCH how the app rejects wrong credentials. Repeatable, OR-ed. Keep it
380
+ narrow: a substring that also matches the CAPTCHA rejection makes
381
+ the two indistinguishable
382
+ -ok, --success MATCH how the app answers a successful login. Repeatable, OR-ed
383
+ -ou, --success-url FRAGMENT shorthand for -ok 'url:FRAGMENT'
384
+
385
+ MATCH SYNTAX (-cf / -af / -ok):
386
+ invalid captcha a substring of the response body
387
+ re:PATTERN regular expression against the body
388
+ status:422 HTTP status code
389
+ url:/dashboard substring of the final URL, after redirects
390
+ json:code=4001 dotted JSON path equals a value
391
+ header:location=/home response header equals a value
392
+ Each flag repeats; its conditions are OR-ed.
393
+
394
+ EXTRACT SYNTAX (-E name=source:key):
395
+ token=hidden:_token a hidden form input
396
+ nonce=regex:captcha[?]n=([^"]+) one capture group
397
+ sid=json:data.id@captcha dotted JSON path, from the CAPTCHA reply
398
+ xsrf=cookie:XSRF-TOKEN a cookie the app expects echoed back
399
+ Names become {{placeholders}} usable in -c, -u, -H and the body.
400
+
401
+ MODULES (-M, all run by default):
402
+ gate is the CAPTCHA required, single-use and rotating at all
403
+ solve-rate how often OCR reads it, using a throwaway identity - needs -cf and -af
404
+ credential password test straight through the CAPTCHA - needs -w
405
+
406
+ EXAMPLE USAGE:
407
+ Tune the engine offline first; a live run spends attempts the target rate limits.
408
+ capat collect -c https://example.org/captcha -o corpus # then name each file its answer
409
+ capat bench corpus -s ddddocr
410
+
411
+ Measure a plain HTML login form. The two phrases are the measurement.
412
+ capat audit -u https://example.org/login \
413
+ -cf "invalid captcha" -af "invalid username or password"
414
+
415
+ A page that mints a fresh CAPTCHA URL on every load.
416
+ capat audit -u https://example.org/login -c auto \
417
+ -cf "invalid captcha" -af "invalid username or password"
418
+
419
+ A JSON API returning a base64 image and an id, matched on response codes.
420
+ capat audit -u https://example.org/api/login -e json \
421
+ -c https://example.org/api/captcha -cr json -ci data.image \
422
+ -cd data.id -cn captchaId \
423
+ -cf json:code=4001 -af json:code=4002 -ok json:code=0 -P app.json
424
+
425
+ Replay a saved profile through Burp, enforcement checks only, no OCR.
426
+ capat audit -p app.json -M gate -x http://127.0.0.1:8080
427
+
428
+ Only the findings worth acting on, written to a file.
429
+ capat audit -p app.json -m medium -f markdown -o report.md
430
+ ```
431
+
432
+ </details>
433
+
434
+
435
+ ## Full guide
436
+
437
+ **[docs/GUIDE.md](docs/GUIDE.md)** is the reference:
438
+
439
+ | Section | What it covers |
440
+ |---|---|
441
+ | Start here: benchmark offline | build a labelled corpus, then measure the engine against known answers |
442
+ | Auditing a live login page | the wizard, the flags, and saving a target as a profile |
443
+ | Target shapes | JSON CAPTCHA endpoints, `-c auto` for a per-load URL, `-c base64` for an inline `data:` image |
444
+ | Fitting it to any application | the full profile format, token extraction, custom matchers, request templating |
445
+ | How the live solve rate is measured | why a throwaway identity, and what the number does and does not claim |
446
+ | Checks that need no OCR | the enforcement tests that run without a solver |
447
+ | The credential audit | the opt-in wordlist run, lockout budget and CAPTCHA retries |
448
+ | Pick a solver, then run the wordlist | the sequence that decides whether a wordlist is runnable at all |
449
+ | Extending it | writing a solver or a check against the entry points |
450
+
451
+ ## Contributing
452
+
453
+ Third-party engines and checks register through the `capat.solvers` and
454
+ `capat.modules` entry points; [docs/GUIDE.md](docs/GUIDE.md) shows how.
455
+
456
+ Open an issue before a large change. The suite must pass without the OCR
457
+ extras installed, and no test may touch the network or load a real model.
458
+
459
+ ## License
460
+
461
+ MIT - see [LICENSE](LICENSE).
462
+
463
+ For authorized security testing and research only. The authors accept no
464
+ liability for misuse.