cookiefarm 1.0.2__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.
- cookiefarm-1.0.2/.gitignore +140 -0
- cookiefarm-1.0.2/LICENSE +21 -0
- cookiefarm-1.0.2/PKG-INFO +132 -0
- cookiefarm-1.0.2/README.md +98 -0
- cookiefarm-1.0.2/cookiefarm/__init__.py +3 -0
- cookiefarm-1.0.2/cookiefarm/bin/ckc +0 -0
- cookiefarm-1.0.2/cookiefarm/cli.py +13 -0
- cookiefarm-1.0.2/cookiefarm/exploit_config.py +78 -0
- cookiefarm-1.0.2/cookiefarm/exploit_executor.py +147 -0
- cookiefarm-1.0.2/cookiefarm/exploiter_manager.py +82 -0
- cookiefarm-1.0.2/cookiefarm/logger.py +96 -0
- cookiefarm-1.0.2/cookiefarm/server_config.py +112 -0
- cookiefarm-1.0.2/cookiefarm/test_executor.py +82 -0
- cookiefarm-1.0.2/pyproject.toml +43 -0
- cookiefarm-1.0.2/venv_1/bin/cookiefarm +8 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
pip-wheel-metadata/
|
|
24
|
+
share/python-wheels/
|
|
25
|
+
*.egg-info/
|
|
26
|
+
.installed.cfg
|
|
27
|
+
*.egg
|
|
28
|
+
MANIFEST
|
|
29
|
+
|
|
30
|
+
# PyInstaller
|
|
31
|
+
# Usually these files are written by a python script from a template
|
|
32
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
33
|
+
*.manifest
|
|
34
|
+
*.spec
|
|
35
|
+
|
|
36
|
+
# Installer logs
|
|
37
|
+
pip-log.txt
|
|
38
|
+
pip-delete-this-directory.txt
|
|
39
|
+
|
|
40
|
+
# Unit test / coverage reports
|
|
41
|
+
htmlcov/
|
|
42
|
+
.tox/
|
|
43
|
+
.nox/
|
|
44
|
+
.coverage
|
|
45
|
+
.coverage.*
|
|
46
|
+
.cache
|
|
47
|
+
nosetests.xml
|
|
48
|
+
coverage.xml
|
|
49
|
+
*.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
|
|
53
|
+
# Translations
|
|
54
|
+
*.mo
|
|
55
|
+
*.pot
|
|
56
|
+
|
|
57
|
+
# Django stuff:
|
|
58
|
+
*.log
|
|
59
|
+
local_settings.py
|
|
60
|
+
db.sqlite3
|
|
61
|
+
db.sqlite3-journal
|
|
62
|
+
|
|
63
|
+
# Flask stuff:
|
|
64
|
+
instance/
|
|
65
|
+
.webassets-cache
|
|
66
|
+
|
|
67
|
+
# Scrapy stuff:
|
|
68
|
+
.scrapy
|
|
69
|
+
|
|
70
|
+
# Sphinx documentation
|
|
71
|
+
docs/_build/
|
|
72
|
+
build/
|
|
73
|
+
temp/
|
|
74
|
+
|
|
75
|
+
# PyBuilder
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
.python-version
|
|
87
|
+
|
|
88
|
+
# pipenv
|
|
89
|
+
Pipfile.lock
|
|
90
|
+
|
|
91
|
+
# poetry
|
|
92
|
+
poetry.lock
|
|
93
|
+
|
|
94
|
+
# PEP 582; used by python-next
|
|
95
|
+
__pypackages__/
|
|
96
|
+
|
|
97
|
+
# Celery stuff
|
|
98
|
+
celerybeat-schedule
|
|
99
|
+
celerybeat.pid
|
|
100
|
+
|
|
101
|
+
# SageMath parsed files
|
|
102
|
+
*.sage.py
|
|
103
|
+
|
|
104
|
+
# Environments
|
|
105
|
+
.env
|
|
106
|
+
.venv
|
|
107
|
+
env/
|
|
108
|
+
venv/
|
|
109
|
+
ENV/
|
|
110
|
+
env.bak/
|
|
111
|
+
venv.bak/
|
|
112
|
+
|
|
113
|
+
# Spyder project settings
|
|
114
|
+
.spyderproject
|
|
115
|
+
.spyproject
|
|
116
|
+
|
|
117
|
+
# Rope project settings
|
|
118
|
+
.ropeproject
|
|
119
|
+
|
|
120
|
+
# mkdocs documentation
|
|
121
|
+
/site
|
|
122
|
+
|
|
123
|
+
# mypy
|
|
124
|
+
.mypy_cache/
|
|
125
|
+
.dmypy.json
|
|
126
|
+
dmypy.json
|
|
127
|
+
|
|
128
|
+
# Pyre type checker
|
|
129
|
+
.pyre/
|
|
130
|
+
|
|
131
|
+
# pytype static type analyzer
|
|
132
|
+
.pytype/
|
|
133
|
+
|
|
134
|
+
# Cython debug symbols
|
|
135
|
+
cython_debug/
|
|
136
|
+
|
|
137
|
+
# VS Code settings
|
|
138
|
+
.vscode/
|
|
139
|
+
|
|
140
|
+
test.py
|
cookiefarm-1.0.2/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 ByteTheCookies
|
|
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,132 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cookiefarm
|
|
3
|
+
Version: 1.0.2
|
|
4
|
+
Summary: Python decorator for parallel exploit dispatch in Attack & Defense CTFs using the CookieFarm framework.
|
|
5
|
+
Project-URL: Homepage, https://github.com/ByteTheCookies/CookieFarmExploiter
|
|
6
|
+
Author-email: ByteTheCookies <bytethecookies@proton.me>
|
|
7
|
+
License: MIT License
|
|
8
|
+
|
|
9
|
+
Copyright (c) 2025 ByteTheCookies
|
|
10
|
+
|
|
11
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
12
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
13
|
+
in the Software without restriction, including without limitation the rights
|
|
14
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
15
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
16
|
+
furnished to do so, subject to the following conditions:
|
|
17
|
+
|
|
18
|
+
The above copyright notice and this permission notice shall be included in all
|
|
19
|
+
copies or substantial portions of the Software.
|
|
20
|
+
|
|
21
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
22
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
23
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
24
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
25
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
26
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
27
|
+
SOFTWARE.
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
30
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
31
|
+
Classifier: Programming Language :: Python :: 3
|
|
32
|
+
Requires-Python: >=3.12
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# 🍪 CookieFarm - Exploiter Manager
|
|
36
|
+
|
|
37
|
+

