""" IVT 봇 트래픽 생성기 - 다양한 IVT 패턴 시뮬레이션 - 테스트 목적 전용 """ import gradio as gr import requests import random import string import time import threading from datetime import datetime from concurrent.futures import ThreadPoolExecutor import json # ============================================ # 봇 User-Agent 목록 # ============================================ BOT_USER_AGENTS = { "googlebot": "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)", "bingbot": "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)", "yandex": "Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)", "baidu": "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)", "curl": "curl/7.68.0", "wget": "Wget/1.20.3 (linux-gnu)", "python_requests": "python-requests/2.28.0", "scrapy": "Scrapy/2.7.0 (+https://scrapy.org)", "selenium": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Selenium", "puppeteer": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/120.0.0.0 Safari/537.36", "phantomjs": "Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) PhantomJS/2.1.1 Safari/538.1", "java": "Java/17.0.1", "go_http": "Go-http-client/1.1", "node_fetch": "node-fetch/1.0 (+https://github.com/node-fetch/node-fetch)", "axios": "axios/1.4.0", "httpx": "python-httpx/0.24.0", "postman": "PostmanRuntime/7.32.0", } NORMAL_USER_AGENTS = [ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15", "Mozilla/5.0 (iPhone; CPU iPhone OS 17_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1", "Mozilla/5.0 (Linux; Android 14; SM-S918B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36", ] SUSPICIOUS_REFERRERS = [ "https://traffic-exchange.com/surf", "https://autosurf.pro/visit", "https://free-traffic-bot.com/", "https://paid-to-click.net/earn", "https://hit-exchange.org/view", "https://buy-cheap-traffic.com/", "https://cashsurf.io/browse", ] DATACENTER_IPS = [ "52.94.76.0", # AWS "35.192.0.0", # GCP "20.36.0.0", # Azure "104.16.0.0", # Cloudflare "159.89.0.0", # DigitalOcean "172.104.0.0", # Linode "45.32.0.0", # Vultr ] # ============================================ # 트래픽 로그 # ============================================ traffic_log = [] log_lock = threading.Lock() def add_log(message: str, status: str = "info"): with log_lock: timestamp = datetime.now().strftime("%H:%M:%S") icon = {"success": "✅", "error": "❌", "warning": "⚠️", "info": "ℹ️"}.get(status, "📝") traffic_log.append(f"[{timestamp}] {icon} {message}") if len(traffic_log) > 100: traffic_log.pop(0) def get_logs(): with log_lock: return "\n".join(traffic_log[-50:]) if traffic_log else "로그 없음" # ============================================ # 핑거프린트 생성기 # ============================================ def generate_fingerprint(pattern_type: str, custom_settings: dict = None) -> dict: """IVT 패턴에 따른 핑거프린트 생성""" fp = { "timestamp": datetime.now().isoformat(), "sessionToken": f"bot_{int(time.time())}_{random.randint(1000, 9999)}", } # 기본 설정 settings = custom_settings or {} if pattern_type == "bot_ua": # 봇 User-Agent 패턴 bot_type = settings.get("bot_type", random.choice(list(BOT_USER_AGENTS.keys()))) fp["userAgent"] = BOT_USER_AGENTS.get(bot_type, BOT_USER_AGENTS["curl"]) fp["platform"] = "Linux x86_64" fp["webdriver"] = False fp["pluginsCount"] = "0" fp["hasMouseMove"] = False fp["hasScroll"] = False fp["hasClick"] = False fp["dwellTime"] = random.uniform(0.1, 0.5) elif pattern_type == "headless": # Headless 브라우저 패턴 fp["userAgent"] = random.choice([ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/120.0.0.0 Safari/537.36", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36" ]) fp["webdriver"] = True fp["webGLVendor"] = "Google Inc. (Google)" fp["webGLRenderer"] = "ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device (Subzero)), SwiftShader driver)" fp["platform"] = "Linux x86_64" fp["pluginsCount"] = "0" fp["hasMouseMove"] = False fp["hasScroll"] = False fp["dwellTime"] = random.uniform(0.05, 0.3) elif pattern_type == "rapid_click": # 빠른 반복 클릭 패턴 fp["userAgent"] = random.choice(NORMAL_USER_AGENTS) fp["platform"] = random.choice(["Win32", "MacIntel", "Linux x86_64"]) fp["webdriver"] = False fp["hasMouseMove"] = True fp["hasScroll"] = False fp["hasClick"] = True fp["dwellTime"] = random.uniform(0.1, 0.8) # 매우 짧은 체류 fp["pluginsCount"] = str(random.randint(3, 10)) elif pattern_type == "no_interaction": # 무상호작용 패턴 fp["userAgent"] = random.choice(NORMAL_USER_AGENTS) fp["platform"] = random.choice(["Win32", "MacIntel"]) fp["webdriver"] = False fp["hasMouseMove"] = False fp["hasScroll"] = False fp["hasClick"] = False fp["hasKeypress"] = False fp["dwellTime"] = random.uniform(0.5, 2.0) fp["pluginsCount"] = str(random.randint(0, 2)) elif pattern_type == "datacenter_ip": # 데이터센터 IP 패턴 (시뮬레이션) fp["userAgent"] = random.choice(NORMAL_USER_AGENTS) fp["platform"] = "Linux x86_64" fp["webdriver"] = False fp["hasMouseMove"] = random.choice([True, False]) fp["dwellTime"] = random.uniform(1, 5) # IP는 실제로는 서버에서 감지되지만, 여기서는 플래그만 설정 fp["_simulated_datacenter"] = True elif pattern_type == "suspicious_referrer": # 의심스러운 Referrer 패턴 fp["userAgent"] = random.choice(NORMAL_USER_AGENTS) fp["platform"] = random.choice(["Win32", "MacIntel"]) fp["referrer"] = random.choice(SUSPICIOUS_REFERRERS) fp["webdriver"] = False fp["hasMouseMove"] = True fp["dwellTime"] = random.uniform(2, 10) elif pattern_type == "device_mismatch": # 디바이스 불일치 패턴 (모바일 UA + 데스크톱 플랫폼) fp["userAgent"] = "Mozilla/5.0 (iPhone; CPU iPhone OS 17_2 like Mac OS X) AppleWebKit/605.1.15 Mobile Safari/604.1" fp["platform"] = "Win32" # 불일치! fp["screenWidth"] = "1920" fp["screenHeight"] = "1080" fp["maxTouchPoints"] = "0" # 터치 없음 (모바일인데?) fp["webdriver"] = False fp["dwellTime"] = random.uniform(3, 10) elif pattern_type == "geo_mismatch": # 지역-언어 불일치 패턴 fp["userAgent"] = random.choice(NORMAL_USER_AGENTS) fp["platform"] = random.choice(["Win32", "MacIntel"]) fp["languages"] = "zh-CN,zh" # 중국어인데 fp["timezone"] = "-540" # 한국 시간대 (불일치) fp["webdriver"] = False fp["dwellTime"] = random.uniform(5, 15) elif pattern_type == "abnormal_screen": # 비정상 해상도 패턴 fp["userAgent"] = random.choice(NORMAL_USER_AGENTS) fp["platform"] = "Win32" fp["screenWidth"] = random.choice(["0", "1", "100"]) fp["screenHeight"] = random.choice(["0", "1", "100"]) fp["webdriver"] = False fp["dwellTime"] = random.uniform(1, 5) elif pattern_type == "mixed_attack": # 복합 공격 패턴 (여러 플래그 동시) fp["userAgent"] = BOT_USER_AGENTS["selenium"] fp["platform"] = "Linux x86_64" fp["webdriver"] = True fp["webGLRenderer"] = "SwiftShader" fp["pluginsCount"] = "0" fp["hasMouseMove"] = False fp["hasScroll"] = False fp["hasClick"] = False fp["dwellTime"] = 0.1 fp["referrer"] = random.choice(SUSPICIOUS_REFERRERS) fp["screenWidth"] = "800" fp["screenHeight"] = "600" else: # normal # 정상 트래픽 패턴 fp["userAgent"] = random.choice(NORMAL_USER_AGENTS) fp["platform"] = random.choice(["Win32", "MacIntel", "Linux x86_64"]) fp["webdriver"] = False fp["hasMouseMove"] = True fp["hasScroll"] = True fp["hasClick"] = True fp["hasKeypress"] = random.choice([True, False]) fp["dwellTime"] = random.uniform(10, 60) fp["pluginsCount"] = str(random.randint(3, 15)) fp["languages"] = random.choice(["ko-KR,ko,en-US,en", "en-US,en", "ja-JP,ja,en"]) # 공통 필드 추가 fp.setdefault("platform", "Win32") fp.setdefault("timezone", str(random.choice([-540, -480, -420, 0, 60, 120]))) fp.setdefault("deviceMemory", str(random.choice([2, 4, 8, 16]))) fp.setdefault("screenWidth", str(random.choice([1366, 1920, 2560, 1440]))) fp.setdefault("screenHeight", str(random.choice([768, 1080, 1440, 900]))) fp.setdefault("screenDepth", "24") fp.setdefault("hardwareConcurrency", str(random.choice([2, 4, 8, 12, 16]))) fp.setdefault("languages", "en-US,en") fp.setdefault("doNotTrack", random.choice(["1", "0", "NC"])) fp.setdefault("maxTouchPoints", "0") fp.setdefault("pluginsCount", str(random.randint(0, 10))) fp.setdefault("referrer", "direct") fp.setdefault("currentUrl", "https://test-site.com/page") fp.setdefault("currentHost", "test-site.com") fp.setdefault("webGLVendor", "Google Inc. (NVIDIA)") fp.setdefault("webGLRenderer", "ANGLE (NVIDIA, NVIDIA GeForce RTX 3080)") fp.setdefault("canvasHash", ''.join(random.choices(string.hexdigits, k=50))) fp.setdefault("audioSampleRate", "48000") fp.setdefault("videoFormats", '{"mp4":"probably","webm":"probably"}') return fp # ============================================ # 트래픽 전송 # ============================================ def send_single_request(target_url: str, fingerprint: dict, request_num: int) -> bool: """단일 요청 전송""" try: # API 엔드포인트 구성 if not target_url.endswith('/'): target_url += '/' # Gradio API 엔드포인트로 전송 시도 api_url = target_url.rstrip('/') + "/api/collect" headers = { "Content-Type": "application/json", "User-Agent": fingerprint.get("userAgent", "python-requests/2.28.0"), "Referer": fingerprint.get("referrer", ""), } response = requests.post( api_url, json=fingerprint, headers=headers, timeout=10 ) if response.status_code == 200: add_log(f"요청 #{request_num} 성공 - {fingerprint.get('sessionToken', 'unknown')[:20]}", "success") return True else: add_log(f"요청 #{request_num} 실패 (HTTP {response.status_code})", "warning") return False except requests.exceptions.Timeout: add_log(f"요청 #{request_num} 타임아웃", "error") return False except requests.exceptions.ConnectionError: add_log(f"요청 #{request_num} 연결 실패", "error") return False except Exception as e: add_log(f"요청 #{request_num} 오류: {str(e)[:50]}", "error") return False def generate_traffic( target_url: str, pattern_type: str, request_count: int, delay_ms: int, concurrent: int, bot_type: str = "random", progress=gr.Progress() ) -> tuple: """트래픽 생성 메인 함수""" if not target_url: return "❌ 대상 URL을 입력하세요", get_logs() if not target_url.startswith("http"): target_url = "https://" + target_url add_log(f"트래픽 생성 시작: {target_url}", "info") add_log(f"패턴: {pattern_type}, 요청 수: {request_count}, 딜레이: {delay_ms}ms", "info") success_count = 0 fail_count = 0 custom_settings = {} if bot_type != "random" and pattern_type == "bot_ua": custom_settings["bot_type"] = bot_type # 빠른 반복 클릭 테스트인 경우 딜레이 무시 if pattern_type == "rapid_click": delay_ms = min(delay_ms, 100) with ThreadPoolExecutor(max_workers=concurrent) as executor: futures = [] for i in range(request_count): progress((i + 1) / request_count, desc=f"요청 {i+1}/{request_count}") fp = generate_fingerprint(pattern_type, custom_settings) future = executor.submit(send_single_request, target_url, fp, i + 1) futures.append(future) if delay_ms > 0 and i < request_count - 1: time.sleep(delay_ms / 1000) for future in futures: if future.result(): success_count += 1 else: fail_count += 1 result = f"""## 📊 트래픽 생성 완료 | 항목 | 결과 | |------|------| | **대상 URL** | `{target_url}` | | **패턴** | {pattern_type} | | **총 요청** | {request_count}회 | | **성공** | ✅ {success_count}회 | | **실패** | ❌ {fail_count}회 | | **성공률** | {success_count/request_count*100:.1f}% | """ add_log(f"완료: 성공 {success_count}, 실패 {fail_count}", "success" if fail_count == 0 else "warning") return result, get_logs() def clear_logs(): global traffic_log with log_lock: traffic_log = [] return "로그가 초기화되었습니다." # ============================================ # Gradio UI # ============================================ with gr.Blocks(title="IVT Bot Traffic Generator") as demo: gr.Markdown("""# 🤖 IVT 봇 트래픽 생성기 **⚠️ 주의: 테스트 목적으로만 사용하세요. 본인 소유 서버에서만 테스트하세요.** IVT 탐지 시스템 테스트를 위한 다양한 부정 트래픽 패턴을 생성합니다. """) with gr.Tabs(): # 탭 1: 트래픽 생성 with gr.TabItem("🚀 트래픽 생성"): with gr.Row(): with gr.Column(scale=2): target_url = gr.Textbox( label="🎯 대상 URL", placeholder="https://your-ivt-server.hf.space", info="IVT 탐지 서버의 URL을 입력하세요" ) pattern_type = gr.Dropdown( label="📋 IVT 패턴 선택", choices=[ ("🤖 봇 User-Agent", "bot_ua"), ("👻 Headless 브라우저 (WebDriver)", "headless"), ("⚡ 빠른 반복 클릭", "rapid_click"), ("🚫 무상호작용 (No Interaction)", "no_interaction"), ("🏢 데이터센터 IP 시뮬레이션", "datacenter_ip"), ("🔗 의심스러운 Referrer", "suspicious_referrer"), ("📱 디바이스 불일치 (Mobile UA + Desktop)", "device_mismatch"), ("🗺️ 지역-언어 불일치", "geo_mismatch"), ("📐 비정상 해상도", "abnormal_screen"), ("💀 복합 공격 (다중 플래그)", "mixed_attack"), ("✅ 정상 트래픽", "normal"), ], value="bot_ua", info="생성할 IVT 패턴을 선택하세요" ) with gr.Row(): request_count = gr.Slider( label="요청 수", minimum=1, maximum=100, value=5, step=1, info="생성할 요청 수" ) delay_ms = gr.Slider( label="딜레이 (ms)", minimum=0, maximum=5000, value=500, step=100, info="요청 간 딜레이" ) with gr.Row(): concurrent = gr.Slider( label="동시 요청 수", minimum=1, maximum=10, value=1, step=1, info="병렬 처리 수" ) # 봇 UA 상세 선택 (패턴이 bot_ua일 때만 표시) bot_type = gr.Dropdown( label="🤖 봇 유형 (봇 UA 패턴 전용)", choices=[ ("랜덤", "random"), ("Googlebot", "googlebot"), ("Bingbot", "bingbot"), ("curl", "curl"), ("wget", "wget"), ("Python Requests", "python_requests"), ("Scrapy", "scrapy"), ("Selenium", "selenium"), ("Puppeteer", "puppeteer"), ("PhantomJS", "phantomjs"), ("Java", "java"), ("Go HTTP", "go_http"), ("Axios", "axios"), ("Postman", "postman"), ], value="random", visible=True ) with gr.Row(): generate_btn = gr.Button("🚀 트래픽 생성", variant="primary", scale=2) clear_btn = gr.Button("🗑️ 로그 초기화", scale=1) with gr.Column(scale=1): result_output = gr.Markdown(value="**설정 후 '트래픽 생성' 버튼을 클릭하세요**") gr.Markdown("### 📜 실행 로그") log_output = gr.Textbox( label="", value="로그 없음", lines=15, max_lines=20, interactive=False ) generate_btn.click( fn=generate_traffic, inputs=[target_url, pattern_type, request_count, delay_ms, concurrent, bot_type], outputs=[result_output, log_output] ) clear_btn.click(fn=clear_logs, outputs=log_output) # 탭 2: 패턴 설명 with gr.TabItem("📖 패턴 설명"): gr.Markdown(""" ## 🛡️ IVT 패턴 상세 설명 각 패턴이 어떤 부정 트래픽을 시뮬레이션하는지 설명합니다. --- ### 🤖 봇 User-Agent (`bot_ua`) **탐지 점수: 40점** 웹 크롤러, 스크레이퍼 등의 봇 UA를 사용합니다. | 봇 유형 | User-Agent 예시 | |---------|----------------| | Googlebot | `compatible; Googlebot/2.1` | | curl | `curl/7.68.0` | | Python | `python-requests/2.28.0` | | Scrapy | `Scrapy/2.7.0` | --- ### 👻 Headless 브라우저 (`headless`) **탐지 점수: 50-85점** Selenium, Puppeteer 등 자동화 도구를 탐지합니다. - `navigator.webdriver = true` - WebGL Renderer: `SwiftShader` (소프트웨어 렌더링) - 플러그인 수: 0개 --- ### ⚡ 빠른 반복 클릭 (`rapid_click`) **탐지 점수: 25-45점** 클릭 팜, 자동 클릭 봇을 시뮬레이션합니다. - 5초 내 2회 이상 클릭 - 1분 내 5회 이상 클릭 - 체류시간 1초 미만 --- ### 🚫 무상호작용 (`no_interaction`) **탐지 점수: 20점** 페이지 로드 후 아무 동작도 하지 않는 패턴입니다. - 마우스 이동 없음 - 스크롤 없음 - 클릭 없음 - 키보드 입력 없음 --- ### 🏢 데이터센터 IP (`datacenter_ip`) **탐지 점수: 25-30점** AWS, GCP, Azure 등 클라우드 서버에서 오는 트래픽입니다. - ASN에 'amazon', 'google', 'microsoft' 포함 - 호스팅/서버 IP로 분류됨 --- ### 🔗 의심스러운 Referrer (`suspicious_referrer`) **탐지 점수: 35점** 트래픽 교환, PTC(Paid-to-Click) 사이트에서 오는 트래픽입니다. - `traffic-exchange.com` - `paid-to-click.net` - `autosurf.pro` --- ### 📱 디바이스 불일치 (`device_mismatch`) **탐지 점수: 20점** UA와 실제 디바이스 정보가 맞지 않는 경우입니다. - 모바일 UA + 데스크톱 플랫폼 - 모바일 UA + 터치 미지원 --- ### 🗺️ 지역-언어 불일치 (`geo_mismatch`) **탐지 점수: 15점** IP 위치와 브라우저 언어 설정이 맞지 않습니다. - 한국 IP + 중국어 언어 - 미국 IP + 일본어 언어 --- ### 📐 비정상 해상도 (`abnormal_screen`) **탐지 점수: 20-30점** 화면 해상도가 비정상적인 경우입니다. - 0x0, 1x1 해상도 - 300 미만의 너비/높이 --- ### 💀 복합 공격 (`mixed_attack`) **탐지 점수: 70점 이상 (GIVT)** 여러 IVT 패턴을 동시에 포함합니다. - WebDriver 감지 - 봇 UA - SwiftShader - 무상호작용 - 의심스러운 Referrer --- ### ✅ 정상 트래픽 (`normal`) **탐지 점수: 0-10점** 일반 사용자 행동을 시뮬레이션합니다. - 정상 브라우저 UA - 마우스/스크롤/클릭 상호작용 - 10-60초 체류시간 - 정상 플러그인 수 """) # 탭 3: 빠른 테스트 with gr.TabItem("⚡ 빠른 테스트"): gr.Markdown(""" ### 원클릭 테스트 각 버튼을 클릭하면 해당 패턴으로 5회 요청을 즉시 전송합니다. """) quick_url = gr.Textbox( label="🎯 대상 URL", placeholder="https://your-ivt-server.hf.space" ) quick_result = gr.Markdown() quick_log = gr.Textbox(label="로그", lines=10, interactive=False) with gr.Row(): btn_bot = gr.Button("🤖 봇 UA", variant="secondary") btn_headless = gr.Button("👻 Headless", variant="secondary") btn_rapid = gr.Button("⚡ 반복클릭", variant="secondary") btn_noact = gr.Button("🚫 무상호작용", variant="secondary") with gr.Row(): btn_referrer = gr.Button("🔗 의심 Referrer", variant="secondary") btn_device = gr.Button("📱 디바이스불일치", variant="secondary") btn_mixed = gr.Button("💀 복합공격", variant="stop") btn_normal = gr.Button("✅ 정상", variant="primary") # 빠른 테스트 버튼 연결 btn_bot.click( fn=lambda url: generate_traffic(url, "bot_ua", 5, 200, 1, "random"), inputs=[quick_url], outputs=[quick_result, quick_log] ) btn_headless.click( fn=lambda url: generate_traffic(url, "headless", 5, 200, 1, "random"), inputs=[quick_url], outputs=[quick_result, quick_log] ) btn_rapid.click( fn=lambda url: generate_traffic(url, "rapid_click", 10, 50, 2, "random"), inputs=[quick_url], outputs=[quick_result, quick_log] ) btn_noact.click( fn=lambda url: generate_traffic(url, "no_interaction", 5, 200, 1, "random"), inputs=[quick_url], outputs=[quick_result, quick_log] ) btn_referrer.click( fn=lambda url: generate_traffic(url, "suspicious_referrer", 5, 200, 1, "random"), inputs=[quick_url], outputs=[quick_result, quick_log] ) btn_device.click( fn=lambda url: generate_traffic(url, "device_mismatch", 5, 200, 1, "random"), inputs=[quick_url], outputs=[quick_result, quick_log] ) btn_mixed.click( fn=lambda url: generate_traffic(url, "mixed_attack", 5, 200, 1, "random"), inputs=[quick_url], outputs=[quick_result, quick_log] ) btn_normal.click( fn=lambda url: generate_traffic(url, "normal", 5, 500, 1, "random"), inputs=[quick_url], outputs=[quick_result, quick_log] ) if __name__ == "__main__": demo.launch()