caarleexx commited on
Commit
8dc9826
·
verified ·
1 Parent(s): 8204b7d

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +598 -0
app.py ADDED
@@ -0,0 +1,598 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ import json
4
+ import time
5
+ import logging
6
+ import requests
7
+ from flask import Flask, request, jsonify, render_template_string
8
+ from playwright.sync_api import sync_playwright, TimeoutError as PlaywrightTimeoutError
9
+ from playwright_stealth import stealth_sync
10
+ import traceback
11
+
12
+ # Configuração de logging
13
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
14
+ logger = logging.getLogger(__name__)
15
+
16
+ app = Flask(__name__)
17
+
18
+ # Template HTML para interface simples
19
+ HTML_TEMPLATE = """
20
+ <!DOCTYPE html>
21
+ <html>
22
+ <head>
23
+ <title>Bypass AWS WAF - STF Jurisprudência</title>
24
+ <meta charset="utf-8">
25
+ <meta name="viewport" content="width=device-width, initial-scale=1">
26
+ <style>
27
+ body {
28
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
29
+ max-width: 1200px;
30
+ margin: 0 auto;
31
+ padding: 20px;
32
+ background: #f5f5f5;
33
+ }
34
+ .container {
35
+ background: white;
36
+ border-radius: 10px;
37
+ padding: 30px;
38
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
39
+ }
40
+ h1 {
41
+ color: #333;
42
+ border-bottom: 2px solid #4CAF50;
43
+ padding-bottom: 10px;
44
+ }
45
+ .info-box {
46
+ background: #e3f2fd;
47
+ border-left: 4px solid #2196F3;
48
+ padding: 15px;
49
+ margin: 20px 0;
50
+ border-radius: 4px;
51
+ }
52
+ button {
53
+ background: #4CAF50;
54
+ color: white;
55
+ border: none;
56
+ padding: 12px 30px;
57
+ font-size: 16px;
58
+ border-radius: 5px;
59
+ cursor: pointer;
60
+ transition: background 0.3s;
61
+ }
62
+ button:hover {
63
+ background: #45a049;
64
+ }
65
+ button:disabled {
66
+ background: #cccccc;
67
+ cursor: not-allowed;
68
+ }
69
+ pre {
70
+ background: #f8f9fa;
71
+ border: 1px solid #dee2e6;
72
+ border-radius: 5px;
73
+ padding: 15px;
74
+ overflow: auto;
75
+ max-height: 500px;
76
+ font-size: 12px;
77
+ }
78
+ .loading {
79
+ display: inline-block;
80
+ width: 20px;
81
+ height: 20px;
82
+ border: 3px solid #f3f3f3;
83
+ border-top: 3px solid #4CAF50;
84
+ border-radius: 50%;
85
+ animation: spin 1s linear infinite;
86
+ margin-right: 10px;
87
+ vertical-align: middle;
88
+ }
89
+ @keyframes spin {
90
+ 0% { transform: rotate(0deg); }
91
+ 100% { transform: rotate(360deg); }
92
+ }
93
+ .stats {
94
+ display: grid;
95
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
96
+ gap: 15px;
97
+ margin: 20px 0;
98
+ }
99
+ .stat-card {
100
+ background: white;
101
+ border: 1px solid #dee2e6;
102
+ border-radius: 8px;
103
+ padding: 15px;
104
+ text-align: center;
105
+ }
106
+ .stat-value {
107
+ font-size: 24px;
108
+ font-weight: bold;
109
+ color: #2196F3;
110
+ }
111
+ .stat-label {
112
+ color: #666;
113
+ font-size: 14px;
114
+ margin-top: 5px;
115
+ }
116
+ .error {
117
+ color: #f44336;
118
+ background: #ffebee;
119
+ border-left: 4px solid #f44336;
120
+ padding: 15px;
121
+ margin: 10px 0;
122
+ border-radius: 4px;
123
+ }
124
+ .success {
125
+ color: #4CAF50;
126
+ background: #e8f5e8;
127
+ border-left: 4px solid #4CAF50;
128
+ padding: 15px;
129
+ margin: 10px 0;
130
+ border-radius: 4px;
131
+ }
132
+ </style>
133
+ </head>
134
+ <body>
135
+ <div class="container">
136
+ <h1>🛡️ Bypass AWS WAF - STF Jurisprudência</h1>
137
+
138
+ <div class="info-box">
139
+ <strong>📌 Sobre:</strong> Teste de bypass do AWS WAF usando Playwright + Stealth para acessar a API de jurisprudência do STF.
140
+ <br>
141
+ <strong>🔗 API Alvo:</strong> https://jurisprudencia.stf.jus.br/api/search/search
142
+ </div>
143
+
144
+ <div class="stats">
145
+ <div class="stat-card">
146
+ <div class="stat-value" id="requestsCount">0</div>
147
+ <div class="stat-label">Total de Requisições</div>
148
+ </div>
149
+ <div class="stat-card">
150
+ <div class="stat-value" id="successCount">0</div>
151
+ <div class="stat-label">Sucessos</div>
152
+ </div>
153
+ <div class="stat-card">
154
+ <div class="stat-value" id="failCount">0</div>
155
+ <div class="stat-label">Falhas</div>
156
+ </div>
157
+ </div>
158
+
159
+ <div style="margin: 20px 0;">
160
+ <button id="testBtn" onclick="runTest()">
161
+ <span class="loading" id="loading" style="display: none;"></span>
162
+ <span id="btnText">🚀 Executar Teste de Bypass</span>
163
+ </button>
164
+ </div>
165
+
166
+ <div id="result" style="margin-top: 20px;"></div>
167
+ </div>
168
+
169
+ <script>
170
+ let requestsCount = 0;
171
+ let successCount = 0;
172
+ let failCount = 0;
173
+
174
+ async function runTest() {
175
+ const btn = document.getElementById('testBtn');
176
+ const loading = document.getElementById('loading');
177
+ const btnText = document.getElementById('btnText');
178
+ const resultDiv = document.getElementById('result');
179
+
180
+ btn.disabled = true;
181
+ loading.style.display = 'inline-block';
182
+ btnText.textContent = ' Executando teste...';
183
+
184
+ resultDiv.innerHTML = '<div class="info-box">⏳ Aguardando resposta do servidor...</div>';
185
+
186
+ try {
187
+ const response = await fetch('/api/test-bypass', {
188
+ method: 'POST',
189
+ headers: {
190
+ 'Content-Type': 'application/json'
191
+ }
192
+ });
193
+
194
+ const data = await response.json();
195
+
196
+ requestsCount++;
197
+ document.getElementById('requestsCount').textContent = requestsCount;
198
+
199
+ if (data.success) {
200
+ successCount++;
201
+ document.getElementById('successCount').textContent = successCount;
202
+
203
+ let resultHtml = '<div class="success">✅ Teste executado com sucesso!</div>';
204
+ resultHtml += '<pre>' + JSON.stringify(data.data, null, 2) + '</pre>';
205
+
206
+ if (data.token) {
207
+ resultHtml += '<div class="info-box"><strong>🔑 Token AWS WAF obtido:</strong><br>' +
208
+ '<code style="word-break: break-all;">' + data.token + '</code></div>';
209
+ }
210
+
211
+ resultDiv.innerHTML = resultHtml;
212
+ } else {
213
+ failCount++;
214
+ document.getElementById('failCount').textContent = failCount;
215
+
216
+ resultDiv.innerHTML = '<div class="error">❌ Falha no teste: ' + data.error + '</div>' +
217
+ '<pre>' + JSON.stringify(data.details, null, 2) + '</pre>';
218
+ }
219
+ } catch (error) {
220
+ failCount++;
221
+ document.getElementById('failCount').textContent = failCount;
222
+ resultDiv.innerHTML = '<div class="error">❌ Erro na requisição: ' + error.message + '</div>';
223
+ } finally {
224
+ btn.disabled = false;
225
+ loading.style.display = 'none';
226
+ btnText.textContent = '🚀 Executar Teste de Bypass';
227
+ }
228
+ }
229
+ </script>
230
+ </body>
231
+ </html>
232
+ """
233
+
234
+ # Dados da requisição (extraídos do curl)
235
+ URL_API = "https://jurisprudencia.stf.jus.br/api/search/search"
236
+ HEADERS = {
237
+ "Accept": "application/json, text/plain, */*",
238
+ "Content-Type": "application/json",
239
+ "User-Agent": "Mozilla/5.0 (Linux; Android 10; Pixel 4a Build/QD4A.200805.003; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/129.0.6668.71 Mobile Safari/537.36",
240
+ "Referer": "https://jurisprudencia.stf.jus.br/pages/search?base=acordaos&pesquisa_inteiro_teor=false&sinonimo=true&plural=true&radicais=false&buscaExata=true&page=1&pageSize=250&queryString=*&sort=_score&sortBy=desc&isAdvanced=true",
241
+ "Accept-Encoding": "gzip, deflate, br",
242
+ "Connection": "keep-alive",
243
+ "Origin": "https://jurisprudencia.stf.jus.br"
244
+ }
245
+
246
+ # Payload completo (resumido para não sobrecarregar o código - o payload completo está no histórico)
247
+ PAYLOAD = {
248
+ "query": {
249
+ "function_score": {
250
+ "functions": [
251
+ {"exp": {"julgamento_data": {"origin": "now", "scale": "47450d", "offset": "1095d", "decay": 0.1}}},
252
+ {"filter": {"term": {"orgao_julgador.keyword": "Tribunal Pleno"}}, "weight": 1.15}
253
+ ],
254
+ "query": {
255
+ "bool": {
256
+ "filter": [
257
+ {"query_string": {
258
+ "default_operator": "AND",
259
+ "fields": ["ementa_texto.plural^3", "acordao_ata.plural^3"],
260
+ "query": "*",
261
+ "type": "cross_fields",
262
+ "fuzziness": "AUTO:4,7"
263
+ }}
264
+ ],
265
+ "should": []
266
+ }
267
+ }
268
+ }
269
+ },
270
+ "_source": ["id", "titulo", "ementa_texto", "processo_numero", "julgamento_data"],
271
+ "size": 10,
272
+ "from": 0,
273
+ "sort": [{"_score": "desc"}]
274
+ }
275
+
276
+ def extract_waf_token_from_cookies(response):
277
+ """Extrai o token AWS WAF dos cookies da resposta"""
278
+ cookies = response.cookies
279
+ for cookie in cookies:
280
+ if cookie.get('name') == 'aws-waf-token':
281
+ return cookie.get('value')
282
+
283
+ # Tentar extrair de headers de resposta
284
+ set_cookie = response.headers.get('set-cookie', '')
285
+ if 'aws-waf-token=' in set_cookie:
286
+ import re
287
+ match = re.search(r'aws-waf-token=([^;]+)', set_cookie)
288
+ if match:
289
+ return match.group(1)
290
+
291
+ return None
292
+
293
+ def test_with_requests():
294
+ """Testa acesso direto com requests"""
295
+ logger.info("Tentando acesso direto com requests...")
296
+
297
+ try:
298
+ # Primeiro, tentar acessar a página principal para obter token inicial
299
+ session = requests.Session()
300
+
301
+ # Acessar página de busca primeiro
302
+ search_page_url = "https://jurisprudencia.stf.jus.br/pages/search"
303
+ headers_page = HEADERS.copy()
304
+ headers_page.pop("Content-Type", None)
305
+
306
+ page_response = session.get(search_page_url, headers=headers_page, timeout=30)
307
+ logger.info(f"Página principal: status {page_response.status_code}")
308
+
309
+ # Verificar se recebemos token
310
+ token = extract_waf_token_from_cookies(page_response)
311
+ if token:
312
+ logger.info(f"Token AWS WAF obtido da página: {token[:30]}...")
313
+
314
+ # Tentar a API
315
+ api_response = session.post(
316
+ URL_API,
317
+ headers=HEADERS,
318
+ json=PAYLOAD,
319
+ timeout=30
320
+ )
321
+
322
+ logger.info(f"API Response: status {api_response.status_code}")
323
+
324
+ if api_response.status_code == 200:
325
+ data = api_response.json()
326
+ return {
327
+ "success": True,
328
+ "method": "requests",
329
+ "status_code": api_response.status_code,
330
+ "token": token,
331
+ "data": data
332
+ }
333
+ elif api_response.status_code in [403, 202]:
334
+ # AWS WAF detectado
335
+ return {
336
+ "success": False,
337
+ "method": "requests",
338
+ "status_code": api_response.status_code,
339
+ "error": "AWS WAF detectado - necessário bypass com navegador",
340
+ "response_text": api_response.text[:500]
341
+ }
342
+ else:
343
+ return {
344
+ "success": False,
345
+ "method": "requests",
346
+ "status_code": api_response.status_code,
347
+ "error": f"Status inesperado: {api_response.status_code}",
348
+ "response_text": api_response.text[:500]
349
+ }
350
+
351
+ except Exception as e:
352
+ logger.error(f"Erro no requests: {str(e)}")
353
+ return {
354
+ "success": False,
355
+ "method": "requests",
356
+ "error": str(e),
357
+ "traceback": traceback.format_exc()
358
+ }
359
+
360
+ def test_with_playwright():
361
+ """Testa acesso usando Playwright com stealth"""
362
+ logger.info("Tentando acesso com Playwright + stealth...")
363
+
364
+ token = None
365
+ playwright = None
366
+
367
+ try:
368
+ with sync_playwright() as p:
369
+ # Configurar navegador com argumentos anti-detecção
370
+ browser = p.chromium.launch(
371
+ headless=True,
372
+ args=[
373
+ '--disable-blink-features=AutomationControlled',
374
+ '--disable-dev-shm-usage',
375
+ '--no-sandbox',
376
+ '--disable-setuid-sandbox',
377
+ '--disable-web-security',
378
+ '--disable-features=IsolateOrigins,site-per-process',
379
+ '--start-maximized'
380
+ ]
381
+ )
382
+
383
+ context = browser.new_context(
384
+ viewport={'width': 1920, 'height': 1080},
385
+ user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
386
+ locale='pt-BR',
387
+ timezone_id='America/Sao_Paulo',
388
+ permissions=['geolocation']
389
+ )
390
+
391
+ page = context.new_page()
392
+
393
+ # Aplicar stealth
394
+ stealth_sync(page)
395
+
396
+ # Navegar para página principal
397
+ logger.info("Navegando para página de busca...")
398
+ response = page.goto(
399
+ "https://jurisprudencia.stf.jus.br/pages/search",
400
+ wait_until='networkidle',
401
+ timeout=30000
402
+ )
403
+
404
+ if not response:
405
+ raise Exception("Sem resposta da página")
406
+
407
+ logger.info(f"Página carregada: status {response.status}")
408
+
409
+ # Aguardar um pouco para execução de JS
410
+ page.wait_for_timeout(5000)
411
+
412
+ # Coletar cookies
413
+ cookies = context.cookies()
414
+ for cookie in cookies:
415
+ if cookie.get('name') == 'aws-waf-token':
416
+ token = cookie.get('value')
417
+ logger.info(f"Token AWS WAF encontrado: {token[:30]}...")
418
+
419
+ if not token:
420
+ # Tentar extrair do localStorage
421
+ token = page.evaluate("""
422
+ () => {
423
+ for(let i=0; i<localStorage.length; i++) {
424
+ let key = localStorage.key(i);
425
+ if(key.includes('waf') || key.includes('token')) {
426
+ return localStorage.getItem(key);
427
+ }
428
+ }
429
+ return null;
430
+ }
431
+ """)
432
+ if token:
433
+ logger.info(f"Token encontrado no localStorage: {token[:30]}...")
434
+
435
+ # Fazer requisição à API via JavaScript
436
+ logger.info("Executando requisição à API via JavaScript...")
437
+ api_result = page.evaluate("""
438
+ async (payload, headers) => {
439
+ try {
440
+ const response = await fetch('https://jurisprudencia.stf.jus.br/api/search/search', {
441
+ method: 'POST',
442
+ headers: headers,
443
+ body: JSON.stringify(payload)
444
+ });
445
+
446
+ const data = await response.json();
447
+ return {
448
+ success: response.ok,
449
+ status: response.status,
450
+ data: data,
451
+ headers: Object.fromEntries(response.headers)
452
+ };
453
+ } catch (error) {
454
+ return {
455
+ success: false,
456
+ error: error.toString()
457
+ };
458
+ }
459
+ }
460
+ """, PAYLOAD, HEADERS)
461
+
462
+ browser.close()
463
+
464
+ if api_result.get('success'):
465
+ return {
466
+ "success": True,
467
+ "method": "playwright",
468
+ "status_code": api_result.get('status'),
469
+ "token": token,
470
+ "data": api_result.get('data')
471
+ }
472
+ else:
473
+ return {
474
+ "success": False,
475
+ "method": "playwright",
476
+ "error": api_result.get('error', 'Falha desconhecida'),
477
+ "token": token
478
+ }
479
+
480
+ except PlaywrightTimeoutError:
481
+ error_msg = "Timeout ao carregar página"
482
+ logger.error(error_msg)
483
+ return {
484
+ "success": False,
485
+ "method": "playwright",
486
+ "error": error_msg,
487
+ "traceback": traceback.format_exc()
488
+ }
489
+ except Exception as e:
490
+ logger.error(f"Erro no Playwright: {str(e)}")
491
+ return {
492
+ "success": False,
493
+ "method": "playwright",
494
+ "error": str(e),
495
+ "traceback": traceback.format_exc()
496
+ }
497
+
498
+ @app.route('/')
499
+ def index():
500
+ """Página principal"""
501
+ return render_template_string(HTML_TEMPLATE)
502
+
503
+ @app.route('/api/test-bypass', methods=['POST'])
504
+ def test_bypass():
505
+ """Endpoint para testar bypass"""
506
+ logger.info("Requisição de teste recebida")
507
+
508
+ result = {
509
+ "success": False,
510
+ "attempts": []
511
+ }
512
+
513
+ # Tentar primeiro com requests
514
+ requests_result = test_with_requests()
515
+ result["attempts"].append(requests_result)
516
+
517
+ # Se requests falhou, tentar com playwright
518
+ if not requests_result.get("success"):
519
+ logger.info("Requests falhou, tentando Playwright...")
520
+ time.sleep(2) # Pequena pausa entre tentativas
521
+
522
+ playwright_result = test_with_playwright()
523
+ result["attempts"].append(playwright_result)
524
+
525
+ if playwright_result.get("success"):
526
+ result["success"] = True
527
+ result["method"] = "playwright"
528
+ result["token"] = playwright_result.get("token")
529
+ result["data"] = playwright_result.get("data")
530
+ else:
531
+ result["success"] = True
532
+ result["method"] = "requests"
533
+ result["token"] = requests_result.get("token")
534
+ result["data"] = requests_result.get("data")
535
+
536
+ # Preparar resposta
537
+ response_data = {
538
+ "success": result["success"],
539
+ "timestamp": time.time(),
540
+ "method": result.get("method"),
541
+ "attempts": [
542
+ {
543
+ "method": a.get("method"),
544
+ "success": a.get("success", False),
545
+ "status_code": a.get("status_code"),
546
+ "error": a.get("error") if not a.get("success") else None
547
+ }
548
+ for a in result["attempts"]
549
+ ]
550
+ }
551
+
552
+ if result.get("success") and result.get("data"):
553
+ response_data["data"] = result["data"]
554
+ response_data["token_preview"] = result.get("token", "")[:50] + "..." if result.get("token") else None
555
+
556
+ if not result["success"]:
557
+ response_data["error"] = "Todas as tentativas falharam"
558
+ response_data["details"] = result["attempts"]
559
+ return jsonify(response_data), 500
560
+
561
+ return jsonify(response_data)
562
+
563
+ @app.route('/api/health', methods=['GET'])
564
+ def health():
565
+ """Endpoint de health check"""
566
+ return jsonify({
567
+ "status": "healthy",
568
+ "timestamp": time.time(),
569
+ "playwright_installed": True,
570
+ "python_version": sys.version
571
+ })
572
+
573
+ @app.route('/api/token-status', methods=['GET'])
574
+ def token_status():
575
+ """Verifica status do token atual"""
576
+ # Este endpoint poderia verificar se o token ainda é válido
577
+ return jsonify({
578
+ "message": "Para obter um token, execute /api/test-bypass primeiro"
579
+ })
580
+
581
+ if __name__ == '__main__':
582
+ # Verificar dependências na inicialização
583
+ logger.info("Iniciando aplicação de bypass AWS WAF")
584
+ logger.info(f"Python version: {sys.version}")
585
+
586
+ # Testar playwright na inicialização
587
+ try:
588
+ with sync_playwright() as p:
589
+ browser = p.chromium.launch(headless=True)
590
+ logger.info("Playwright iniciado com sucesso")
591
+ browser.close()
592
+ except Exception as e:
593
+ logger.error(f"Erro ao iniciar Playwright: {str(e)}")
594
+ logger.error("Verifique se as dependências do sistema estão instaladas")
595
+
596
+ # Iniciar servidor Flask
597
+ port = int(os.environ.get('PORT', 7860))
598
+ app.run(host='0.0.0.0', port=port, debug=False)