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.
- capat-0.1.0/.github/workflows/ci.yml +28 -0
- capat-0.1.0/.github/workflows/release.yml +22 -0
- capat-0.1.0/LICENSE +21 -0
- capat-0.1.0/PKG-INFO +464 -0
- capat-0.1.0/README.md +426 -0
- capat-0.1.0/docs/GUIDE.md +580 -0
- capat-0.1.0/docs/img/audit.png +0 -0
- capat-0.1.0/pyproject.toml +90 -0
- capat-0.1.0/src/capat/__init__.py +30 -0
- capat-0.1.0/src/capat/__main__.py +4 -0
- capat-0.1.0/src/capat/cli.py +813 -0
- capat-0.1.0/src/capat/collector.py +109 -0
- capat-0.1.0/src/capat/core/__init__.py +0 -0
- capat-0.1.0/src/capat/core/config.py +55 -0
- capat-0.1.0/src/capat/core/discovery.py +101 -0
- capat-0.1.0/src/capat/core/engine.py +85 -0
- capat-0.1.0/src/capat/core/inline.py +390 -0
- capat-0.1.0/src/capat/core/matching.py +241 -0
- capat-0.1.0/src/capat/core/profile.py +585 -0
- capat-0.1.0/src/capat/core/result.py +51 -0
- capat-0.1.0/src/capat/core/target.py +25 -0
- capat-0.1.0/src/capat/core/template.py +135 -0
- capat-0.1.0/src/capat/core/throttle.py +127 -0
- capat-0.1.0/src/capat/corpus.py +157 -0
- capat-0.1.0/src/capat/http/__init__.py +0 -0
- capat-0.1.0/src/capat/http/client.py +135 -0
- capat-0.1.0/src/capat/http/rate_limiter.py +43 -0
- capat-0.1.0/src/capat/modules/__init__.py +43 -0
- capat-0.1.0/src/capat/modules/base.py +38 -0
- capat-0.1.0/src/capat/modules/credential_audit.py +416 -0
- capat-0.1.0/src/capat/modules/gate_enforcement.py +478 -0
- capat-0.1.0/src/capat/modules/login_flow.py +245 -0
- capat-0.1.0/src/capat/modules/solve_rate.py +359 -0
- capat-0.1.0/src/capat/py.typed +0 -0
- capat-0.1.0/src/capat/reporting/__init__.py +0 -0
- capat-0.1.0/src/capat/reporting/banner.py +248 -0
- capat-0.1.0/src/capat/reporting/reporter.py +175 -0
- capat-0.1.0/src/capat/solvers/__init__.py +117 -0
- capat-0.1.0/src/capat/solvers/base.py +153 -0
- capat-0.1.0/src/capat/solvers/ddddocr_solver.py +137 -0
- capat-0.1.0/src/capat/solvers/easyocr_solver.py +76 -0
- capat-0.1.0/src/capat/solvers/ensemble.py +163 -0
- capat-0.1.0/src/capat/solvers/preprocess.py +94 -0
- capat-0.1.0/src/capat/solvers/tesseract_solver.py +64 -0
- capat-0.1.0/src/capat/trace.py +110 -0
- capat-0.1.0/src/capat/wizard.py +171 -0
- capat-0.1.0/tests/__init__.py +0 -0
- capat-0.1.0/tests/conftest.py +58 -0
- capat-0.1.0/tests/test_banner.py +222 -0
- capat-0.1.0/tests/test_captcha_source.py +240 -0
- capat-0.1.0/tests/test_collector.py +145 -0
- capat-0.1.0/tests/test_custom_model.py +121 -0
- capat-0.1.0/tests/test_engine_and_corpus.py +200 -0
- capat-0.1.0/tests/test_ensemble.py +110 -0
- capat-0.1.0/tests/test_gate_criteria.py +128 -0
- capat-0.1.0/tests/test_inline.py +328 -0
- capat-0.1.0/tests/test_modules.py +870 -0
- capat-0.1.0/tests/test_profile.py +78 -0
- capat-0.1.0/tests/test_request_shaping.py +302 -0
- capat-0.1.0/tests/test_rotating_captcha_url.py +272 -0
- capat-0.1.0/tests/test_run_integrity.py +172 -0
- capat-0.1.0/tests/test_solvers.py +101 -0
- capat-0.1.0/tests/test_throttling.py +138 -0
- 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
|
+
[](https://github.com/goblensec/capat/actions/workflows/ci.yml)
|
|
50
|
+
[](https://www.python.org/downloads/)
|
|
51
|
+
[](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
|
+

|
|
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> — 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.
|