wafaHell 0.2.0__py3-none-any.whl → 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
wafaHell/app.py ADDED
@@ -0,0 +1,31 @@
1
+ from middleware import WafaHell
2
+ from flask import Flask, render_template, request
3
+ from werkzeug.middleware.proxy_fix import ProxyFix
4
+
5
+ app = Flask(__name__)
6
+
7
+
8
+ app.wsgi_app = ProxyFix(
9
+ app.wsgi_app,
10
+ x_for=1,
11
+ x_proto=1,
12
+ x_host=1,
13
+ x_port=1
14
+ )
15
+
16
+ @app.route('/', methods=['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'])
17
+ def home():
18
+ return "<h1>Bem-vindo à minha aplicação Flask!</h1><p>Acesse /hello?nome=test</p>"
19
+
20
+ @app.route('/hello', methods=['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'])
21
+ def hello():
22
+ nome = request.args.get('nome', 'Visitante')
23
+ return f"<h1>Olá, {nome}!</h1>"
24
+
25
+ @app.route('/admin/dashboard')
26
+ def dashboard():
27
+ return "<h1>Dashboard Personalizado</h1><p>Este é o painel de controle personalizado.</p>"
28
+
29
+ if __name__ == '__main__':
30
+ WafaHell(app, dashboard_path='/hell/dashboard', block_durantion=1, rate_limit=True, block_ip=True)
31
+ app.run(debug=True, host='0.0.0.0', port=5000)
wafaHell/globals.py ADDED
@@ -0,0 +1,5 @@
1
+ import os
2
+ from diskcache import Cache
3
+
4
+ cache_path = '/dev/shm/waf_cache' if os.path.exists('/dev/shm') else './waf_cache_temp'
5
+ waf_cache = Cache(cache_path)
wafaHell/logger.py CHANGED
@@ -1,34 +1,121 @@
1
+ from datetime import datetime
1
2
  import logging
3
+ from model import WafLog, get_session
4
+ import re
5
+ # Handler customizado para salvar no SQLite via SQLAlchemy
6
+ class SQLAlchemyHandler(logging.Handler):
7
+ def __init__(self):
8
+ super().__init__()
9
+ self.session = get_session()
10
+ # Regex para extrair dados da string formatada pelo parse_req
11
+ self.attr_pattern = re.compile(
12
+ r"Attack_type: (?P<type>.*?), IP: (?P<ip>.*?), .*?Path: (?P<path>.*?), Method: (?P<method>.*?), Payload: (?P<payload>.*?), attack_local: (?P<local>.*)"
13
+ )
14
+
15
+ def emit(self, record):
16
+ session = self.session # Certifique-se de instanciar a sessão
17
+ try:
18
+ msg = record.getMessage()
19
+
20
+ # Valores padrão
21
+ ip, path, method, payload, local, attack_type = (None, None, None, msg, None, "Info")
22
+
23
+ # Expandimos a condição para incluir [BLOCKED]
24
+ if any(tag in msg for tag in ["[ATTACK]", "[RATE LIMIT]", "[BLOCKED]"]):
25
+
26
+ def extract(key, text):
27
+ # Regex ajustada para capturar até a vírgula ou fim da tag, permitindo espaços internos
28
+ match = re.search(rf"{key}:?\s*([^,\]]+)", text)
29
+ return match.group(1).strip() if match else None
30
+
31
+ ip = extract("IP", msg)
32
+
33
+ if "[BLOCKED]" in msg:
34
+ attack_type = "IP BLOCK"
35
+ ua = extract("UA", msg)
36
+ payload = f"IP Bloqueado. User-Agent: {ua}" if ua else "IP Bloqueado"
37
+ path = "---"
38
+ method = "---"
39
+ elif "[RATE LIMIT]" in msg:
40
+ attack_type = "RATE LIMIT"
41
+ payload = "Exceeded request limit"
42
+ path = extract("Path", msg)
43
+ method = extract("Method", msg)
44
+
45
+ bucket = datetime.utcnow().strftime('%Y-%m-%d %H:%M')
46
+ log_entry = WafLog(
47
+ level=record.levelname,
48
+ attack_type=attack_type,
49
+ ip=ip,
50
+ path=path,
51
+ method=method,
52
+ payload=payload,
53
+ attack_local=local,
54
+ time_bucket=bucket
55
+ )
56
+
57
+ session.add(log_entry)
58
+ session.commit()
59
+ else:
60
+ # Lógica para [ATTACK]
61
+ attack_type = extract("Attack_type", msg) or "Unknown"
62
+ path = extract("Path", msg)
63
+ method = extract("Method", msg)
64
+
65
+ # Captura o local completo (ex: HEADER 'User-Agent')
66
+ local_match = re.search(r"attack_local:\s*(.+)$", msg)
67
+ local = local_match.group(1).strip() if local_match else extract("attack_local", msg)
68
+
69
+ # Regex específica para payloads que podem conter vírgulas
70
+ payload_match = re.search(r"Payload: (.*?), attack_local:", msg)
71
+ payload = payload_match.group(1) if payload_match else extract("Payload", msg)
72
+
73
+ if not "[RATE LIMIT]" in msg:
74
+ log_entry = WafLog(
75
+ level=record.levelname,
76
+ attack_type=attack_type,
77
+ ip=ip,
78
+ path=path,
79
+ method=method,
80
+ payload=payload,
81
+ attack_local=local,
82
+ # time_bucket fica None aqui para não filtrar ataques normais no banco
83
+ )
84
+
85
+ session.add(log_entry)
86
+ session.commit()
87
+
88
+ except Exception as e:
89
+ session.rollback()
90
+ finally:
91
+ session.close()
2
92
 
