ctf-attackapi 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.
@@ -0,0 +1,231 @@
1
+ Metadata-Version: 2.4
2
+ Name: ctf-attackapi
3
+ Version: 0.1.0
4
+ Summary: Get attack infos in attack-defense CTFs quickly to your exploits. CTF-agnostic and cached.
5
+ Keywords: Attack-Defense,CTF,Attack API,Attack Info,Flag IDs,FAUST CTF,ENOWARS,saarCTF
6
+ Author: Markus Bauer
7
+ Author-email: Markus Bauer <markus.bauer@cispa.saarland>
8
+ License-Expression: MIT
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Intended Audience :: Education
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Topic :: Security
20
+ Classifier: Typing :: Typed
21
+ Requires-Dist: aiohttp>=3.13.3
22
+ Requires-Dist: aiologic>=0.16.0
23
+ Requires-Dist: filelock>=3.19.1
24
+ Requires-Dist: pyyaml>=6.0.3
25
+ Requires-Dist: typing-extensions>=4.15.0
26
+ Requires-Dist: gunicorn>=23.0.0 ; extra == 'server'
27
+ Requires-Python: >=3.9
28
+ Project-URL: Homepage, https://github.com/Attacking-Lab/ctf-attackapi
29
+ Project-URL: Repository, https://github.com/Attacking-Lab/ctf-attackapi
30
+ Project-URL: Issues, https://github.com/Attacking-Lab/ctf-attackapi/issues
31
+ Project-URL: Background, https://wiki.attacking-lab.com/attack-defense/
32
+ Provides-Extra: server
33
+ Description-Content-Type: text/markdown
34
+
35
+ CTF AttackAPI - Cached and Unified!
36
+ ===================================
37
+
38
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/license/mit)
39
+ ![Python](https://img.shields.io/pypi/pyversions/ctf-attackapi?pypiBaseUrl=https://test.pypi.org)
40
+ ![Types](https://img.shields.io/pypi/types/ctf-attackapi?pypiBaseUrl=https://test.pypi.org)
41
+ [![Python package tests](https://github.com/Attacking-Lab/ctf-attackapi/actions/workflows/python-package.yml/badge.svg)](https://github.com/Attacking-Lab/ctf-attackapi/actions/workflows/python-package.yml)
42
+ [![PyPI version](https://img.shields.io/pypi/v/ctf-attackapi?pypiBaseUrl=https://test.pypi.org)](https://pypi.org/project/ctf-attackapi)
43
+ ![Downloads](https://img.shields.io/pypi/dm/ctf-attackapi?pypiBaseUrl=https://test.pypi.org)
44
+ ![Repo size](https://img.shields.io/github/repo-size/Attacking-Lab/ctf-attackapi)
45
+
46
+
47
+ Gather attack information quickly in your attack-defense CTF exploits!
48
+
49
+ During [attack-defense CTF competitions](https://wiki.attacking-lab.com/attack-defense/), you have to write exploits
50
+ quickly and run them on a large scale.
51
+ These exploits often require information about the targets to attack (teams and sometimes usernames).
52
+ This attack info is available as a big JSON file which is updated every few minutes.
53
+ Downloading that file for every exploit you're firing is costing time and bandwidth.
54
+
55
+ This package fetches, parses, and caches attack info for you, so you can focus on writing exploits!
56
+
57
+ Features
58
+ --------
59
+
60
+ - Efficient caching between threads, processes, or containers
61
+ - Direct access from your Python exploits ([sync](./examples/basic.py) or [async](./examples/basic_async.py))
62
+ - Optional REST API for exploits in other languages (with [OpenAPI spec](./api.yaml))
63
+ - Unifies team, IP, and flag info lookup between different CTFs:
64
+ - Supports [ENOWARS](https://enowars.com)
65
+ - Supports [FAUST CTF](https://faustctf.net)
66
+ - Supports [saarCTF](https://ctf.saarland) (including ECSC gameserver)
67
+
68
+ Quick-Start
69
+ -----------
70
+ See [examples](./examples) directory for more full scripts.
71
+
72
+ Install the package (possibly in a virtual environment):
73
+
74
+ ```shell
75
+ pip install ctf-attackapi
76
+ ```
77
+
78
+ Get attack infos for your python exploit:
79
+
80
+ ```python
81
+ from attackapi import *
82
+
83
+ # 1. Set the API URL in code (or use CTF_API environment variable)
84
+ configure("https://scoreboard.ctf.saarland/api/attack.json")
85
+ # 2. Get attack infos!
86
+ for username in attack_info().flag_id_flat("no-service", "10.32.1.2"):
87
+ pwn("10.32.1.2", username)
88
+ ```
89
+
90
+ List all teams that you can attack:
91
+
92
+ ```python
93
+ from attackapi import *
94
+
95
+ configure("https://scoreboard.ctf.saarland/api/attack.json")
96
+ for team in attack_info().teams:
97
+ print(team.id, team.ip, team.name)
98
+ ```
99
+
100
+ Get attack infos from REST API if you're not pwning in Python:
101
+
102
+ ```shell
103
+ python -m attackapi.server --url "https://scoreboard.ctf.saarland/api/attack.json"
104
+ curl "http://localhost:14320/api/v1/teams"
105
+ ```
106
+
107
+ The server has documentation on its frontpage, and here is [the OpenAPI specification](./api.yaml).
108
+
109
+ If you're not pwning in Python and dislike pip, try docker:
110
+ ```shell
111
+ # edit compose.yaml and insert your CTF API URL
112
+ docker compose up -d
113
+ # visit http://localhost:14320/
114
+ ```
115
+
116
+
117
+ Structure
118
+ ---------
119
+
120
+ - Attack info data is retrieved and cached twice: in-memory and on disk (`/tmp` by default)
121
+ - Each request goes to the caches. If the cached data is outdated, it is refreshed in the background.
122
+ - No concurrent requests are made to the game API.
123
+ - Game-specific decoders process the game APIs data and make it accessible.
124
+ - You can query the data via python API from your exploits, or via REST API from other languages.
125
+ - Relying on the disk cache is good enough for typical exploitation scenarios.
126
+
127
+ Python Library Documentation
128
+ ----------------------------
129
+ There are different ways to get an `AttackInfo` object:
130
+
131
+ ```python
132
+ # 1. Functional
133
+ from attackapi import *
134
+
135
+ # Set the API URL in code (or use CTF_API environment variable)
136
+ configure("https://scoreboard.ctf.saarland/api/attack.json")
137
+ # sync:
138
+ info: AttackInfo = attack_info()
139
+ # async
140
+ info: AttackInfo = await attack_info_async()
141
+
142
+ # 2. By manually using the classes
143
+ from attackapi.sync_api import AdCtfApiSync
144
+ from attackapi.async_api import AdCtfApiAsync
145
+
146
+ api = AdCtfApiSync("https://scoreboard.ctf.saarland/api/attack.json")
147
+ info = api.attack_info()
148
+ api2 = AdCtfApiAsync("https://scoreboard.ctf.saarland/api/attack.json")
149
+ info = await api2.attack_info()
150
+ ```
151
+
152
+ Optional parameters can be passed to the `configure` function or the API constructors:
153
+
154
+ - `url: str` (default: `CTF_API` environment variable)
155
+ - `tmp_directory: str | Path` (default: `/tmp` or OS-specific alternative)
156
+ - `lifetime: float` (default: 30 seconds) - after this time, cached data is invalidated and refreshed
157
+ - `timeout: float` (default: 10 seconds) - abort game API requests after this duration
158
+ - `decoder: Decoder` (default: generic decoder) - custom decoder, if your game's format is different from what we've
159
+ seen so far
160
+ - `aiohttp_arguments: dict` - additional arguments passed to the aiohttp Session which contacts the game API
161
+
162
+ The `AttackInfo` class itself has these methods:
163
+
164
+ ```python
165
+ info: AttackInfo
166
+
167
+ # Get attackable teams
168
+ print(info.teams) # list of Team objects
169
+ print(info.teams[0].id, info.teams[0].ip, info.teams[0].name) # Team is ID, IP, and optional name
170
+ print(info.team("10.32.1.2")) # query Team object by ID, IP, or name
171
+
172
+ # set of service names
173
+ print(info.services)
174
+
175
+ # raw flag IDs for a service and team.
176
+ # team can be ID, IP, or name.
177
+ # Return data format is determined by game API.
178
+ print(info.flag_id_raw("servicename", "10.32.1.2"))
179
+ # => {"227": "abc", "228": "def", ...}
180
+
181
+ # Get flag IDs as string list (independent of game API format, but less precise)
182
+ print(info.flag_id_flat("servicename", "10.32.1.2"))
183
+ # => ["abc", "def"]
184
+ ```
185
+
186
+ Server Documentation
187
+ --------------------
188
+
189
+ ```shell
190
+ # Simple usage:
191
+ python -m attackapi.server --help
192
+ ```
193
+
194
+ Options:
195
+
196
+ - `--port PORT`
197
+ - `--url URL`: API url to get CTF info from.
198
+ - `--tmp-directory TMP_DIRECTORY`: Cache directory
199
+ - `--lifetime LIFETIME`: Lifetime of cached data in seconds
200
+ - `--timeout TIMEOUT`: Timeout for API calls in seconds
201
+
202
+ ```shell
203
+ # Usage for higher load scenarios:
204
+ pip install ctf-attackapi[gunicorn]
205
+ gunicorn attackapi.server:create_app --bind :14320 --worker-class attackapi.server.worker.MyGunicornWebWorker --workers 4
206
+ ```
207
+
208
+ Environment variables:
209
+
210
+ - `CTF_API`: URL to get CTF info from.
211
+ - `CTF_API_TMP_DIR`: Cache directory (gunicorn only)
212
+ - `CTF_API_LIFETIME`: Lifetime of cached data in seconds (gunicorn only)
213
+ - `CTF_API_TIMEOUT`: Timeout for API calls in seconds (gunicorn only)
214
+
215
+ You can also use docker to run the server:
216
+ ```shell
217
+ # edit compose.yaml and insert your CTF API URL before!
218
+ docker compose up -d
219
+ ```
220
+
221
+
222
+ Using attackapi for other information (scoreboard etc.)
223
+ -------------------------------------------------------
224
+ Feel free to re-use the caching layers for other information, like the current scoreboard.
225
+ The class `JsonAdCtfApiAsync` accepts arbitrary JSON endpoints:
226
+
227
+ ```python
228
+ from attackapi.async_api import JsonAdCtfApiAsync
229
+
230
+ info = await JsonAdCtfApiAsync("https://scoreboard.ctf.saarland/api/scoreboard_current.json").retrieve()
231
+ ```
@@ -0,0 +1,197 @@
1
+ CTF AttackAPI - Cached and Unified!
2
+ ===================================
3
+
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/license/mit)
5
+ ![Python](https://img.shields.io/pypi/pyversions/ctf-attackapi?pypiBaseUrl=https://test.pypi.org)
6
+ ![Types](https://img.shields.io/pypi/types/ctf-attackapi?pypiBaseUrl=https://test.pypi.org)
7
+ [![Python package tests](https://github.com/Attacking-Lab/ctf-attackapi/actions/workflows/python-package.yml/badge.svg)](https://github.com/Attacking-Lab/ctf-attackapi/actions/workflows/python-package.yml)
8
+ [![PyPI version](https://img.shields.io/pypi/v/ctf-attackapi?pypiBaseUrl=https://test.pypi.org)](https://pypi.org/project/ctf-attackapi)
9
+ ![Downloads](https://img.shields.io/pypi/dm/ctf-attackapi?pypiBaseUrl=https://test.pypi.org)
10
+ ![Repo size](https://img.shields.io/github/repo-size/Attacking-Lab/ctf-attackapi)
11
+
12
+
13
+ Gather attack information quickly in your attack-defense CTF exploits!
14
+
15
+ During [attack-defense CTF competitions](https://wiki.attacking-lab.com/attack-defense/), you have to write exploits
16
+ quickly and run them on a large scale.
17
+ These exploits often require information about the targets to attack (teams and sometimes usernames).
18
+ This attack info is available as a big JSON file which is updated every few minutes.
19
+ Downloading that file for every exploit you're firing is costing time and bandwidth.
20
+
21
+ This package fetches, parses, and caches attack info for you, so you can focus on writing exploits!
22
+
23
+ Features
24
+ --------
25
+
26
+ - Efficient caching between threads, processes, or containers
27
+ - Direct access from your Python exploits ([sync](./examples/basic.py) or [async](./examples/basic_async.py))
28
+ - Optional REST API for exploits in other languages (with [OpenAPI spec](./api.yaml))
29
+ - Unifies team, IP, and flag info lookup between different CTFs:
30
+ - Supports [ENOWARS](https://enowars.com)
31
+ - Supports [FAUST CTF](https://faustctf.net)
32
+ - Supports [saarCTF](https://ctf.saarland) (including ECSC gameserver)
33
+
34
+ Quick-Start
35
+ -----------
36
+ See [examples](./examples) directory for more full scripts.
37
+
38
+ Install the package (possibly in a virtual environment):
39
+
40
+ ```shell
41
+ pip install ctf-attackapi
42
+ ```
43
+
44
+ Get attack infos for your python exploit:
45
+
46
+ ```python
47
+ from attackapi import *
48
+
49
+ # 1. Set the API URL in code (or use CTF_API environment variable)
50
+ configure("https://scoreboard.ctf.saarland/api/attack.json")
51
+ # 2. Get attack infos!
52
+ for username in attack_info().flag_id_flat("no-service", "10.32.1.2"):
53
+ pwn("10.32.1.2", username)
54
+ ```
55
+
56
+ List all teams that you can attack:
57
+
58
+ ```python
59
+ from attackapi import *
60
+
61
+ configure("https://scoreboard.ctf.saarland/api/attack.json")
62
+ for team in attack_info().teams:
63
+ print(team.id, team.ip, team.name)
64
+ ```
65
+
66
+ Get attack infos from REST API if you're not pwning in Python:
67
+
68
+ ```shell
69
+ python -m attackapi.server --url "https://scoreboard.ctf.saarland/api/attack.json"
70
+ curl "http://localhost:14320/api/v1/teams"
71
+ ```
72
+
73
+ The server has documentation on its frontpage, and here is [the OpenAPI specification](./api.yaml).
74
+
75
+ If you're not pwning in Python and dislike pip, try docker:
76
+ ```shell
77
+ # edit compose.yaml and insert your CTF API URL
78
+ docker compose up -d
79
+ # visit http://localhost:14320/
80
+ ```
81
+
82
+
83
+ Structure
84
+ ---------
85
+
86
+ - Attack info data is retrieved and cached twice: in-memory and on disk (`/tmp` by default)
87
+ - Each request goes to the caches. If the cached data is outdated, it is refreshed in the background.
88
+ - No concurrent requests are made to the game API.
89
+ - Game-specific decoders process the game APIs data and make it accessible.
90
+ - You can query the data via python API from your exploits, or via REST API from other languages.
91
+ - Relying on the disk cache is good enough for typical exploitation scenarios.
92
+
93
+ Python Library Documentation
94
+ ----------------------------
95
+ There are different ways to get an `AttackInfo` object:
96
+
97
+ ```python
98
+ # 1. Functional
99
+ from attackapi import *
100
+
101
+ # Set the API URL in code (or use CTF_API environment variable)
102
+ configure("https://scoreboard.ctf.saarland/api/attack.json")
103
+ # sync:
104
+ info: AttackInfo = attack_info()
105
+ # async
106
+ info: AttackInfo = await attack_info_async()
107
+
108
+ # 2. By manually using the classes
109
+ from attackapi.sync_api import AdCtfApiSync
110
+ from attackapi.async_api import AdCtfApiAsync
111
+
112
+ api = AdCtfApiSync("https://scoreboard.ctf.saarland/api/attack.json")
113
+ info = api.attack_info()
114
+ api2 = AdCtfApiAsync("https://scoreboard.ctf.saarland/api/attack.json")
115
+ info = await api2.attack_info()
116
+ ```
117
+
118
+ Optional parameters can be passed to the `configure` function or the API constructors:
119
+
120
+ - `url: str` (default: `CTF_API` environment variable)
121
+ - `tmp_directory: str | Path` (default: `/tmp` or OS-specific alternative)
122
+ - `lifetime: float` (default: 30 seconds) - after this time, cached data is invalidated and refreshed
123
+ - `timeout: float` (default: 10 seconds) - abort game API requests after this duration
124
+ - `decoder: Decoder` (default: generic decoder) - custom decoder, if your game's format is different from what we've
125
+ seen so far
126
+ - `aiohttp_arguments: dict` - additional arguments passed to the aiohttp Session which contacts the game API
127
+
128
+ The `AttackInfo` class itself has these methods:
129
+
130
+ ```python
131
+ info: AttackInfo
132
+
133
+ # Get attackable teams
134
+ print(info.teams) # list of Team objects
135
+ print(info.teams[0].id, info.teams[0].ip, info.teams[0].name) # Team is ID, IP, and optional name
136
+ print(info.team("10.32.1.2")) # query Team object by ID, IP, or name
137
+
138
+ # set of service names
139
+ print(info.services)
140
+
141
+ # raw flag IDs for a service and team.
142
+ # team can be ID, IP, or name.
143
+ # Return data format is determined by game API.
144
+ print(info.flag_id_raw("servicename", "10.32.1.2"))
145
+ # => {"227": "abc", "228": "def", ...}
146
+
147
+ # Get flag IDs as string list (independent of game API format, but less precise)
148
+ print(info.flag_id_flat("servicename", "10.32.1.2"))
149
+ # => ["abc", "def"]
150
+ ```
151
+
152
+ Server Documentation
153
+ --------------------
154
+
155
+ ```shell
156
+ # Simple usage:
157
+ python -m attackapi.server --help
158
+ ```
159
+
160
+ Options:
161
+
162
+ - `--port PORT`
163
+ - `--url URL`: API url to get CTF info from.
164
+ - `--tmp-directory TMP_DIRECTORY`: Cache directory
165
+ - `--lifetime LIFETIME`: Lifetime of cached data in seconds
166
+ - `--timeout TIMEOUT`: Timeout for API calls in seconds
167
+
168
+ ```shell
169
+ # Usage for higher load scenarios:
170
+ pip install ctf-attackapi[gunicorn]
171
+ gunicorn attackapi.server:create_app --bind :14320 --worker-class attackapi.server.worker.MyGunicornWebWorker --workers 4
172
+ ```
173
+
174
+ Environment variables:
175
+
176
+ - `CTF_API`: URL to get CTF info from.
177
+ - `CTF_API_TMP_DIR`: Cache directory (gunicorn only)
178
+ - `CTF_API_LIFETIME`: Lifetime of cached data in seconds (gunicorn only)
179
+ - `CTF_API_TIMEOUT`: Timeout for API calls in seconds (gunicorn only)
180
+
181
+ You can also use docker to run the server:
182
+ ```shell
183
+ # edit compose.yaml and insert your CTF API URL before!
184
+ docker compose up -d
185
+ ```
186
+
187
+
188
+ Using attackapi for other information (scoreboard etc.)
189
+ -------------------------------------------------------
190
+ Feel free to re-use the caching layers for other information, like the current scoreboard.
191
+ The class `JsonAdCtfApiAsync` accepts arbitrary JSON endpoints:
192
+
193
+ ```python
194
+ from attackapi.async_api import JsonAdCtfApiAsync
195
+
196
+ info = await JsonAdCtfApiAsync("https://scoreboard.ctf.saarland/api/scoreboard_current.json").retrieve()
197
+ ```
@@ -0,0 +1,73 @@
1
+ [project]
2
+ name = "ctf-attackapi"
3
+ version = "0.1.0"
4
+ description = "Get attack infos in attack-defense CTFs quickly to your exploits. CTF-agnostic and cached."
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license_files = ["LICENSE.txt"]
8
+ keywords = ["Attack-Defense", "CTF", "Attack API", "Attack Info", "Flag IDs", "FAUST CTF", "ENOWARS", "saarCTF"]
9
+ authors = [
10
+ { name = "Markus Bauer", email = "markus.bauer@cispa.saarland" }
11
+ ]
12
+ requires-python = ">=3.9"
13
+ classifiers = [
14
+ "Development Status :: 4 - Beta",
15
+ "Intended Audience :: Developers",
16
+ "Intended Audience :: Education",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3.9",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ "Programming Language :: Python :: 3.14",
24
+ "Topic :: Security",
25
+ "Typing :: Typed",
26
+ ]
27
+ dependencies = [
28
+ "aiohttp>=3.13.3",
29
+ "aiologic>=0.16.0",
30
+ "filelock>=3.19.1",
31
+ "pyyaml>=6.0.3",
32
+ "typing-extensions>=4.15.0",
33
+ ]
34
+
35
+ [project.optional-dependencies]
36
+ server = [
37
+ "gunicorn>=23.0.0",
38
+ ]
39
+
40
+ [project.urls]
41
+ Homepage = "https://github.com/Attacking-Lab/ctf-attackapi"
42
+ Repository = "https://github.com/Attacking-Lab/ctf-attackapi"
43
+ Issues = "https://github.com/Attacking-Lab/ctf-attackapi/issues"
44
+ Background = "https://wiki.attacking-lab.com/attack-defense/"
45
+
46
+ [build-system]
47
+ requires = ["uv_build>=0.9.27,<0.10.0"]
48
+ build-backend = "uv_build"
49
+
50
+ [project.scripts]
51
+ ctf-attackapi-server = "attackapi.server:main"
52
+
53
+ [dependency-groups]
54
+ dev = [
55
+ "mypy>=1.19.1",
56
+ "pytest>=8.4.2",
57
+ "types-pyyaml>=6.0.12.20250915",
58
+ ]
59
+
60
+ [tool.uv.build-backend]
61
+ module-name = "attackapi"
62
+
63
+ [[tool.uv.index]]
64
+ name = "testpypi"
65
+ url = "https://test.pypi.org/simple/"
66
+ publish-url = "https://test.pypi.org/legacy/"
67
+ explicit = true
68
+
69
+ [tool.mypy]
70
+ python_version = "3.9"
71
+ disallow_untyped_defs = true
72
+ packages = ["attackapi", "tests", "examples"]
73
+ mypy_path = ["./src", "./tests", "./examples"]
@@ -0,0 +1,4 @@
1
+ from .models import Team, AttackInfo
2
+ from .functional import configure, attack_info, attack_info_async
3
+
4
+ __all__ = ["Team", "AttackInfo", "configure", "attack_info", "attack_info_async"]
@@ -0,0 +1,4 @@
1
+ from .api import AdCtfApiAsync, JsonAdCtfApiAsync
2
+ from .decoders import Decoder, Dialect
3
+
4
+ __all__ = ["AdCtfApiAsync", "JsonAdCtfApiAsync", "Decoder", "Dialect"]