exploitfarm 1.7.5__tar.gz → 1.7.7__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.
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/PKG-INFO +23 -9
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/README.md +22 -8
- exploitfarm-1.7.7/exploitfarm/__init__.py +91 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/_init_exposed.py +53 -15
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/models/flags.py +1 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/xfarm.py +18 -17
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/xploit.py +0 -3
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm.egg-info/PKG-INFO +23 -9
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/setup.py +1 -1
- exploitfarm-1.7.5/exploitfarm/__init__.py +0 -29
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/MANIFEST.in +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/__main__.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/models/__init__.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/models/client.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/models/dbtypes.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/models/enums.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/models/exploit.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/models/exploit_template.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/models/groups.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/models/response.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/models/service.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/models/submitter.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/models/teams.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/tui/__init__.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/tui/config.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/tui/exploitdownload.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/tui/exploitinit.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/tui/group_prejoin.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/tui/group_select.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/tui/login.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/tui/shared_attack_group.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/tui/startxploit.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/tui/template.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/utils/__init__.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/utils/cmd.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/utils/config.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/utils/exploit.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/utils/reqs.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/utils/templates.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm/utils/windows_close_fix.py +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm.egg-info/SOURCES.txt +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm.egg-info/dependency_links.txt +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm.egg-info/requires.txt +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/exploitfarm.egg-info/top_level.txt +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/requirements.txt +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/setup.cfg +0 -0
- {exploitfarm-1.7.5 → exploitfarm-1.7.7}/xfarm +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: exploitfarm
|
|
3
|
-
Version: 1.7.
|
|
3
|
+
Version: 1.7.7
|
|
4
4
|
Summary: Exploit Farm client
|
|
5
5
|
Home-page: https://github.com/pwnzer0tt1/exploitfarm
|
|
6
6
|
Author: Pwnzer0tt1
|
|
@@ -98,15 +98,18 @@ submit_flags(flags)
|
|
|
98
98
|
#### **Store Class**
|
|
99
99
|
|
|
100
100
|
The `Store` class provides a key-value storage system for exploits, allowing you to save and retrieve centralized data during execution.
|
|
101
|
-
The key value store is
|
|
101
|
+
The key value store is by default isolated per exploit id.
|
|
102
|
+
If you need data shared between exploits you should use a custom context_id that allows to access the same data from different exploits. The context_id can be set also in the constructor of the Store class, and will be used as default context_id for all the calls with that store instance.
|
|
102
103
|
|
|
103
104
|
```python
|
|
104
105
|
from exploitfarm import Store
|
|
105
106
|
|
|
106
|
-
|
|
107
|
+
# Initialize with an optional default context
|
|
108
|
+
store = Store(default_context_id="my_global_context_id")
|
|
107
109
|
|
|
108
|
-
# Set a value
|
|
110
|
+
# Set a value (context_id can be overridden per call)
|
|
109
111
|
store.set("example_key", {"example": "data"})
|
|
112
|
+
store.set("other_key", "value", context_id="explicit_ctx")
|
|
110
113
|
|
|
111
114
|
# Get a value (if not exists returns None)
|
|
112
115
|
data = store.get("example_key")
|
|
@@ -115,17 +118,28 @@ print(data)
|
|
|
115
118
|
# Delete a key
|
|
116
119
|
store.delete("example_key")
|
|
117
120
|
|
|
118
|
-
# List all keys
|
|
121
|
+
# List all keys in the current context
|
|
119
122
|
keys = store.keys()
|
|
120
123
|
print(keys)
|
|
124
|
+
|
|
125
|
+
# List all available contexts
|
|
126
|
+
contexts = store.contexts()
|
|
127
|
+
print(contexts)
|
|
128
|
+
|
|
129
|
+
# Delete an entire context
|
|
130
|
+
store.delete_context(context_id="explicit_ctx")
|
|
121
131
|
```
|
|
122
132
|
|
|
123
133
|
**Methods**:
|
|
124
134
|
|
|
125
|
-
- `get(key: str, timeout: int = HTTP_TIMEOUT) ->
|
|
126
|
-
- `
|
|
127
|
-
- `
|
|
128
|
-
- `
|
|
135
|
+
- `get(key: str, default=None, context_id: str | None = None, timeout: int = HTTP_TIMEOUT) -> any`: Retrieve and deserialize a value by key.
|
|
136
|
+
- `get_bytes(key: str, default=None, context_id: str | None = None, timeout: int = HTTP_TIMEOUT) -> bytes`: Retrieve a raw byte value.
|
|
137
|
+
- `set(key: str, value: any, context_id: str | None = None, timeout: int = HTTP_TIMEOUT)`: Serialize and store a value by key.
|
|
138
|
+
- `set_bytes(key: str, value: bytes, context_id: str | None = None, timeout: int = HTTP_TIMEOUT)`: Store a raw byte value.
|
|
139
|
+
- `delete(key: str, context_id: str | None = None, timeout: int = HTTP_TIMEOUT)`: Delete a key.
|
|
140
|
+
- `keys(timeout: int = HTTP_TIMEOUT, context_id: str | None = None) -> list[str]`: List all stored keys in a context.
|
|
141
|
+
- `contexts(timeout: int = HTTP_TIMEOUT) -> list[str]`: List all available contexts.
|
|
142
|
+
- `delete_context(context_id: str | None = None, timeout: int = HTTP_TIMEOUT)`: Delete an entire context and its keys.
|
|
129
143
|
|
|
130
144
|
**Environment Variables Required**:
|
|
131
145
|
|
|
@@ -61,15 +61,18 @@ submit_flags(flags)
|
|
|
61
61
|
#### **Store Class**
|
|
62
62
|
|
|
63
63
|
The `Store` class provides a key-value storage system for exploits, allowing you to save and retrieve centralized data during execution.
|
|
64
|
-
The key value store is
|
|
64
|
+
The key value store is by default isolated per exploit id.
|
|
65
|
+
If you need data shared between exploits you should use a custom context_id that allows to access the same data from different exploits. The context_id can be set also in the constructor of the Store class, and will be used as default context_id for all the calls with that store instance.
|
|
65
66
|
|
|
66
67
|
```python
|
|
67
68
|
from exploitfarm import Store
|
|
68
69
|
|
|
69
|
-
|
|
70
|
+
# Initialize with an optional default context
|
|
71
|
+
store = Store(default_context_id="my_global_context_id")
|
|
70
72
|
|
|
71
|
-
# Set a value
|
|
73
|
+
# Set a value (context_id can be overridden per call)
|
|
72
74
|
store.set("example_key", {"example": "data"})
|
|
75
|
+
store.set("other_key", "value", context_id="explicit_ctx")
|
|
73
76
|
|
|
74
77
|
# Get a value (if not exists returns None)
|
|
75
78
|
data = store.get("example_key")
|
|
@@ -78,17 +81,28 @@ print(data)
|
|
|
78
81
|
# Delete a key
|
|
79
82
|
store.delete("example_key")
|
|
80
83
|
|
|
81
|
-
# List all keys
|
|
84
|
+
# List all keys in the current context
|
|
82
85
|
keys = store.keys()
|
|
83
86
|
print(keys)
|
|
87
|
+
|
|
88
|
+
# List all available contexts
|
|
89
|
+
contexts = store.contexts()
|
|
90
|
+
print(contexts)
|
|
91
|
+
|
|
92
|
+
# Delete an entire context
|
|
93
|
+
store.delete_context(context_id="explicit_ctx")
|
|
84
94
|
```
|
|
85
95
|
|
|
86
96
|
**Methods**:
|
|
87
97
|
|
|
88
|
-
- `get(key: str, timeout: int = HTTP_TIMEOUT) ->
|
|
89
|
-
- `
|
|
90
|
-
- `
|
|
91
|
-
- `
|
|
98
|
+
- `get(key: str, default=None, context_id: str | None = None, timeout: int = HTTP_TIMEOUT) -> any`: Retrieve and deserialize a value by key.
|
|
99
|
+
- `get_bytes(key: str, default=None, context_id: str | None = None, timeout: int = HTTP_TIMEOUT) -> bytes`: Retrieve a raw byte value.
|
|
100
|
+
- `set(key: str, value: any, context_id: str | None = None, timeout: int = HTTP_TIMEOUT)`: Serialize and store a value by key.
|
|
101
|
+
- `set_bytes(key: str, value: bytes, context_id: str | None = None, timeout: int = HTTP_TIMEOUT)`: Store a raw byte value.
|
|
102
|
+
- `delete(key: str, context_id: str | None = None, timeout: int = HTTP_TIMEOUT)`: Delete a key.
|
|
103
|
+
- `keys(timeout: int = HTTP_TIMEOUT, context_id: str | None = None) -> list[str]`: List all stored keys in a context.
|
|
104
|
+
- `contexts(timeout: int = HTTP_TIMEOUT) -> list[str]`: List all available contexts.
|
|
105
|
+
- `delete_context(context_id: str | None = None, timeout: int = HTTP_TIMEOUT)`: Delete an entire context and its keys.
|
|
92
106
|
|
|
93
107
|
**Environment Variables Required**:
|
|
94
108
|
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
def _get_version():
|
|
2
|
+
v = "1.7.7" if "{" not in "1.7.7" else "0.0.0"
|
|
3
|
+
if v == "0.0.0":
|
|
4
|
+
import os
|
|
5
|
+
env_v = os.getenv("EXPLOITFARM_VERSION")
|
|
6
|
+
if env_v:
|
|
7
|
+
return env_v
|
|
8
|
+
import re
|
|
9
|
+
|
|
10
|
+
def parse_version(ver):
|
|
11
|
+
ver = ver.lstrip("v")
|
|
12
|
+
parts = re.split(r'[^0-9]+', ver)
|
|
13
|
+
return tuple(int(p) if p.isdigit() else 0 for p in parts if p)
|
|
14
|
+
|
|
15
|
+
def find_git_dir(start_path):
|
|
16
|
+
current = start_path
|
|
17
|
+
while current and current != "/" and current != "":
|
|
18
|
+
gd = os.path.join(current, ".git")
|
|
19
|
+
if os.path.isdir(gd):
|
|
20
|
+
return gd
|
|
21
|
+
parent = os.path.dirname(current)
|
|
22
|
+
if parent == current:
|
|
23
|
+
break
|
|
24
|
+
current = parent
|
|
25
|
+
return None
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
git_dir = find_git_dir(os.path.abspath(os.path.dirname(__file__)))
|
|
29
|
+
if not git_dir:
|
|
30
|
+
return v
|
|
31
|
+
|
|
32
|
+
branch = None
|
|
33
|
+
with open(os.path.join(git_dir, "HEAD"), "r") as f:
|
|
34
|
+
head = f.read().strip()
|
|
35
|
+
if head.startswith("ref: refs/heads/"):
|
|
36
|
+
branch = head.split("ref: refs/heads/")[1]
|
|
37
|
+
|
|
38
|
+
if branch != "main":
|
|
39
|
+
return v
|
|
40
|
+
|
|
41
|
+
tags = []
|
|
42
|
+
try:
|
|
43
|
+
with open(os.path.join(git_dir, "packed-refs"), "r") as f:
|
|
44
|
+
for line in f:
|
|
45
|
+
if " refs/tags/" in line:
|
|
46
|
+
tags.append(line.strip().split(" refs/tags/")[1])
|
|
47
|
+
except Exception:
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
tags_dir = os.path.join(git_dir, "refs", "tags")
|
|
51
|
+
try:
|
|
52
|
+
for root, _, files in os.walk(tags_dir):
|
|
53
|
+
for file in files:
|
|
54
|
+
tags.append(os.path.relpath(os.path.join(root, file), tags_dir).replace(os.path.sep, "/"))
|
|
55
|
+
except Exception:
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
version_tags = [t for t in set(tags) if any(c.isdigit() for c in t)]
|
|
59
|
+
if version_tags:
|
|
60
|
+
return max(version_tags, key=parse_version)
|
|
61
|
+
except Exception:
|
|
62
|
+
pass
|
|
63
|
+
return v
|
|
64
|
+
|
|
65
|
+
__version__ = _get_version()
|
|
66
|
+
|
|
67
|
+
from exploitfarm.utils import try_tcp_connection
|
|
68
|
+
from exploitfarm.models.enums import AttackMode, SetupStatus, FlagStatus
|
|
69
|
+
from exploitfarm.utils import DEV_MODE
|
|
70
|
+
from exploitfarm._init_exposed import get_host, Prio
|
|
71
|
+
from exploitfarm._init_exposed import nicenessify, get_config, random_str, print, Store
|
|
72
|
+
from exploitfarm._init_exposed import os_unix, os_windows, session
|
|
73
|
+
|
|
74
|
+
# Exported functions
|
|
75
|
+
__all__ = [
|
|
76
|
+
"try_tcp_connection",
|
|
77
|
+
"AttackMode",
|
|
78
|
+
"SetupStatus",
|
|
79
|
+
"get_host",
|
|
80
|
+
"get_config",
|
|
81
|
+
"random_str",
|
|
82
|
+
"session",
|
|
83
|
+
"print",
|
|
84
|
+
"nicenessify",
|
|
85
|
+
"Prio",
|
|
86
|
+
"FlagStatus",
|
|
87
|
+
"Store",
|
|
88
|
+
"DEV_MODE",
|
|
89
|
+
"os_unix",
|
|
90
|
+
"os_windows",
|
|
91
|
+
]
|
|
@@ -147,7 +147,7 @@ print = functools.partial(print, flush=True)
|
|
|
147
147
|
|
|
148
148
|
|
|
149
149
|
class Store:
|
|
150
|
-
def __init__(self):
|
|
150
|
+
def __init__(self, default_context_id: str | None = None):
|
|
151
151
|
self.base_url = os.getenv("XFARM_REMOTE_URL", None)
|
|
152
152
|
if not self.base_url:
|
|
153
153
|
raise ValueError("this exploit has to be run with xfarm")
|
|
@@ -156,62 +156,100 @@ class Store:
|
|
|
156
156
|
raise ValueError("this exploit has to be run with xfarm")
|
|
157
157
|
self.login_token = os.getenv("XFARM_LOGIN_TOKEN", None)
|
|
158
158
|
self.session: Session = get_session(self.login_token)
|
|
159
|
-
|
|
160
|
-
|
|
159
|
+
if default_context_id is not None:
|
|
160
|
+
self._keycheck(default_context_id, "default_context_id")
|
|
161
|
+
self.default_context_id = default_context_id
|
|
162
|
+
|
|
163
|
+
def _get_context_id(self, context_id: str | None = None) -> str | None:
|
|
164
|
+
if context_id is not None:
|
|
165
|
+
self._keycheck(context_id, "context_id")
|
|
166
|
+
return context_id
|
|
167
|
+
if self.default_context_id is not None:
|
|
168
|
+
return self.default_context_id
|
|
169
|
+
return None
|
|
170
|
+
|
|
171
|
+
def _keycheck(self, key: str, what: str = "key"):
|
|
161
172
|
if not isinstance(key, str):
|
|
162
|
-
raise ValueError("
|
|
173
|
+
raise ValueError(f"{what} must be a string")
|
|
163
174
|
if len(key) > 255:
|
|
164
|
-
raise ValueError("
|
|
175
|
+
raise ValueError(f"{what} must be less than 255 characters")
|
|
165
176
|
if not re.match(r"^[A-Za-z0-9\-\._~()'!*:@,;]+$", key):
|
|
166
|
-
raise ValueError(
|
|
177
|
+
raise ValueError(rf"{what} must match this regex: ^[A-Za-z0-9\-\._~()'!*:@,;$]+$")
|
|
167
178
|
|
|
168
|
-
def get_bytes(self, key: str, default=None, timeout: int = HTTP_TIMEOUT) -> bytes:
|
|
179
|
+
def get_bytes(self, key: str, default=None, context_id: str | None = None, timeout: int = HTTP_TIMEOUT) -> bytes:
|
|
180
|
+
context_id = self._get_context_id(context_id)
|
|
169
181
|
self._keycheck(key)
|
|
170
182
|
r = self.session.get(
|
|
171
183
|
urljoin(self.base_url, f"exploits/{self.exploit_id}/store/key/{key}"),
|
|
184
|
+
params={"context_id": context_id} if context_id else None,
|
|
172
185
|
timeout=timeout,
|
|
173
186
|
)
|
|
174
187
|
if r.status_code == 404:
|
|
175
188
|
return default
|
|
176
189
|
return check_req(r).content
|
|
177
190
|
|
|
178
|
-
def get(self, key: str, default=None, timeout: int = HTTP_TIMEOUT) -> bytes:
|
|
191
|
+
def get(self, key: str, default=None, context_id: str | None = None, timeout: int = HTTP_TIMEOUT) -> bytes:
|
|
179
192
|
class TmpNotFound:
|
|
180
193
|
pass
|
|
181
|
-
|
|
182
|
-
data = self.get_bytes(key, default=TmpNotFound, timeout=timeout)
|
|
194
|
+
data = self.get_bytes(key, context_id=context_id, default=TmpNotFound, timeout=timeout)
|
|
183
195
|
if data == TmpNotFound:
|
|
184
196
|
return default
|
|
185
197
|
return pickle.loads(data)
|
|
186
198
|
|
|
187
|
-
def set_bytes(self, key: str, value: bytes, timeout: int = HTTP_TIMEOUT):
|
|
199
|
+
def set_bytes(self, key: str, value: bytes, context_id: str | None = None, timeout: int = HTTP_TIMEOUT):
|
|
188
200
|
self._keycheck(key)
|
|
201
|
+
context_id = self._get_context_id(context_id)
|
|
189
202
|
return requests_check_json(
|
|
190
203
|
self.session.put(
|
|
191
204
|
urljoin(self.base_url, f"exploits/{self.exploit_id}/store/key/{key}"),
|
|
192
205
|
data=value,
|
|
193
206
|
timeout=timeout,
|
|
194
207
|
headers={"Content-Type": "application/octet-stream"},
|
|
208
|
+
params={"context_id": context_id} if context_id else None,
|
|
195
209
|
)
|
|
196
210
|
)
|
|
197
211
|
|
|
198
|
-
def set(self, key: str, value, timeout: int = HTTP_TIMEOUT):
|
|
212
|
+
def set(self, key: str, value, context_id: str | None = None, timeout: int = HTTP_TIMEOUT):
|
|
199
213
|
value = pickle.dumps(value)
|
|
200
|
-
return self.set_bytes(key, value, timeout)
|
|
214
|
+
return self.set_bytes(key, value, context_id=context_id, timeout=timeout)
|
|
201
215
|
|
|
202
|
-
def delete(self, key: str, timeout: int = HTTP_TIMEOUT):
|
|
216
|
+
def delete(self, key: str, context_id: str | None = None, timeout: int = HTTP_TIMEOUT):
|
|
203
217
|
self._keycheck(key)
|
|
218
|
+
context_id = self._get_context_id(context_id)
|
|
204
219
|
return requests_check_json(
|
|
205
220
|
self.session.delete(
|
|
206
221
|
urljoin(self.base_url, f"exploits/{self.exploit_id}/store/key/{key}"),
|
|
222
|
+
params={"context_id": context_id} if context_id else None,
|
|
207
223
|
timeout=timeout,
|
|
208
224
|
)
|
|
209
225
|
)
|
|
210
226
|
|
|
211
|
-
def keys(self, timeout: int = HTTP_TIMEOUT) -> list[str]:
|
|
227
|
+
def keys(self, timeout: int = HTTP_TIMEOUT, context_id: str | None = None) -> list[str]:
|
|
228
|
+
context_id = self._get_context_id(context_id)
|
|
212
229
|
return requests_check_json(
|
|
213
230
|
self.session.get(
|
|
214
231
|
urljoin(self.base_url, f"exploits/{self.exploit_id}/store/keys"),
|
|
232
|
+
params={ "context_id": context_id } if context_id else None,
|
|
233
|
+
timeout=timeout,
|
|
234
|
+
)
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
def contexts(self, timeout: int = HTTP_TIMEOUT) -> list[str]:
|
|
238
|
+
return requests_check_json(
|
|
239
|
+
self.session.get(
|
|
240
|
+
urljoin(self.base_url, f"exploits/{self.exploit_id}/store/contexts"),
|
|
241
|
+
timeout=timeout,
|
|
242
|
+
)
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
def delete_context(self, context_id: str | None = None, timeout: int = HTTP_TIMEOUT):
|
|
246
|
+
context_id = self._get_context_id(context_id)
|
|
247
|
+
if not context_id:
|
|
248
|
+
raise ValueError("context_id must be provided to delete it")
|
|
249
|
+
self._keycheck(context_id, "context_id")
|
|
250
|
+
return requests_check_json(
|
|
251
|
+
self.session.delete(
|
|
252
|
+
urljoin(self.base_url, f"exploits/{self.exploit_id}/store/context/{context_id}"),
|
|
215
253
|
timeout=timeout,
|
|
216
254
|
)
|
|
217
255
|
)
|
|
@@ -21,6 +21,7 @@ class AttackExecutionDTO(BaseModel):
|
|
|
21
21
|
executed_by: ClientID|None = Field(None, validation_alias=AliasChoices('executed_by_id'))
|
|
22
22
|
executed_by_group: AttackGroupID|None = Field(None, validation_alias=AliasChoices('executed_by_group_id'))
|
|
23
23
|
flags: list[FlagDTOSmall]
|
|
24
|
+
duplicate_flags: list[str] = []
|
|
24
25
|
exploit_source: ExploitSourceID|None = Field(None, validation_alias=AliasChoices('exploit_source_id'))
|
|
25
26
|
|
|
26
27
|
class FlagDTOAttackDetails(BaseModel):
|
|
@@ -123,28 +123,29 @@ def fast_check_exploit_conf_service(exploit_config: ExploitConfig) -> ExploitCon
|
|
|
123
123
|
|
|
124
124
|
|
|
125
125
|
def auto_push_is_safe(exploit_config: ExploitConfig):
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
== 0
|
|
135
|
-
)
|
|
136
|
-
if no_latest_groups:
|
|
126
|
+
# Since groups always use the latest version, check if there are ANY groups using this exploit
|
|
127
|
+
active_groups = [
|
|
128
|
+
gr
|
|
129
|
+
for gr in g.config.reqs.groups()
|
|
130
|
+
if gr.get("exploit") == str(exploit_config.uuid)
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
if len(active_groups) == 0:
|
|
137
134
|
return True
|
|
138
135
|
else:
|
|
139
136
|
current_hash = exploit_config.hash()
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
137
|
+
try:
|
|
138
|
+
# If the exact source version is already pushed, it's safe
|
|
139
|
+
pushed_hashes = [ele["hash"] for ele in g.config.reqs.exploit_source_log(exploit_config.uuid)]
|
|
140
|
+
if current_hash in pushed_hashes:
|
|
141
|
+
return True
|
|
142
|
+
except Exception:
|
|
143
|
+
pass
|
|
144
|
+
|
|
144
145
|
if g.interactive:
|
|
145
146
|
return typer.confirm(
|
|
146
|
-
"There are attack groups
|
|
147
|
-
"
|
|
147
|
+
"There are active attack groups using this exploit. "
|
|
148
|
+
"Pushing a new version will update them to the new code. "
|
|
148
149
|
"Do you want to continue?",
|
|
149
150
|
default=False,
|
|
150
151
|
)
|
|
@@ -55,7 +55,6 @@ class g:
|
|
|
55
55
|
server_id = None
|
|
56
56
|
thread_pool: ThreadPoolExecutor = None
|
|
57
57
|
tot_teams: int = 0
|
|
58
|
-
flag_cache: set = set()
|
|
59
58
|
attack_round_deadline = dt_now()
|
|
60
59
|
exploit_hash: str | None = None
|
|
61
60
|
virtual_time_available = 0
|
|
@@ -198,8 +197,6 @@ class AttackStorage:
|
|
|
198
197
|
stats: bool = True,
|
|
199
198
|
):
|
|
200
199
|
with self._lock:
|
|
201
|
-
flags = [flag for flag in flags if flag not in g.flag_cache]
|
|
202
|
-
g.flag_cache.update(flags)
|
|
203
200
|
execution_submit = {
|
|
204
201
|
"start_time": start_time,
|
|
205
202
|
"end_time": end_time,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: exploitfarm
|
|
3
|
-
Version: 1.7.
|
|
3
|
+
Version: 1.7.7
|
|
4
4
|
Summary: Exploit Farm client
|
|
5
5
|
Home-page: https://github.com/pwnzer0tt1/exploitfarm
|
|
6
6
|
Author: Pwnzer0tt1
|
|
@@ -98,15 +98,18 @@ submit_flags(flags)
|
|
|
98
98
|
#### **Store Class**
|
|
99
99
|
|
|
100
100
|
The `Store` class provides a key-value storage system for exploits, allowing you to save and retrieve centralized data during execution.
|
|
101
|
-
The key value store is
|
|
101
|
+
The key value store is by default isolated per exploit id.
|
|
102
|
+
If you need data shared between exploits you should use a custom context_id that allows to access the same data from different exploits. The context_id can be set also in the constructor of the Store class, and will be used as default context_id for all the calls with that store instance.
|
|
102
103
|
|
|
103
104
|
```python
|
|
104
105
|
from exploitfarm import Store
|
|
105
106
|
|
|
106
|
-
|
|
107
|
+
# Initialize with an optional default context
|
|
108
|
+
store = Store(default_context_id="my_global_context_id")
|
|
107
109
|
|
|
108
|
-
# Set a value
|
|
110
|
+
# Set a value (context_id can be overridden per call)
|
|
109
111
|
store.set("example_key", {"example": "data"})
|
|
112
|
+
store.set("other_key", "value", context_id="explicit_ctx")
|
|
110
113
|
|
|
111
114
|
# Get a value (if not exists returns None)
|
|
112
115
|
data = store.get("example_key")
|
|
@@ -115,17 +118,28 @@ print(data)
|
|
|
115
118
|
# Delete a key
|
|
116
119
|
store.delete("example_key")
|
|
117
120
|
|
|
118
|
-
# List all keys
|
|
121
|
+
# List all keys in the current context
|
|
119
122
|
keys = store.keys()
|
|
120
123
|
print(keys)
|
|
124
|
+
|
|
125
|
+
# List all available contexts
|
|
126
|
+
contexts = store.contexts()
|
|
127
|
+
print(contexts)
|
|
128
|
+
|
|
129
|
+
# Delete an entire context
|
|
130
|
+
store.delete_context(context_id="explicit_ctx")
|
|
121
131
|
```
|
|
122
132
|
|
|
123
133
|
**Methods**:
|
|
124
134
|
|
|
125
|
-
- `get(key: str, timeout: int = HTTP_TIMEOUT) ->
|
|
126
|
-
- `
|
|
127
|
-
- `
|
|
128
|
-
- `
|
|
135
|
+
- `get(key: str, default=None, context_id: str | None = None, timeout: int = HTTP_TIMEOUT) -> any`: Retrieve and deserialize a value by key.
|
|
136
|
+
- `get_bytes(key: str, default=None, context_id: str | None = None, timeout: int = HTTP_TIMEOUT) -> bytes`: Retrieve a raw byte value.
|
|
137
|
+
- `set(key: str, value: any, context_id: str | None = None, timeout: int = HTTP_TIMEOUT)`: Serialize and store a value by key.
|
|
138
|
+
- `set_bytes(key: str, value: bytes, context_id: str | None = None, timeout: int = HTTP_TIMEOUT)`: Store a raw byte value.
|
|
139
|
+
- `delete(key: str, context_id: str | None = None, timeout: int = HTTP_TIMEOUT)`: Delete a key.
|
|
140
|
+
- `keys(timeout: int = HTTP_TIMEOUT, context_id: str | None = None) -> list[str]`: List all stored keys in a context.
|
|
141
|
+
- `contexts(timeout: int = HTTP_TIMEOUT) -> list[str]`: List all available contexts.
|
|
142
|
+
- `delete_context(context_id: str | None = None, timeout: int = HTTP_TIMEOUT)`: Delete an entire context and its keys.
|
|
129
143
|
|
|
130
144
|
**Environment Variables Required**:
|
|
131
145
|
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
__version__ = (
|
|
2
|
-
"1.7.5" if "{" not in "1.7.5" else "0.0.0"
|
|
3
|
-
)
|
|
4
|
-
|
|
5
|
-
from exploitfarm.utils import try_tcp_connection
|
|
6
|
-
from exploitfarm.models.enums import AttackMode, SetupStatus, FlagStatus
|
|
7
|
-
from exploitfarm.utils import DEV_MODE
|
|
8
|
-
from exploitfarm._init_exposed import get_host, Prio
|
|
9
|
-
from exploitfarm._init_exposed import nicenessify, get_config, random_str, print, Store
|
|
10
|
-
from exploitfarm._init_exposed import os_unix, os_windows, session
|
|
11
|
-
|
|
12
|
-
# Exported functions
|
|
13
|
-
__all__ = [
|
|
14
|
-
"try_tcp_connection",
|
|
15
|
-
"AttackMode",
|
|
16
|
-
"SetupStatus",
|
|
17
|
-
"get_host",
|
|
18
|
-
"get_config",
|
|
19
|
-
"random_str",
|
|
20
|
-
"session",
|
|
21
|
-
"print",
|
|
22
|
-
"nicenessify",
|
|
23
|
-
"Prio",
|
|
24
|
-
"FlagStatus",
|
|
25
|
-
"Store",
|
|
26
|
-
"DEV_MODE",
|
|
27
|
-
"os_unix",
|
|
28
|
-
"os_windows",
|
|
29
|
-
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|