3
93
  class Logger:
4
94
  def __init__(self, name="WAF", log_file="waf.log", level=logging.INFO):
5
- # Cria logger com nome
6
95
  self.logger = logging.getLogger(name)
7
96
  self.logger.setLevel(level)
8
97
 
9
- # Evita handlers duplicados ao reinicializar
10
98
  if not self.logger.handlers:
11
- # Handler para console
12
- console_handler = logging.StreamHandler()
13
- console_handler.setLevel(level)
14
-
15
- # Handler para arquivo
16
- file_handler = logging.FileHandler(log_file)
17
- file_handler.setLevel(level)
18
-
19
- # Formato
20
99
  formatter = logging.Formatter(
21
100
  "[%(asctime)s] [%(name)s] [%(levelname)s] %(message)s",
22
101
  datefmt="%H:%M:%S - %d/%m/%Y"
23
102
  )
24
103
 
104
+ # Handler 1: Console
105
+ console_handler = logging.StreamHandler()
25
106
  console_handler.setFormatter(formatter)
26
- file_handler.setFormatter(formatter)
27
-
28
- # Adiciona handlers
29
107
  self.logger.addHandler(console_handler)
108
+
109
+ # Handler 2: Arquivo
110
+ file_handler = logging.FileHandler(log_file)
111
+ file_handler.setFormatter(formatter)
30
112
  self.logger.addHandler(file_handler)
31
113
 
114
+ # Handler 3: Banco de Dados (A Mágica acontece aqui)
115
+ db_handler = SQLAlchemyHandler()
116
+ db_handler.setLevel(logging.INFO) # Salva INFO, WARNING e acima no DB
117
+ self.logger.addHandler(db_handler)
118
+
32
119
  def info(self, msg):
33
120
  self.logger.info(msg)
34
121
 
wafaHell/middleware.py CHANGED
@@ -1,151 +1,351 @@
1
+ from datetime import datetime, timedelta, timezone
2
+ import os
1
3
  import re
2
- from flask import request as req, abort
4
+ import subprocess
5
+ import time
6
+ from flask import request as req, abort, g
3
7
  from urllib.parse import unquote
4
- from model import Blocked, get_session
8
+ from model import Base, Blocked, WafLog, get_session, engine
5
9
  from logger import Logger