|
|
38
|
+

|
|
39
|
+

|
|
40
|
+
|
|
41
|
+
> Python decorator for automating exploit execution in CTF Attack & Defense competitions
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 📦 What is it?
|
|
46
|
+
|
|
47
|
+
This package provides a **`@exploit_manager` decorator** designed to automate the parallel execution of exploits in CTF (*Attack & Defense*) settings, specifically for use with the [CookieFarm](https://github.com/BytesTheCookies/CookieFarm) project.
|
|
48
|
+
|
|
49
|
+
It handles:
|
|
50
|
+
|
|
51
|
+
* Authentication with the central server
|
|
52
|
+
* Retrieving team configuration
|
|
53
|
+
* Asynchronous dispatch of exploits to multiple targets
|
|
54
|
+
* Automatic flag parsing from `stdout`
|
|
55
|
+
|
|
56
|
+
> ⚠️ **Note:** This package is **not standalone**. It must be used together with the [CookieFarm client](https://github.com/BytesTheCookies/CookieFarm). The client provides the required APIs and team configurations.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 📦 Installation
|
|
61
|
+
|
|
62
|
+
To install the package:
|
|
63
|
+
```bash
|
|
64
|
+
pip install cookiefarm-exploiter
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
## ⚙️ How it works
|
|
69
|
+
|
|
70
|
+
The `@exploit_manager` decorator takes care of:
|
|
71
|
+
|
|
72
|
+
* Calling your `exploit(ip, port, name_service)` function
|
|
73
|
+
* Capturing your exploit's `stdout`
|
|
74
|
+
* Parsing flags via regex
|
|
75
|
+
* Logging the result in JSON format, including: team ID, port, service name, and the flag found
|
|
76
|
+
|
|
77
|
+
All of this is done in parallel using `asyncio` and `ThreadPoolExecutor`, making the process **extremely efficient**, even with dozens of teams.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 🚀 Example usage
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from cookiefarm_exploiter import exploit_manager
|
|
85
|
+
import requests
|
|
86
|
+
|
|
87
|
+
@exploit_manager
|
|
88
|
+
def exploit(ip, port, name_service):
|
|
89
|
+
# Run your exploit here
|
|
90
|
+
response = requests.get(f"http://{ip}:{port}/")
|
|
91
|
+
|
|
92
|
+
# Just print the flag to stdout
|
|
93
|
+
print(response.text)
|
|
94
|
+
|
|
95
|
+
# Run from the command line with arguments from CookieFarm
|
|
96
|
+
# python3 myexploit.py <ip_server> <password> <tick_time> <thread_number> <port> <name_service>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
For execution, you have to pass the required arguments from the command line, which are provided by the CookieFarm client. The decorator will handle the rest.
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
|
|
103
|
+
python3 myexploit.py <server_address> <tick_time> <thread_number> <port> <name_service>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Where:
|
|
107
|
+
|
|
108
|
+
* `<server_address>`: The address of the CookieFarm server
|
|
109
|
+
* `<tick_time>`: The time interval for the exploit to run
|
|
110
|
+
* `<thread_number>`: The number of threads to use for parallel execution
|
|
111
|
+
* `<port>`: The port of the service to exploit
|
|
112
|
+
* `<name_service>`: The name of the service being exploited
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 🛠️ Requirements
|
|
117
|
+
|
|
118
|
+
* Python ≥ 3.12
|
|
119
|
+
* Working CookieFarm client installed
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## 📝 License
|
|
125
|
+
|
|
126
|
+
Distributed under the [MIT License](LICENSE). Feel free to use, modify, and contribute.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
For any questions, suggestions, or issues, feel free to open a [GitHub issue](https://www.github.com/BytesTheCookies/CookieFarmExploiter)!
|
|
131
|
+
|
|
132
|
+
**Created with ❤️ by ByteTheCookies (feat. @0xMatte)**
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# 🍪 CookieFarm - Exploiter Manager
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
> Python decorator for automating exploit execution in CTF Attack & Defense competitions
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 📦 What is it?
|
|
12
|
+
|
|
13
|
+
This package provides a **`@exploit_manager` decorator** designed to automate the parallel execution of exploits in CTF (*Attack & Defense*) settings, specifically for use with the [CookieFarm](https://github.com/BytesTheCookies/CookieFarm) project.
|
|
14
|
+
|
|
15
|
+
It handles:
|
|
16
|
+
|
|
17
|
+
* Authentication with the central server
|
|
18
|
+
* Retrieving team configuration
|
|
19
|
+
* Asynchronous dispatch of exploits to multiple targets
|
|
20
|
+
* Automatic flag parsing from `stdout`
|
|
21
|
+
|
|
22
|
+
> ⚠️ **Note:** This package is **not standalone**. It must be used together with the [CookieFarm client](https://github.com/BytesTheCookies/CookieFarm). The client provides the required APIs and team configurations.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 📦 Installation
|
|
27
|
+
|
|
28
|
+
To install the package:
|
|
29
|
+
```bash
|
|
30
|
+
pip install cookiefarm-exploiter
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
## ⚙️ How it works
|
|
35
|
+
|
|
36
|
+
The `@exploit_manager` decorator takes care of:
|
|
37
|
+
|
|
38
|
+
* Calling your `exploit(ip, port, name_service)` function
|
|
39
|
+
* Capturing your exploit's `stdout`
|
|
40
|
+
* Parsing flags via regex
|
|
41
|
+
* Logging the result in JSON format, including: team ID, port, service name, and the flag found
|
|
42
|
+
|
|
43
|
+
All of this is done in parallel using `asyncio` and `ThreadPoolExecutor`, making the process **extremely efficient**, even with dozens of teams.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 🚀 Example usage
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from cookiefarm_exploiter import exploit_manager
|
|
51
|
+
import requests
|
|
52
|
+
|
|
53
|
+
@exploit_manager
|
|
54
|
+
def exploit(ip, port, name_service):
|
|
55
|
+
# Run your exploit here
|
|
56
|
+
response = requests.get(f"http://{ip}:{port}/")
|
|
57
|
+
|
|
58
|
+
# Just print the flag to stdout
|
|
59
|
+
print(response.text)
|
|
60
|
+
|
|
61
|
+
# Run from the command line with arguments from CookieFarm
|
|
62
|
+
# python3 myexploit.py <ip_server> <password> <tick_time> <thread_number> <port> <name_service>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
For execution, you have to pass the required arguments from the command line, which are provided by the CookieFarm client. The decorator will handle the rest.
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
|
|
69
|
+
python3 myexploit.py <server_address> <tick_time> <thread_number> <port> <name_service>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Where:
|
|
73
|
+
|
|
74
|
+
* `<server_address>`: The address of the CookieFarm server
|
|
75
|
+
* `<tick_time>`: The time interval for the exploit to run
|
|
76
|
+
* `<thread_number>`: The number of threads to use for parallel execution
|
|
77
|
+
* `<port>`: The port of the service to exploit
|
|
78
|
+
* `<name_service>`: The name of the service being exploited
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 🛠️ Requirements
|
|
83
|
+
|
|
84
|
+
* Python ≥ 3.12
|
|
85
|
+
* Working CookieFarm client installed
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 📝 License
|
|
91
|
+
|
|
92
|
+
Distributed under the [MIT License](LICENSE). Feel free to use, modify, and contribute.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
For any questions, suggestions, or issues, feel free to open a [GitHub issue](https://www.github.com/BytesTheCookies/CookieFarmExploiter)!
|
|
97
|
+
|
|
98
|
+
**Created with ❤️ by ByteTheCookies (feat. @0xMatte)**
|
|
Binary file
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import subprocess
|
|
4
|
+
|
|
5
|
+
def main():
|
|
6
|
+
base_dir = os.path.dirname(__file__)
|
|
7
|
+
binary_path = os.path.join(base_dir, "bin", "ckc")
|
|
8
|
+
|
|
9
|
+
if not os.path.exists(binary_path):
|
|
10
|
+
print(f"Binary not found at {binary_path}")
|
|
11
|
+
exit(1)
|
|
12
|
+
|
|
13
|
+
subprocess.run([binary_path] + sys.argv[1:])
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class ExploitConfigInfo:
|
|
8
|
+
server_address: str
|
|
9
|
+
tick_time: int
|
|
10
|
+
thread_number: int
|
|
11
|
+
port_service: int
|
|
12
|
+
name_service: str
|
|
13
|
+
test_mode: bool
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def parse_exploit_config() -> ExploitConfigInfo:
|
|
17
|
+
"""
|
|
18
|
+
Parse the exploit configuration from command line arguments.
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
ExploitConfigInfo: The parsed configuration.
|
|
22
|
+
"""
|
|
23
|
+
parser = argparse.ArgumentParser(
|
|
24
|
+
description="Advanced Exploit Manager per CTF Attack/Defense"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
parser.add_argument(
|
|
28
|
+
"-s",
|
|
29
|
+
"--server_address",
|
|
30
|
+
type=str,
|
|
31
|
+
help="Server address in the format <ip>:<port>",
|
|
32
|
+
required=True,
|
|
33
|
+
)
|
|
34
|
+
parser.add_argument(
|
|
35
|
+
"-t",
|
|
36
|
+
"--tick_time",
|
|
37
|
+
type=int,
|
|
38
|
+
help="Time in seconds between each exploiting phase attempt",
|
|
39
|
+
default=120,
|
|
40
|
+
)
|
|
41
|
+
parser.add_argument(
|
|
42
|
+
"-T",
|
|
43
|
+
"--thread_number",
|
|
44
|
+
type=int,
|
|
45
|
+
help="Number of threads to use for exploits",
|
|
46
|
+
default=30,
|
|
47
|
+
)
|
|
48
|
+
parser.add_argument(
|
|
49
|
+
"-p",
|
|
50
|
+
"--port_service",
|
|
51
|
+
type=int,
|
|
52
|
+
help="Port number of the service to exploit",
|
|
53
|
+
required=True,
|
|
54
|
+
)
|
|
55
|
+
parser.add_argument(
|
|
56
|
+
"-n",
|
|
57
|
+
"--name_service",
|
|
58
|
+
type=str,
|
|
59
|
+
help="Name of the service to exploit",
|
|
60
|
+
default="UnknownService",
|
|
61
|
+
)
|
|
62
|
+
parser.add_argument(
|
|
63
|
+
"-x",
|
|
64
|
+
"--test",
|
|
65
|
+
action="store_true",
|
|
66
|
+
help="Run in test mode, exploit ONLY the NOP team",
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
args = parser.parse_args()
|
|
70
|
+
|
|
71
|
+
return ExploitConfigInfo(
|
|
72
|
+
server_address=args.server_address,
|
|
73
|
+
tick_time=args.tick_time,
|
|
74
|
+
thread_number=args.thread_number,
|
|
75
|
+
port_service=args.port_service,
|
|
76
|
+
name_service=args.name_service,
|
|
77
|
+
test_mode=args.test if args.test else False,
|
|
78
|
+
)
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import time
|
|
3
|
+
|
|
4
|
+
from itertools import count
|
|
5
|
+
from typing import Callable
|
|
6
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
7
|
+
from .logger import StatusCode, log_stats, log_status
|
|
8
|
+
from threading import Lock
|
|
9
|
+
|
|
10
|
+
class ExploitExecutor:
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
exploit_function: Callable,
|
|
14
|
+
teams: dict[int, str],
|
|
15
|
+
port_service: int,
|
|
16
|
+
name_service: str,
|
|
17
|
+
flag_regex: re.Pattern,
|
|
18
|
+
tick_time: int = 120,
|
|
19
|
+
thread_number: int = 30,
|
|
20
|
+
):
|
|
21
|
+
self.exploit_function = exploit_function
|
|
22
|
+
self.teams = teams
|
|
23
|
+
self.port_service = port_service
|
|
24
|
+
self.name_service = name_service
|
|
25
|
+
self.flag_regex = flag_regex
|
|
26
|
+
self.tick_time = tick_time
|
|
27
|
+
self.thread_number = thread_number
|
|
28
|
+
self.locker = Lock()
|
|
29
|
+
self.stat = {
|
|
30
|
+
"success_team": 0,
|
|
31
|
+
"failed_team": 0,
|
|
32
|
+
"total_flag": 0,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
def __extract_flag(self, source: str | list[str]) -> set[str]:
|
|
36
|
+
if isinstance(source, list):
|
|
37
|
+
if len(source) == 0 or any(isinstance(item, str) for item in source):
|
|
38
|
+
return set()
|
|
39
|
+
|
|
40
|
+
return set(self.flag_regex.findall("\n".join(source)))
|
|
41
|
+
|
|
42
|
+
elif isinstance(source, str):
|
|
43
|
+
return set(self.flag_regex.findall(source))
|
|
44
|
+
|
|
45
|
+
return set()
|
|
46
|
+
|
|
47
|
+
def __single_exploit_execution(self, team_id: int, ip: str):
|
|
48
|
+
try:
|
|
49
|
+
result = self.exploit_function(ip, self.port_service, self.name_service)
|
|
50
|
+
flags = self.__extract_flag(result)
|
|
51
|
+
|
|
52
|
+
with self.locker:
|
|
53
|
+
if flags:
|
|
54
|
+
self.stat["success_team"] += 1
|
|
55
|
+
self.stat["total_flag"] += len(flags)
|
|
56
|
+
else:
|
|
57
|
+
self.stat["failed_team"] += 1
|
|
58
|
+
|
|
59
|
+
return flags
|
|
60
|
+
|
|
61
|
+
except Exception as e:
|
|
62
|
+
log_status(
|
|
63
|
+
StatusCode.ERROR,
|
|
64
|
+
f"Error while exploiting team {team_id} ({ip}:{self.port_service}): {str(e)}",
|
|
65
|
+
team_id=team_id,
|
|
66
|
+
port_service=self.port_service,
|
|
67
|
+
name_service=self.name_service,
|
|
68
|
+
)
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
def get_stat(self) -> dict[str, int]:
|
|
72
|
+
"""
|
|
73
|
+
Returns the current statistics of the exploit execution.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
dict[str, int]: A dictionary containing the statistics.
|
|
77
|
+
"""
|
|
78
|
+
with self.locker:
|
|
79
|
+
|
|
80
|
+
current_stat = self.stat.copy()
|
|
81
|
+
self.stat = {
|
|
82
|
+
"success_team": 0,
|
|
83
|
+
"failed_team": 0,
|
|
84
|
+
"total_flag": 0,
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return current_stat
|
|
88
|
+
|
|
89
|
+
def __process_exploits(self):
|
|
90
|
+
|
|
91
|
+
with ThreadPoolExecutor(
|
|
92
|
+
max_workers=min(self.thread_number, len(self.teams)),
|
|
93
|
+
thread_name_prefix="exploit",
|
|
94
|
+
) as executor:
|
|
95
|
+
|
|
96
|
+
futures_to_team = {
|
|
97
|
+
executor.submit(
|
|
98
|
+
self.__single_exploit_execution,
|
|
99
|
+
team_id,
|
|
100
|
+
ip,
|
|
101
|
+
): team_id
|
|
102
|
+
for team_id, ip in self.teams.items()
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
for future in as_completed(futures_to_team):
|
|
106
|
+
team_id = futures_to_team[future]
|
|
107
|
+
try:
|
|
108
|
+
flags = future.result()
|
|
109
|
+
|
|
110
|
+
if flags:
|
|
111
|
+
for flag in flags:
|
|
112
|
+
log_status(
|
|
113
|
+
StatusCode.SUCCESS,
|
|
114
|
+
f"Flag found for team {team_id}",
|
|
115
|
+
team_id=team_id,
|
|
116
|
+
port_service=self.port_service,
|
|
117
|
+
flag_code=flag,
|
|
118
|
+
name_service=self.name_service,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
except Exception as e:
|
|
122
|
+
log_status(
|
|
123
|
+
StatusCode.ERROR,
|
|
124
|
+
f"Error while processing team {team_id}: {str(e)}",
|
|
125
|
+
team_id=team_id,
|
|
126
|
+
port_service=self.port_service,
|
|
127
|
+
name_service=self.name_service,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def execute(self):
|
|
131
|
+
|
|
132
|
+
round = count(1)
|
|
133
|
+
|
|
134
|
+
while True:
|
|
135
|
+
start_time = time.time()
|
|
136
|
+
|
|
137
|
+
self.__process_exploits()
|
|
138
|
+
|
|
139
|
+
log_stats(
|
|
140
|
+
f"Round {next(round)} - statistics",
|
|
141
|
+
port_service=self.port_service,
|
|
142
|
+
name_service=self.name_service,
|
|
143
|
+
stats=self.get_stat(),
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
elapsed_time = time.time() - start_time
|
|
147
|
+
time.sleep(max(0, self.tick_time - elapsed_time))
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Exploit Manager
|
|
3
|
+
This module manages the exploit process by handling authentication,
|
|
4
|
+
configuration retrieval, and dispatching exploits to the target IP teams.
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import io
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
from threading import local
|
|
12
|
+
from .server_config import ServerConfig
|
|
13
|
+
from .test_executor import TestExploitExecutor
|
|
14
|
+
from .exploit_executor import ExploitExecutor
|
|
15
|
+
from .exploit_config import parse_exploit_config
|
|
16
|
+
|
|
17
|
+
thread_local = local()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ThreadSafeStdout:
|
|
21
|
+
"""
|
|
22
|
+
A thread-safe wrapper for standard output to ensure that
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def write(self, text):
|
|
26
|
+
return self._get_buffer().write(text)
|
|
27
|
+
|
|
28
|
+
def _get_buffer(self):
|
|
29
|
+
return getattr(thread_local, "buf", sys.__stdout__)
|
|
30
|
+
|
|
31
|
+
def __getattr__(self, attr):
|
|
32
|
+
return getattr(self._get_buffer(), attr)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# Redirect standard output to the thread-local buffer
|
|
36
|
+
sys.stdout = ThreadSafeStdout()
|
|
37
|
+
|
|
38
|
+
def exploit_manager(func):
|
|
39
|
+
|
|
40
|
+
def wrapper(*_args, **kwargs):
|
|
41
|
+
|
|
42
|
+
def exploit_function_wrapper(*args, **kwargs):
|
|
43
|
+
buf = io.StringIO()
|
|
44
|
+
thread_local.buf = buf
|
|
45
|
+
|
|
46
|
+
try:
|
|
47
|
+
func(*args, **kwargs)
|
|
48
|
+
finally:
|
|
49
|
+
del thread_local.buf
|
|
50
|
+
out = buf.getvalue()
|
|
51
|
+
buf.close()
|
|
52
|
+
return out
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
exploit_config = parse_exploit_config()
|
|
56
|
+
server_config = ServerConfig(
|
|
57
|
+
server_address=exploit_config.server_address
|
|
58
|
+
).config()
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
if exploit_config.test_mode:
|
|
62
|
+
TestExploitExecutor(
|
|
63
|
+
exploit_function=exploit_function_wrapper,
|
|
64
|
+
port_service=exploit_config.port_service,
|
|
65
|
+
name_service=exploit_config.name_service,
|
|
66
|
+
flag_regex=server_config.regex_flag,
|
|
67
|
+
nop_team=server_config.nop_team,
|
|
68
|
+
).execute()
|
|
69
|
+
else:
|
|
70
|
+
ExploitExecutor(
|
|
71
|
+
exploit_function=exploit_function_wrapper,
|
|
72
|
+
flag_regex=server_config.regex_flag,
|
|
73
|
+
teams=server_config.teams,
|
|
74
|
+
port_service=exploit_config.port_service,
|
|
75
|
+
name_service=exploit_config.name_service,
|
|
76
|
+
tick_time=exploit_config.tick_time,
|
|
77
|
+
thread_number=exploit_config.thread_number,
|
|
78
|
+
).execute()
|
|
79
|
+
except KeyboardInterrupt:
|
|
80
|
+
sys.exit(0)
|
|
81
|
+
|
|
82
|
+
return wrapper()
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Logger module for logging status messages.
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
from enum import Enum
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class StatusCode(Enum):
|
|
11
|
+
"""Enumeration of possible status codes for logging."""
|
|
12
|
+
|
|
13
|
+
SUCCESS = "success" # Case: get flag successfully
|
|
14
|
+
ERROR = "error" # Case: get flag unsuccessfully [error in exploit returns an error]
|
|
15
|
+
FAILED = "failed" # Case: not get the flag [exploit returns no flag]
|
|
16
|
+
FATAL = "fatal" # Case: fatal error that requires immediate attention. Stop script's execution
|
|
17
|
+
INFO = "info" # Case: informative message
|
|
18
|
+
STATS = "stats" # Case: statistics message
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def log_status(
|
|
22
|
+
status: StatusCode,
|
|
23
|
+
message: str,
|
|
24
|
+
team_id: int = 0,
|
|
25
|
+
port_service: int = 0,
|
|
26
|
+
flag_code: str = "",
|
|
27
|
+
name_service: str = "",
|
|
28
|
+
) -> bool:
|
|
29
|
+
"""
|
|
30
|
+
Logs a status message with additional information.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
status (StatusCode): The status code to log.
|
|
34
|
+
message (str): The message to log.
|
|
35
|
+
team_id (int, optional): The ID of the team. Defaults to 0.
|
|
36
|
+
port_service (int, optional): The port number of the service. Defaults to 0.
|
|
37
|
+
flag_code (str, optional): The flag code. Defaults to "".
|
|
38
|
+
name_service (str, optional): The name of the service. Defaults to "".
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
bool: True if the status was logged successfully, False otherwise.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
if not isinstance(status, StatusCode):
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
print(
|
|
48
|
+
json.dumps(
|
|
49
|
+
{
|
|
50
|
+
"status": status.value,
|
|
51
|
+
"message": message,
|
|
52
|
+
"team_id": team_id,
|
|
53
|
+
"port_service": port_service,
|
|
54
|
+
"flag_code": flag_code,
|
|
55
|
+
"name_service": name_service,
|
|
56
|
+
}
|
|
57
|
+
),
|
|
58
|
+
flush=True,
|
|
59
|
+
)
|
|
60
|
+
return True
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def log_stats(
|
|
64
|
+
message: str,
|
|
65
|
+
port_service: int,
|
|
66
|
+
name_service: str,
|
|
67
|
+
stats: dict[str, int]
|
|
68
|
+
):
|
|
69
|
+
|
|
70
|
+
"""
|
|
71
|
+
Logs statistics message.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
message (str): The message to log.
|
|
75
|
+
port_service (int): The port number of the service.
|
|
76
|
+
name_service (str): The name of the service.
|
|
77
|
+
stats (dict[str, int]): A dictionary containing statistics.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
bool: True if the statistics were logged successfully, False otherwise.
|
|
81
|
+
"""
|
|
82
|
+
print(
|
|
83
|
+
json.dumps(
|
|
84
|
+
{
|
|
85
|
+
"status": StatusCode.STATS.value,
|
|
86
|
+
"message": message,
|
|
87
|
+
"port_service": port_service,
|
|
88
|
+
"name_service": name_service,
|
|
89
|
+
"total_flag": stats.get("total_flag", 0),
|
|
90
|
+
"success_team": stats.get("success_team", 0),
|
|
91
|
+
"failed_team": stats.get("failed_team", 0),
|
|
92
|
+
}
|
|
93
|
+
),
|
|
94
|
+
flush=True,
|
|
95
|
+
)
|
|
96
|
+
return True
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import sys
|
|
3
|
+
import requests
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
from array import array
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from .logger import StatusCode, log_status
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class ServerConfigInfo:
|
|
14
|
+
regex_flag: re.Pattern
|
|
15
|
+
teams: dict[int, str]
|
|
16
|
+
nop_team: tuple[int, str]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ServerConfig:
|
|
20
|
+
def __init__(self, server_address: str, config_api_path: str = "/api/v1/config"):
|
|
21
|
+
self.server_address = server_address
|
|
22
|
+
self.config_api_path = config_api_path
|
|
23
|
+
self.raw_config = self.__get_raw_config()
|
|
24
|
+
|
|
25
|
+
def __get_auth_token(self) -> str:
|
|
26
|
+
"""
|
|
27
|
+
Get the authentication token from the session file.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
str: The authentication token.
|
|
31
|
+
"""
|
|
32
|
+
session_path = Path.home().joinpath(".config", "cookiefarm", "session")
|
|
33
|
+
|
|
34
|
+
if not session_path.exists():
|
|
35
|
+
log_status(StatusCode.FATAL, "Session file not found. Please login first.")
|
|
36
|
+
sys.exit(1)
|
|
37
|
+
|
|
38
|
+
with open(session_path) as session_file:
|
|
39
|
+
token = session_file.read().strip()
|
|
40
|
+
|
|
41
|
+
return token
|
|
42
|
+
|
|
43
|
+
def __get_raw_config(self) -> dict[str, Any]:
|
|
44
|
+
"""
|
|
45
|
+
Get the configuration from the server.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
dict[str, Any]: The configuration dictionary.
|
|
49
|
+
Raises:
|
|
50
|
+
ValueError: If the response from the server is not ok.
|
|
51
|
+
"""
|
|
52
|
+
response = requests.get(
|
|
53
|
+
f"http://{self.server_address}{self.config_api_path}",
|
|
54
|
+
headers={
|
|
55
|
+
"Content-Type": "application/json",
|
|
56
|
+
"Cookie": f"token={self.__get_auth_token()}",
|
|
57
|
+
},
|
|
58
|
+
)
|
|
59
|
+
if not response.ok:
|
|
60
|
+
log_status(
|
|
61
|
+
StatusCode.FATAL, "Failed to retrieve configuration from the server."
|
|
62
|
+
)
|
|
63
|
+
sys.exit(1)
|
|
64
|
+
|
|
65
|
+
return response.json()
|
|
66
|
+
|
|
67
|
+
def config(self) -> ServerConfigInfo:
|
|
68
|
+
"""
|
|
69
|
+
Get the configuration information from the server.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
ServerConfigInfo: A ServerConfigInfo object containing regex_flag and teams.
|
|
73
|
+
Raises:
|
|
74
|
+
ValueError: If the response from the server is not ok.
|
|
75
|
+
"""
|
|
76
|
+
config_json = self.raw_config
|
|
77
|
+
|
|
78
|
+
regex_flag = re.compile(config_json["client"]["regex_flag"])
|
|
79
|
+
format_ip_teams: str = config_json["client"]["format_ip_teams"]
|
|
80
|
+
my_team = int(config_json["client"]["my_team_id"])
|
|
81
|
+
nop_team: int = int(config_json["client"]["nop_team"])
|
|
82
|
+
|
|
83
|
+
if my_team < nop_team:
|
|
84
|
+
x_1 = my_team
|
|
85
|
+
x_2 = nop_team
|
|
86
|
+
else:
|
|
87
|
+
x_1 = nop_team
|
|
88
|
+
x_2 = my_team
|
|
89
|
+
|
|
90
|
+
ip_teams = array("H", range(x_1))
|
|
91
|
+
|
|
92
|
+
for i in range(
|
|
93
|
+
x_1 + 1, x_2
|
|
94
|
+
):
|
|
95
|
+
ip_teams.append(i)
|
|
96
|
+
|
|
97
|
+
for i in range(
|
|
98
|
+
x_2 + 1, int(config_json["client"]["range_ip_teams"]) + 1
|
|
99
|
+
):
|
|
100
|
+
ip_teams.append(i)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
for i in range(
|
|
104
|
+
my_team + 1, int(config_json["client"]["range_ip_teams"]) + 1
|
|
105
|
+
):
|
|
106
|
+
ip_teams.append(i)
|
|
107
|
+
|
|
108
|
+
return ServerConfigInfo(
|
|
109
|
+
re.compile(regex_flag),
|
|
110
|
+
{i: format_ip_teams.format(i) for i in ip_teams},
|
|
111
|
+
(nop_team, format_ip_teams.format(nop_team))
|
|
112
|
+
)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from typing import Callable
|
|
4
|
+
from .logger import StatusCode, log_status
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TestExploitExecutor:
|
|
8
|
+
def __init__(
|
|
9
|
+
self,
|
|
10
|
+
exploit_function: Callable,
|
|
11
|
+
port_service: int,
|
|
12
|
+
name_service: str,
|
|
13
|
+
flag_regex: re.Pattern,
|
|
14
|
+
nop_team: tuple[int, str],
|
|
15
|
+
):
|
|
16
|
+
self.exploit_function = exploit_function
|
|
17
|
+
self.port_service = port_service
|
|
18
|
+
self.name_service = name_service
|
|
19
|
+
self.flag_regex = flag_regex
|
|
20
|
+
self.nop_team = nop_team
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def __extract_flag(self, source: str | list[str]) -> set[str]:
|
|
24
|
+
if isinstance(source, list):
|
|
25
|
+
if len(source) == 0 or any(isinstance(item, str) for item in source):
|
|
26
|
+
return set()
|
|
27
|
+
|
|
28
|
+
return set(self.flag_regex.findall("\n".join(source)))
|
|
29
|
+
|
|
30
|
+
elif isinstance(source, str):
|
|
31
|
+
return set(self.flag_regex.findall(source))
|
|
32
|
+
|
|
33
|
+
return set()
|
|
34
|
+
|
|
35
|
+
def __single_exploit_execution(
|
|
36
|
+
self,
|
|
37
|
+
team_id: int,
|
|
38
|
+
ip: str,
|
|
39
|
+
):
|
|
40
|
+
try:
|
|
41
|
+
result = self.exploit_function(ip, self.port_service, self.name_service)
|
|
42
|
+
return self.__extract_flag(result)
|
|
43
|
+
|
|
44
|
+
except Exception as e:
|
|
45
|
+
log_status(
|
|
46
|
+
StatusCode.ERROR,
|
|
47
|
+
f"Error while exploiting team {team_id} ({ip}:{self.port_service}): {str(e)}",
|
|
48
|
+
team_id=team_id,
|
|
49
|
+
port_service=self.port_service,
|
|
50
|
+
name_service=self.name_service,
|
|
51
|
+
)
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
def execute(self):
|
|
55
|
+
flags = self.__single_exploit_execution(self.nop_team[0], self.nop_team[1])
|
|
56
|
+
|
|
57
|
+
if flags:
|
|
58
|
+
for flag in flags:
|
|
59
|
+
log_status(
|
|
60
|
+
StatusCode.SUCCESS,
|
|
61
|
+
f"Flag found for team {self.nop_team[0]}: {flag}",
|
|
62
|
+
team_id=self.nop_team[0],
|
|
63
|
+
port_service=self.port_service,
|
|
64
|
+
name_service=self.name_service,
|
|
65
|
+
flag_code=flag,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
log_status(
|
|
69
|
+
StatusCode.INFO,
|
|
70
|
+
message="Exploit test completed successfully",
|
|
71
|
+
team_id=self.nop_team[0],
|
|
72
|
+
port_service=self.port_service,
|
|
73
|
+
name_service=self.name_service,
|
|
74
|
+
)
|
|
75
|
+
else:
|
|
76
|
+
log_status(
|
|
77
|
+
StatusCode.FAILED,
|
|
78
|
+
f"No flags found for team {self.nop_team[0]}",
|
|
79
|
+
team_id=self.nop_team[0],
|
|
80
|
+
port_service=self.port_service,
|
|
81
|
+
name_service=self.name_service,
|
|
82
|
+
)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "cookiefarm"
|
|
7
|
+
version = "1.0.2"
|
|
8
|
+
description = "Python decorator for parallel exploit dispatch in Attack & Defense CTFs using the CookieFarm framework."
|
|
9
|
+
authors = [{ name = "ByteTheCookies", email = "bytethecookies@proton.me" }]
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
requires-python = ">=3.12"
|
|
12
|
+
license = { file = "LICENSE" }
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
"Operating System :: POSIX :: Linux",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[project.urls]
|
|
20
|
+
Homepage = "https://github.com/ByteTheCookies/CookieFarmExploiter"
|
|
21
|
+
|
|
22
|
+
[tool.hatch.build.targets.sdist]
|
|
23
|
+
include = [
|
|
24
|
+
"cookiefarm",
|
|
25
|
+
"README.md",
|
|
26
|
+
"LICENSE",
|
|
27
|
+
"cookiefarm/bin/*"
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[tool.hatch.metadata]
|
|
31
|
+
allow-direct-references = true
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
[tool.hatch.build]
|
|
35
|
+
include = [
|
|
36
|
+
"cookiefarm/bin/*"
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[tool.hatch.build.targets.wheel]
|
|
40
|
+
packages = ["cookiefarm"]
|
|
41
|
+
|
|
42
|
+
[project.scripts]
|
|
43
|
+
ckc = "cookiefarm.cli:main"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/home/suga/Workspace/Projects/CookieFarmExploiter/venv_1/bin/python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
import re
|
|
4
|
+
import sys
|
|
5
|
+
from cookiefarm.cli import main
|
|
6
|
+
if __name__ == '__main__':
|
|
7
|
+
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
|
8
|
+
sys.exit(main())
|