pwninit.py 1.0.0__py3-none-any.whl

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.
pwninit/__init__.py ADDED
@@ -0,0 +1,39 @@
1
+ from pwn import *
2
+ from .io import *
3
+ from .helpers import *
4
+ from types import SimpleNamespace
5
+
6
+ from .io import ioctx
7
+ from .helpers import pwnctx
8
+
9
+ config = None
10
+
11
+
12
+ class Config(SimpleNamespace):
13
+ def __init__(
14
+ self,
15
+ binary:ELF,
16
+ libc:ELF,
17
+ libs:list=[],
18
+ chall:list|str=[],
19
+ env:dict={},
20
+ archive:str="",
21
+ kernel:str="",
22
+ prefix:str="",
23
+ **kwargs,
24
+ ):
25
+ global config
26
+ if binary and not chall:
27
+ chall = binary
28
+ super().__init__(
29
+ binary=binary,
30
+ libc=libc,
31
+ libs=libs,
32
+ chall=chall,
33
+ env=env,
34
+ archive=archive,
35
+ kernel=kernel,
36
+ prefix=prefix,
37
+ **kwargs,
38
+ )
39
+ config = self
pwninit/config.py ADDED
@@ -0,0 +1,64 @@
1
+ import os
2
+ from pathlib import Path
3
+ from pwn import log
4
+
5
+
6
+ class Config:
7
+ def __init__(self):
8
+ self.config_file = Path.home() / ".config" / "pwninit.conf"
9
+ self._config_data = {}
10
+ self._load_config()
11
+
12
+ def _load_config(self):
13
+ if self.config_file.exists():
14
+ try:
15
+ with open(self.config_file, "r") as f:
16
+ for line in f:
17
+ line = line.strip()
18
+ if line and not line.startswith("#") and "=" in line:
19
+ key, value = line.split("=", 1)
20
+ self._config_data[key.strip()] = value.strip().strip("\"'")
21
+ except Exception as e:
22
+ log.warning(f"Error reading config file {
23
+ self.config_file}: {e}")
24
+
25
+ def get(self, key, default=None, env_var=None):
26
+ if env_var and env_var in os.environ:
27
+ return os.environ[env_var]
28
+
29
+ return self._config_data.get(key, default)
30
+
31
+ def get_author(self):
32
+ return self.get("author", "0xB0tm4n", "PWNINIT_AUTHOR")
33
+
34
+ def create_default_config(self):
35
+ self.config_file.parent.mkdir(parents=True, exist_ok=True)
36
+
37
+ default_config = """# pwninit configuration file
38
+ # You can override these values with environment variables
39
+
40
+ # Author name for generated files
41
+ author=0xB0tm4n
42
+
43
+ # Root-me provider settings
44
+ # rootme_api_key=your_api_key_here
45
+
46
+ # Proof of work binaries
47
+ # sossette=pow-sossette
48
+ # hxp=pow-hxp
49
+ # redpwn=redpwnpow
50
+ # kctf=pow-kctf
51
+ # hashcash=hashcash
52
+ """
53
+
54
+ if not self.config_file.exists():
55
+ try:
56
+ with open(self.config_file, "w") as f:
57
+ f.write(default_config)
58
+ log.success(f"Created default config file at {
59
+ self.config_file}")
60
+ except Exception as e:
61
+ log.warning(f"Could not create config file: {e}")
62
+
63
+
64
+ config = Config()
pwninit/farm.py ADDED
@@ -0,0 +1,334 @@
1
+ import copy
2
+ import itertools
3
+ import queue
4
+ import re
5
+ import threading
6
+ import time
7
+ from math import ceil
8
+ from pathlib import Path
9
+ from urllib.parse import urljoin
10
+
11
+ import requests
12
+ from pwn import log
13
+
14
+ from pwninit.io import IOContext
15
+ from pwninit.helpers import PwnContext
16
+
17
+ SERVER_TIMEOUT = 5
18
+ POST_PERIOD = 5
19
+ POST_FLAG_LIMIT = 1000
20
+ exit_event = threading.Event()
21
+
22
+
23
+ class FlagStorage:
24
+ def __init__(self):
25
+ self._flags = []
26
+ self._seen = set()
27
+ self._lock = threading.Lock()
28
+
29
+ def add(self, flag, team_name, service):
30
+ with self._lock:
31
+ if flag not in self._seen:
32
+ self._seen.add(flag)
33
+ self._flags.append(
34
+ {"flag": flag, "team": team_name, "service": service}
35
+ )
36
+
37
+ def flush(self):
38
+ with self._lock:
39
+ flags = self._flags[:POST_FLAG_LIMIT]
40
+ self._flags = self._flags[POST_FLAG_LIMIT:]
41
+ return flags
42
+
43
+ def unflushed(self, flags):
44
+ with self._lock:
45
+ self._flags = flags + self._flags
46
+
47
+
48
+ class FlagIDStorage:
49
+ def __init__(self):
50
+ self._data = {}
51
+ self._lock = threading.Lock()
52
+
53
+ def update(self, chall, data):
54
+ with self._lock:
55
+ self._data[chall] = data
56
+
57
+ def get(self, chall, team=None):
58
+ with self._lock:
59
+ if chall not in self._data:
60
+ return None
61
+ if team is None:
62
+ return self._data[chall]
63
+ return self._data[chall].get(str(team))
64
+
65
+
66
+ def get_auth_headers(args):
67
+ return {"Authorization": args.password}
68
+
69
+
70
+ def set_farm_config(args, config):
71
+ url = urljoin(args.url, "/api/set_config")
72
+ r = requests.post(
73
+ url, headers=get_auth_headers(args), json=config, timeout=SERVER_TIMEOUT
74
+ )
75
+ if not r.ok:
76
+ raise Exception(r.text)
77
+ return r.json()
78
+
79
+
80
+ def get_farm_config(args):
81
+ url = urljoin(args.url, "/api/get_config")
82
+ r = requests.get(url, headers=get_auth_headers(args), timeout=SERVER_TIMEOUT)
83
+ if not r.ok:
84
+ raise Exception(r.text)
85
+ return r.json()
86
+
87
+
88
+ def get_flagids(args, *keys):
89
+ path = "/".join(str(k) for k in keys)
90
+ url = urljoin(args.url, f"/api/get_flagid/{path}")
91
+ r = requests.get(url, headers=get_auth_headers(args), timeout=SERVER_TIMEOUT)
92
+ if not r.ok:
93
+ raise Exception(r.text)
94
+ return r.json()
95
+
96
+
97
+ def post_flags(args, flags):
98
+ sploit_name = Path("exploit.py").resolve().parent.name
99
+ data = [
100
+ {
101
+ "flag": item["flag"],
102
+ "sploit": sploit_name,
103
+ "team": item["team"],
104
+ "service": item["service"],
105
+ }
106
+ for item in flags
107
+ ]
108
+ url = urljoin(args.url, "/api/post_flags")
109
+ r = requests.post(
110
+ url, headers=get_auth_headers(args), json=data, timeout=SERVER_TIMEOUT
111
+ )
112
+ if not r.ok:
113
+ raise Exception(r.text)
114
+
115
+
116
+ def once_in_a_period(period):
117
+ for iter_no in itertools.count(1):
118
+ start_time = time.time()
119
+ yield iter_no
120
+ time_spent = time.time() - start_time
121
+ if period > time_spent:
122
+ exit_event.wait(period - time_spent)
123
+ if exit_event.is_set():
124
+ break
125
+
126
+
127
+ def _post_loop(args, flag_storage, stop_event):
128
+ while not stop_event.is_set():
129
+ flags = flag_storage.flush()
130
+ if flags:
131
+ try:
132
+ post_flags(args, flags)
133
+ log.info("Posted %d flags" % len(flags))
134
+ except Exception as e:
135
+ log.warning("Can't post flags: %s" % e)
136
+ flag_storage.unflushed(flags)
137
+ stop_event.wait(POST_PERIOD)
138
+
139
+ flags = flag_storage.flush()
140
+ if flags:
141
+ try:
142
+ post_flags(args, flags)
143
+ log.info("Posted %d flags" % len(flags))
144
+ except Exception as e:
145
+ log.warning("Can't post remaining flags: %s" % e)
146
+ flag_storage.unflushed(flags)
147
+
148
+
149
+ def _run_one(
150
+ exploit,
151
+ ioctx,
152
+ ctx,
153
+ flag_ids,
154
+ team_name,
155
+ max_runtime,
156
+ flag_format,
157
+ flag_storage,
158
+ chall,
159
+ ):
160
+ try:
161
+ ioctx.connect(enable_log=False)
162
+ except Exception as e:
163
+ log.warning("%s: connection failed - %s" % (team_name, e))
164
+ return
165
+
166
+ ioctx.conn.timeout = max_runtime
167
+
168
+ try:
169
+ result = exploit(ctx, ioctx, flag_ids)
170
+ flags = flag_format.findall(result) if isinstance(result, (str, bytes)) else []
171
+ if not flags and result:
172
+ flags = [result]
173
+ for flag in flags:
174
+ log.success("%s: got flag %s" % (team_name, flag))
175
+ flag_storage.add(flag, team_name, chall)
176
+ except Exception as e:
177
+ log.warning("%s: exploit failed - %s" % (team_name, e))
178
+ finally:
179
+ ioctx.close(enable_log=False)
180
+
181
+
182
+ def _team_worker(
183
+ team_name,
184
+ ioctx,
185
+ ctx,
186
+ exploit,
187
+ config,
188
+ args,
189
+ flag_format,
190
+ flag_storage,
191
+ flagid_storage,
192
+ task_queue,
193
+ stop_event,
194
+ ):
195
+ """Persistent per-team thread. Binds its IOContext into the thread-local
196
+ proxy once at startup, then waits for round tasks."""
197
+ import pwninit.io as io
198
+ import pwninit.helpers as helpers
199
+
200
+ io.set_ctx(ioctx)
201
+ helpers.set_ctx(ctx)
202
+
203
+ while not stop_event.is_set():
204
+ try:
205
+ max_runtime = task_queue.get(timeout=1)
206
+ except queue.Empty:
207
+ continue
208
+
209
+ flag_ids = flagid_storage.get(config.challname, team_name)
210
+ try:
211
+ _run_one(
212
+ exploit,
213
+ ioctx,
214
+ ctx,
215
+ flag_ids,
216
+ team_name,
217
+ max_runtime,
218
+ flag_format,
219
+ flag_storage,
220
+ config.challname,
221
+ )
222
+ finally:
223
+ task_queue.task_done()
224
+
225
+
226
+ def run_farm(args, config, exploit):
227
+ log.info("Connecting to farm server at %s" % args.url)
228
+
229
+ if hasattr(config, "farm_config"):
230
+ set_farm_config(args, config.farm_config)
231
+
232
+ try:
233
+ farm_config = get_farm_config(args)
234
+ log.info(f'Flag format: {farm_config["FLAG_FORMAT"]}')
235
+ log.info(f'Flag lifetime: {farm_config["FLAG_LIFETIME"]}')
236
+ if not args.period:
237
+ args.period = farm_config["FLAG_LIFETIME"]
238
+ else:
239
+ args.period = 55
240
+ except Exception as e:
241
+ log.error("Can't get farm_config from server: %s" % e)
242
+ return 1
243
+
244
+ teams = farm_config["TEAMS"]
245
+ flag_format = re.compile(farm_config["FLAG_FORMAT"])
246
+ if not teams:
247
+ log.error("No teams in server farm_config")
248
+ return 1
249
+
250
+ log.info("Got %d teams from server" % len(teams))
251
+
252
+ flag_storage = FlagStorage()
253
+ flagid_storage = FlagIDStorage()
254
+
255
+ team_ctxs = {}
256
+ for team_name, team_addr in teams.items():
257
+ try:
258
+ host = team_addr
259
+ if ":" in host:
260
+ host = team_addr.split(":")[0]
261
+ port = int(team_addr.split(":")[1])
262
+
263
+ team_args = copy.copy(args)
264
+ team_args.remote.host = host
265
+ if port:
266
+ team_args.remote.port = port
267
+ ioctx = IOContext(team_args, config)
268
+ ctx = PwnContext(ioctx.proc, config.binary, config.libc)
269
+ team_ctxs[team_name] = (copy.deepcopy(ioctx), copy.deepcopy(ctx))
270
+ except Exception as e:
271
+ log.warning("%s: failed to init - %s" % (team_name, e))
272
+
273
+ if not team_ctxs:
274
+ log.error("No teams could be initialized")
275
+ return 1
276
+
277
+ team_queues = {}
278
+ stop_event = threading.Event()
279
+
280
+ for team_name, (ioctx, ctx) in team_ctxs.items():
281
+ q = queue.Queue()
282
+ team_queues[team_name] = q
283
+ t = threading.Thread(
284
+ target=_team_worker,
285
+ args=(
286
+ team_name,
287
+ ioctx,
288
+ ctx,
289
+ exploit,
290
+ config,
291
+ args,
292
+ flag_format,
293
+ flag_storage,
294
+ flagid_storage,
295
+ q,
296
+ stop_event,
297
+ ),
298
+ daemon=True,
299
+ )
300
+ t.start()
301
+
302
+ try:
303
+ for attack_no in once_in_a_period(args.period):
304
+ try:
305
+ data = get_flagids(args, config.challname)
306
+ flagid_storage.update(config.challname, data)
307
+ log.info("Fetched flag IDs for %d teams" % len(data))
308
+ except Exception as e:
309
+ log.warning("Can't fetch flag IDs: %s" % e)
310
+ log.warning("No flag id for attack #%d" % attack_no)
311
+
312
+ log.info("Launching attack #%d on %d teams" % (attack_no, len(team_ctxs)))
313
+
314
+ post_stop = threading.Event()
315
+ post_thread = threading.Thread(
316
+ target=_post_loop, args=(args, flag_storage, post_stop), daemon=True
317
+ )
318
+ post_thread.start()
319
+
320
+ for q in team_queues.values():
321
+ q.put(args.period)
322
+
323
+ for q in team_queues.values():
324
+ q.join()
325
+
326
+ post_stop.set()
327
+ post_thread.join()
328
+
329
+ except KeyboardInterrupt:
330
+ log.info("Got Ctrl+C, shutting down")
331
+
332
+ stop_event.set()
333
+ exit_event.set()
334
+ return 0
@@ -0,0 +1,3 @@
1
+ from .pwncontext import *
2
+ from .utils import *
3
+ from .constants import *
@@ -0,0 +1,67 @@
1
+ # FSOP
2
+ IO_USER_BUF = 0x0001
3
+ IO_UNBUFFERED = 0x0002
4
+ IO_NO_READS = 0x0004
5
+ IO_NO_WRITES = 0x0008
6
+ IO_EOF_SEEN = 0x0010
7
+ IO_ERR_SEEN = 0x0020
8
+ IO_DELETE_DONT_CLOSE = 0x0040
9
+ IO_LINKED = 0x0080
10
+ IO_IN_BACKUP = 0x0100
11
+ IO_LINE_BUF = 0x0200
12
+ IO_TIED_PUT_GET = 0x0400
13
+ IO_CURRENTLY_PUTTING = 0x0800
14
+ IO_IS_APPENDING = 0x1000
15
+ IO_IS_FILEBUF = 0x2000
16
+
17
+ DUMMY = 0x00
18
+ DUMMY2 = 0x08
19
+ FINISH = 0x10
20
+ OVERFLOW = 0x18
21
+ UNDERFLOW = 0x20
22
+ UFLOW = 0x28
23
+ PBACKFAIL = 0x30
24
+ XSPUTN = 0x38
25
+ XSGETN = 0x40
26
+ SEEKOFF = 0x48
27
+ SEEKPOS = 0x50
28
+ SETBUF = 0x58
29
+ SYNC = 0x60
30
+ DOALLOCATE = 0x68
31
+ READ = 0x70
32
+ WRITE = 0x78
33
+ SEEK = 0x80
34
+ CLOSE = 0x88
35
+ STAT = 0x90
36
+ SHOWMANYC = 0x98
37
+ IMBUE = 0xA0
38
+
39
+ # House Of Muney
40
+ STB_LOCAL = 0
41
+ STB_GLOBAL = 1
42
+ STB_WEAK = 2
43
+ STB_NUM = 3
44
+ STB_LOOS = (10,)
45
+ STB_GNU_UNIQUE = (10,)
46
+ STB_HIOS = (12,)
47
+ STB_LOPROC = (13,)
48
+ STB_HIPROC = (15,)
49
+
50
+ STT_NOTYPE = 0
51
+ STT_OBJECT = 1
52
+ STT_FUNC = 2
53
+ STT_SECTION = 3
54
+ STT_FILE = 4
55
+ STT_COMMON = 5
56
+ STT_TLS = 6
57
+ STT_NUM = 7
58
+ STT_LOOS = (10,)
59
+ STT_GNU_IFUNC = 10
60
+ STT_HIOS = (12,)
61
+ STT_LOPROC = (13,)
62
+ STT_HIPROC = (15,)
63
+
64
+ STV_DEFAULT = 0
65
+ STV_INTERNAL = 1
66
+ STV_HIDDEN = 2
67
+ STV_PROTECTED = 3