6
- from utils import is_block_expired
7
10
  from rateLimiter import RateLimiter
8
11
  from sqlalchemy.exc import OperationalError
12
+ from panel import setup_dashboard
13
+ from utils import Admin
14
+ from sqlalchemy import text
15
+ from globals import waf_cache
9
16
 
10
17
  # Inicializa o RateLimiter
11
18
  limiter = RateLimiter(limit=100, window=60)
12
19
 
13
20
  class WafaHell:
14
- def __init__(self, app=None, block_code=403, log_func=None, monitor_mode=False, block_ip=False, rate_limit=False):
21
+ def __init__(self, app=None, block_code=403, block_durantion=5, block_ip=False, log_func=None, monitor_mode=False, rate_limit=False, dashboard_path=None):
15
22
  self.app = app
16
23
  self.block_code = block_code
17
24
  self.log = log_func or Logger()
18
25
  self.monitor_mode = monitor_mode
19
26
  self.block_ip = block_ip
20
27
  self.rate_limit = rate_limit
28
+ self.dashboard_path = dashboard_path
29
+ self.block_durantion = block_durantion
30
+ self.recent_blocks_cache = {}
21
31
 
22
- self.rules = [
32
+ self.rules_sqli = [
23
33
  r"(\bUNION\b|\bSELECT\b|\bINSERT\b|\bDROP\b)",
24
- r"' OR '1'='1",
34
+ r"' OR '1'='1"
35
+ ]
36
+ self.rules_xss = [
25
37
  r"<script.*?>.*?</script>",
26
- r"javascript:",
38
+ r"javascript:"
27
39
  ]
28
40
 
29
41
  if app is not None:
30
42
  self.init_app(app)
31
43
 
32
44
  def init_app(self, app):
33
- # Configura a sessão para cada requisição
45
+ Base.metadata.create_all(engine)
46
+ setup_dashboard(app, self.dashboard_path)
47
+ Admin.create_admin_user(get_session())
48
+
49
+ if not app.secret_key:
50
+ app.secret_key = os.urandom(24)
51
+
34
52
  @app.before_request
35
53
  def create_session():
36
54
  try:
37
- req.session = get_session() # Cria uma nova sessão usando get_session
55
+ req.session = get_session()
38
56
  except Exception as e:
39
57
  self.log.error(f"Erro ao criar sessão para requisição: {e}")
40
- abort(self.block_code) # Retorna erro 500 se não conseguir criar a sessão
58
+ abort(self.block_code)
41
59
 
42
- # Fecha a sessão após cada requisição
43
60
  @app.teardown_request
44
61
  def close_session(exc=None):
45
62
  if hasattr(req, 'session'):
46
63
  try:
47
- req.session.close() # Fecha a sessão para liberar a conexão
64
+ req.session.close()
48
65
  except Exception as e:
49
66
  self.log.error(f"Erro ao fechar sessão: {e}")
67
+
50
68
 
51
69
  @app.before_request
52
70
  def waf_check():
53
71
  self.verify_client_blocked(req)
54
72
  self.verify_rate_limit(req)
55
- is_malicious, attack_local, payload = self.is_malicious(req)
73
+ is_malicious, attack_local, payload, attack_type = self.is_malicious(req)
56
74
 
57
75
  if not is_malicious:
58
76
  return
59
77
 
60
78
  if not self.monitor_mode:
61
- self.log.warning(self.parse_req(req, payload,attack_local))
79
+ self.log.warning(self.parse_req(req, payload, attack_local, attack_type))
62
80
  self.block_ip_address(req.remote_addr, req.headers.get("User-Agent", "unknown"))
81
+ abort(self.block_code)
63
82
  else:
64
- self.log.info(self.parse_req(req, payload, attack_local))
83
+ self.log.info(self.parse_req(req, payload, attack_local, attack_type))
84
+
85
+ @app.before_request
86
+ def start_timer():
87
+ g.waf_start_time = time.time()
88
+
89
+ @app.after_request
90
+ def stop_timer(response):
91
+ ignored_paths = [self.dashboard_path, f'{self.dashboard_path}/stats', '/static']
92
+
93
+ # 1. Ignora rotas do próprio painel para não sujar os logs e métricas
94
+ if any(req.path.startswith(path) for path in ignored_paths):
95
+ return response
96
+
97
+ # 2. LOG DE TRÁFEGO LEGÍTIMO
98
+ # Se o status_code for menor que 400, significa que o WAF não deu abort()
99
+ # e a requisição seguiu o fluxo normal.
100
+ if response.status_code != self.block_code:
101
+ self.log_legit_access(req)
102
+
103
+ # 3. RPS Logic (Bucketing by second)
104
+ current_timestamp = int(time.time())
105
+ rps_key = f"rps_{current_timestamp}"
106
+ waf_cache.incr(rps_key, default=0)
107
+ waf_cache.expire(rps_key, 10)
108
+
109
+ # 4. Latency Logic
110
+ if hasattr(g, 'waf_start_time'):
111
+ latency = (time.time() - g.waf_start_time) * 1000
112
+
113
+ # Exponential Moving Average
114
+ old_avg = waf_cache.get('latency_avg', default=0.0)
115
+ new_avg = (old_avg * 0.95) + (latency * 0.05) if old_avg > 0 else latency
116
+
117
+ waf_cache.set('latency_avg', new_avg, expire=3600)
118
+
119
+ return response
120
+
121
+ def log_legit_access(self, req):
122
+ session = get_session()
123
+ try:
124
+ new_log = WafLog(
125
+ timestamp=datetime.now(timezone.utc),
126
+ attack_type='INFO', # Identificador de tráfego limpo
127
+ ip=req.remote_addr,
128
+ path=req.path,
129
+ method=req.method,
130
+ level='INFO'
131
+ )
132
+ session.add(new_log)
133
+ session.commit()
134
+ except Exception as e:
135
+ session.rollback()
136
+ self.log.error(f"Error logging legit access: {e}")
137
+ finally:
138
+ session.close()
65
139
 
66
140
  def detect_attack(self, data: str) -> bool:
67
- for pattern in self.rules:
141
+ for pattern in self.rules_xss:
68
142
  if re.search(pattern, data, re.IGNORECASE):
69
- return True
70
- return False
143
+ return "XSS"
144
+ for pattern in self.rules_sqli:
145
+ if re.search(pattern, data, re.IGNORECASE):
146
+ return "SQLI"
147
+ return None
71
148
 
72
- def is_malicious(self, req) -> tuple[bool, str | None, str | None]:
149
+ def is_malicious(self, req) -> tuple:
150
+ attack = self.detect_attack(req.base_url)
151
+ if attack:
152
+ print(f"[DEBUG] Attack detected in URL: {attack}")
153
+ return True, "URL", req.base_url, attack
154
+
155
+ for key, value in req.form.items():
156
+ attack = self.detect_attack(value)
157
+ if attack:
158
+ print(f"[DEBUG] Attack detected in FORM '{key}': {attack}")
159
+ return True, f"FORM '{key}'", value, attack
73
160
 
74
- if self.detect_attack(req.base_url):
75
- return True, "URL", req.base_url
76
-
77
161
  for key, value in req.args.items():
78
- if self.detect_attack(value):
79
- return True, f"QUERY '{key}'", value
162
+ attack = self.detect_attack(value)
163
+ if attack:
164
+ print(f"[DEBUG] Attack detected in QUERY '{key}': {attack}")
165
+ return True, f"QUERY '{key}'", value, attack
80
166
 
81
167
  for key, value in req.headers.items():
82
- if self.detect_attack(value):
83
- return True, f"HEADER '{key}'", value
168
+ attack = self.detect_attack(value)
169
+ if attack:
170
+ print(f"[DEBUG] Attack detected in HEADER '{key}': {attack}")
171
+ return True, f"HEADER '{key}'", value, attack
84
172
 
85
173
  if req.data:
86
174
  body_content = req.data.decode(errors="ignore")
87
- if self.detect_attack(body_content):
88
- return True, "BODY", body_content
175
+ attack = self.detect_attack(body_content)
176
+ if attack:
177
+ print(f"[DEBUG] Attack detected in BODY: {attack}")
178
+ return True, "BODY", body_content, attack
89
179
 
90
180
  if req.is_json:
91
181
  json_data = req.get_json(silent=True)
92
182
  if json_data:
93
183
  import json
94
184
  json_str = json.dumps(json_data)
95
- if self.detect_attack(json_str):
96
- return True, "JSON BODY", json_str
185
+ attack = self.detect_attack(json_str)
186
+ if attack:
187
+ print(f"[DEBUG] Attack detected in JSON BODY: {attack}")
188
+ return True, "JSON BODY", json_str, attack
97
189
 
98
- return False, None, None
190
+ return False, None, None, None
99
191
 
100
192
  def verify_client_blocked(self, req) -> None:
101
193
  session = req.session
102
194
  try:
103
195
  client_blocked = session.query(Blocked).filter_by(
104
- ip=req.remote_addr, user_agent=req.headers.get("User-Agent")
196
+ ip=req.remote_addr,
105
197
  ).first()
198
+
106
199
  if client_blocked:
107
- if is_block_expired(client_blocked.blocked_at):
200
+
201
+ now = datetime.now(timezone.utc) if client_blocked.blocked_until.tzinfo else datetime.utcnow()
202
+
203
+ if client_blocked.blocked_until <= now:
204
+ # --- TRAVA DE DESBLOQUEIO (Anti-Race Condition) ---
205
+ # Usamos um marcador no cache para saber se alguém já está desbloqueando este IP
206
+ cache_key = f"unblocking_{req.remote_addr}"
207
+ if cache_key in self.recent_blocks_cache:
208
+ return # Outra thread já está limpando este IP, apenas saia
209
+
210
+ self.recent_blocks_cache[cache_key] = True
211
+ # --------------------------------------------------
212
+
213
+ try:
214
+ session.delete(client_blocked)
215
+ session.commit()
216
+
217
+ # Limpa os caches de controle deste IP
218
+ self.recent_blocks_cache.pop(req.remote_addr, None)
219
+ self.recent_blocks_cache.pop(cache_key, None)
220
+
221
+ self.log.info(f"[UNBLOCKED] IP {req.remote_addr} bloqueio expirou.")
222
+ except Exception as e:
223
+ session.rollback()
224
+ self.recent_blocks_cache.pop(cache_key, None)
225
+ raise e
226
+ return
227
+
228
+ # Se chegou aqui, ainda está bloqueado
229
+ abort(self.block_code)
230
+
231
+ except OperationalError:
232
+ session.rollback()
233
+ abort(self.block_code)
234
+
235
+ try:
236
+
237
+ client_blocked = session.query(Blocked).filter_by(
238
+ ip=req.remote_addr,
239
+ user_agent=req.headers.get("User-Agent")
240
+ ).first()
241
+
242
+ if not client_blocked:
243
+ return
244
+
245
+ # Normaliza o tempo para comparação
246
+ now = datetime.now(timezone.utc) if client_blocked.blocked_until.tzinfo else datetime.utcnow()
247
+
248
+ if client_blocked.blocked_until <= now:
249
+ # --- TRAVA DE DESBLOQUEIO (Anti-Race Condition) ---
250
+ # Usamos um marcador no cache para saber se alguém já está desbloqueando este IP
251
+ cache_key = f"unblocking_{req.remote_addr}"
252
+ if cache_key in self.recent_blocks_cache:
253
+ return # Outra thread já está limpando este IP, apenas saia
254
+
255
+ self.recent_blocks_cache[cache_key] = True
256
+ # --------------------------------------------------
257
+
258
+ try:
108
259
  session.delete(client_blocked)
109
260
  session.commit()
110
- self.log.info(f"[UNBLOCKED] IP {req.remote_addr} desbloqueado apos expiracao do bloqueio.")
111
- else:
112
- abort(self.block_code)
113
- except OperationalError as e:
261
+
262
+ # Limpa os caches de controle deste IP
263
+ self.recent_blocks_cache.pop(req.remote_addr, None)
264
+ self.recent_blocks_cache.pop(cache_key, None)
265
+
266
+ self.log.info(f"[UNBLOCKED] IP {req.remote_addr} bloqueio expirou.")
267
+ except Exception as e:
268
+ session.rollback()
269
+ self.recent_blocks_cache.pop(cache_key, None)
270
+ raise e
271
+ return
272
+
273
+ abort(self.block_code)
274
+
275
+ except OperationalError:
114
276
  session.rollback()
115
277
  abort(self.block_code)
116
-
117
278
 
118
279
  def block_ip_address(self, ip, user_agent=None):
119
- if self.block_ip:
120
- try:
121
- session = req.session
122
- blocked_client = Blocked(ip=ip, user_agent=user_agent)
123
- session.add(blocked_client)
124
- session.commit()
125
- self.log.warning(f"[BLOCKED] IP: {ip}, User-Agent: {user_agent}")
126
- except OperationalError as e:
127
- self.log.error(f"Erro de banco de dados ao bloquear IP {ip}: {e}")
128
- session.rollback()
129
- abort(self.block_code)
130
- except Exception as e:
131
- self.log.error(f"Erro ao bloquear IP {ip}: {e}")
132
- session.rollback()
280
+ if not self.block_ip:
281
+ return
282
+
283
+ # 1. Trava de Memória (ajuda, mas não resolve 100% em multi-processo)
284
+ now_ts = datetime.now().timestamp()
285
+ if ip in self.recent_blocks_cache:
286
+ if now_ts - self.recent_blocks_cache[ip] < 5:
287
+ return
288
+ self.recent_blocks_cache[ip] = now_ts
289
+
290
+ session = get_session()
291
+ try:
292
+ # 2. TRAVA DE BANCO: Verifica se já houve um log desse IP nos últimos 2 segundos
293
+ # Isso evita que as 6 threads do ffuf que passaram pela trava de memória gravem no banco
294
+
295
+ time_threshold = datetime.now(timezone.utc) - timedelta(seconds=2)
296
+
297
+ # Buscamos na tabela de LOGS (WafLog) se já existe um registro recente
298
+
299
+ exists_recent_log = session.query(WafLog).filter(
300
+ WafLog.ip == ip,
301
+ WafLog.attack_type.in_(['RATE LIMIT', 'IP BLOCK']),
302
+ WafLog.timestamp >= time_threshold
303
+ ).first()
304
+
305
+ if not exists_recent_log:
306
+ # Só prossegue se não houver log recente
307
+ exists_block = session.query(Blocked).filter_by(ip=ip).first()
308
+ if not exists_block:
309
+ now = datetime.now(timezone.utc)
310
+ until = now + timedelta(minutes=self.block_durantion)
311
+
312
+ new_block = Blocked(
313
+ ip=ip, user_agent=user_agent,
314
+ blocked_at=now, blocked_until=until
315
+ )
316
+ session.add(new_block)
317
+ session.commit()
318
+
319
+
320
+
321
+ except Exception as e:
322
+ session.rollback()
323
+ self.log.error(f"Erro ao persistir bloqueio: {e}")
324
+ finally:
325
+ session.close()
133
326
 
134
327
  def verify_rate_limit(self, req) -> None:
135
328
  if self.rate_limit:
136
- ip = req.remote_addr
137
- ua = req.headers.get("User-Agent", "unknown")
138
- if limiter.is_rate_limited(ip, ua):
139
- self.log.warning(f"[RATE LIMIT] IP: {ip}, User-Agent: {ua} excedeu o limite de requisições.")
140
- if self.block_ip:
141
- self.block_ip_address(ip, ua)
142
- abort(self.block_code)
329
+ ip = req.remote_addr
330
+ ua = req.headers.get("User-Agent", "unknown")
331
+
332
+ if limiter.is_rate_limited(ip, ua):
333
+ if self.monitor_mode:
334
+ return
335
+
336
+
337
+ if self.block_ip:
338
+ self.block_ip_address(ip, ua)
339
+ self.log.warning(f"[RATE LIMIT] IP: {ip} exceeded limit.")
340
+ self.log.warning(f"[BLOCKED] IP: {ip}, UA: {ua}")
341
+ abort(self.block_code)
342
+
343
+ self.log.warning(f"[RATE LIMIT] IP: {ip} exceeded limit.")
143
344
 
144
- def parse_req(self, req, payload, attack_local=None) -> str:
345
+ def parse_req(self, req, payload, attack_local=None, attack_type=None) -> str:
145
346
  ip = req.remote_addr
146
347
  user_agent = req.headers.get("User-Agent", "unknown")
147
348
  path = req.path
148
349
  method = req.method
149
350
  attack_local = attack_local or "unknown"
150
- msg = f"""[ATTACK] IP: {ip}, User-Agent: {user_agent}, Path: {path}, Method: {method}, Payload: {unquote(payload)}, attack_local: {attack_local}"""
151
- return msg
351
+ return f"[ATTACK] Attack_type: {attack_type}, IP: {ip}, User-Agent: {user_agent}, Path: {path}, Method: {method}, Payload: {unquote(payload)}, attack_local: {attack_local}"
wafaHell/mock.py ADDED
@@ -0,0 +1,53 @@
1
+ import requests
2
+
3
+ # Configuração do alvo
4
+ BASE_URL = "http://127.0.0.1:5000/hello"
5
+
6
+ # Definição dos payloads de teste (SQLi e XSS clássicos)
7
+ payloads = {
8
+ "URL Query": {"url": f"{BASE_URL}?id=1' OR '1'='1"},
9
+ "Form Data": {"method": "POST", "data": {"user": "<script>alert(1)</script>"}},
10
+ "JSON Body": {"method": "POST", "json": {"search": "SELECT * FROM users"}},
11
+ "Cookies": {"method": "POST", "cookies": {"session": "UNION SELECT NULL,NULL--"}},
12
+ "Custom Header": {"method": "POST", "headers": {"User-Agent": "'; DROP TABLE logs;--"}},
13
+ "Multipart File": {"method": "POST", "files": {"file": ("README.md", "payload: <script>alert('XSS')</script>")}},
14
+ }
15
+
16
+ def run_mock():
17
+ print(f"🚀 Iniciando Mock de Testes WafaHell no alvo: {BASE_URL}\n")
18
+ print(f"{'VETOR DE TESTE':<20} | {'STATUS':<10} | {'RESULTADO'}")
19
+ print("-" * 55)
20
+
21
+ for test_name, config in payloads.items():
22
+ try:
23
+ # Prepara a requisição
24
+ url = config.get("url", BASE_URL)
25
+ method = config.get("method", "GET")
26
+
27
+ # Executa a requisição com os parâmetros dinâmicos
28
+ response = requests.request(
29
+ method=method,
30
+ url=url,
31
+ data=config.get("data"),
32
+ json=config.get("json"),
33
+ cookies=config.get("cookies"),
34
+ headers=config.get("headers"),
35
+ files=config.get("files"),
36
+ timeout=5
37
+ )
38
+
39
+ # Validação: 403 significa que o WAF bloqueou (Sucesso no teste)
40
+ if response.status_code == 403:
41
+ status_txt = "✅ BLOQUEADO"
42
+ result_txt = "Sucesso (WAF Ativo)"
43
+ else:
44
+ status_txt = f"❌ {response.status_code}"
45
+ result_txt = "Falha (Vulnerável!)"
46
+
47
+ print(f"{test_name:<20} | {status_txt:<10} | {result_txt}")
48
+
49
+ except Exception as e:
50
+ print(f"{test_name:<20} | ⚠️ ERRO | {str(e)}")
51
+
52
+ if __name__ == "__main__":
53
+ run_mock()