SmartPenTest
OWASP Vulnerability Scanner — AI Analysis
Workshop
FloodBoy
Arena
Scan Configuration
mqtt: connecting...
Target URL
Scan Profile
Quick Scan — เร็ว ตรวจพื้นฐาน
Standard — มาตรฐาน OWASP Top 10
Deep Scan — ตรวจลึก ใช้เวลานาน
Scan ID
Scan Scenario
Normal Scan
ผลปกติ
SQL Injection
พบ SQLi
XSS Found
พบ XSS
All Clean
ปลอดภัย
Server Down
เซิร์ฟเวอร์ล่ม
Start Scan
Scan Progress
Ready to scan
Severity Distribution
Vulnerabilities Found
กด Start Scan เพื่อเริ่มสแกน
Scan Statistics
0
Total Vulns
0
Critical
0
High
0
Medium
0
Pages Crawled
0
Risk Score
AI Security Analysis
🤖
AI Security Advisor
เริ่มสแกนเพื่อรับการวิเคราะห์จาก AI
Ask AI — วิเคราะห์เพิ่มเติม
OWASP Top 10 Coverage
A01 Broken Access Control —
-
A02 Cryptographic Failures —
-
A03 Injection —
-
A04 Insecure Design —
-
A05 Security Misconfiguration —
-
A06 Vulnerable Components —
-
A07 Auth Failures —
-
A08 Data Integrity —
-
A09 Logging Failures —
-
A10 SSRF —
-
' }, { severity: 'critical', category: 'A03', title: 'Stored XSS — Comment Section', desc: 'พบ Stored XSS ที่ช่องแสดงความคิดเห็น script จะทำงานกับผู้ใช้ทุกคนที่เข้าชมหน้านี้', url: '/post/123#comments' }, { severity: 'high', category: 'A03', title: 'DOM-based XSS — URL Fragment', desc: 'พบ DOM XSS ผ่าน URL fragment ที่ถูกใส่เข้า innerHTML โดยตรง', url: '/page#
' }, { severity: 'medium', category: 'A05', title: 'Missing CSP Header', desc: 'ไม่มี Content-Security-Policy header ทำให้ XSS สามารถโหลด external scripts ได้', url: '/ (Response headers)' }, { severity: 'medium', category: 'A03', title: 'HTML Injection — Profile Name', desc: 'สามารถใส่ HTML tags ในชื่อโปรไฟล์ แสดงผลบนหน้าอื่นโดยไม่ escape', url: '/profile/edit' }, { severity: 'low', category: 'A05', title: 'HttpOnly Flag Missing on Session Cookie', desc: 'Cookie ไม่มี HttpOnly flag ทำให้ XSS สามารถอ่าน session cookie ได้', url: '/ (Set-Cookie header)' }, ], clean: [ { severity: 'info', category: 'A05', title: 'Server Fingerprint', desc: 'ตรวจพบ web server เป็น Nginx แต่ไม่ได้เปิดเผยเวอร์ชัน', url: '/ (Server header)' }, { severity: 'low', category: 'A09', title: 'Strict-Transport-Security Missing on Subdomain', desc: 'Subdomain บางตัวไม่มี HSTS header แต่ main domain มีแล้ว', url: 'cdn.testsite.com' }, ], overloaded: [ { severity: 'high', category: 'A05', title: 'Server 503 — Service Unavailable', desc: 'เซิร์ฟเวอร์ตอบ 503 ระหว่างการสแกน อาจมี rate limiting หรือ server overloaded', url: '/ (HTTP 503)' }, { severity: 'medium', category: 'A05', title: 'Connection Timeout on Deep Paths', desc: 'หลาย URL ตอบช้าเกิน 30 วินาที สแกนไม่สมบูรณ์', url: '/api/admin/*' }, { severity: 'info', category: 'A05', title: 'WAF Detected — Cloudflare', desc: 'ตรวจพบ Web Application Firewall (Cloudflare) อาจบล็อกบาง scan requests', url: '/ (cf-ray header)' }, ], }; // ===== AI EXPLANATIONS ===== const AI_EXPLANATIONS = { normal: 'จากการสแกนพบช่องโหว่หลายจุด ที่สำคัญที่สุดคือ Default Admin Credentials (Critical) — ต้องเปลี่ยนรหัสผ่าน admin ทันที เพราะผู้โจมตีสามารถเข้าถึงระบบได้ง่ายมาก\n\nSQL Injection ที่หน้า login ก็อันตรายมาก แนะนำให้ใช้ Prepared Statements แทน string concatenation\n\nสำหรับ IDOR ที่ /api/users ให้เพิ่ม authorization check ทุกครั้งที่เข้าถึงข้อมูลผู้ใช้', sqli: 'พบช่องโหว่ SQL Injection ร้ายแรงหลายจุด! นี่คือสถานการณ์ฉุกเฉิน\n\nBlind SQLi + Union SQLi หมายความว่าผู้โจมตีสามารถ:\n- ดึงข้อมูลทั้ง database (username, password, ข้อมูลส่วนตัว)\n- แก้ไข/ลบข้อมูล\n- อาจเข้าถึง OS ของเซิร์ฟเวอร์ได้\n\nวิธีแก้เร่งด่วน:\n1. ใช้ Prepared Statements / Parameterized Queries ทุกจุด\n2. ใช้ ORM แทน raw SQL\n3. ปิด error messages ใน production\n4. Hash รหัสผ่านด้วย bcrypt ทันที', xss: 'พบ Cross-Site Scripting (XSS) หลายประเภท — โดยเฉพาะ Stored XSS ที่อันตรายมาก!\n\nStored XSS หมายความว่าผู้โจมตีฝัง script ไว้ใน database แล้ว script จะทำงานกับผู้ใช้ทุกคน สามารถ:\n- ขโมย session cookie → เข้าบัญชีผู้ใช้\n- Redirect ไปหน้า phishing\n- ติดตั้ง keylogger บนหน้าเว็บ\n\nวิธีแก้:\n1. Escape HTML output ทุกจุด (ใช้ library เช่น DOMPurify)\n2. เพิ่ม Content-Security-Policy header\n3. เพิ่ม HttpOnly flag บน session cookie\n4. ใช้ template engine ที่ auto-escape', clean: 'สแกนเสร็จสมบูรณ์ ผลลัพธ์ดีมาก!\n\nพบเพียงข้อสังเกตเล็กน้อย ไม่มีช่องโหว่ร้ายแรง เว็บไซต์นี้มีการป้องกันที่ดี มี security headers ครบถ้วน และไม่มี injection vulnerabilities\n\nแนะนำเพิ่มเติม: เพิ่ม HSTS header ให้ครบทุก subdomain', overloaded: 'การสแกนไม่สมบูรณ์เนื่องจากเซิร์ฟเวอร์ตอบสนองช้า/ล่ม\n\nอาจเกิดจาก:\n1. Rate limiting / WAF บล็อกการสแกน\n2. เซิร์ฟเวอร์ overloaded จริง\n3. ทรัพยากรไม่เพียงพอ\n\nแนะนำ:\n- ลดความเร็วในการสแกน\n- ขอ whitelist IP จากผู้ดูแลระบบ\n- สแกนนอกเวลาใช้งาน peak', }; const AI_EXTRA = [ 'เพิ่มเติม: ควรทำ penetration test อย่างน้อยปีละ 2 ครั้ง และหลังจากอัปเดตระบบทุกครั้ง', 'แนะนำให้ทีมพัฒนาเรียนรู้ OWASP Top 10 ทุกคน เพราะ security เป็นหน้าที่ของทุกคนในทีม ไม่ใช่แค่ security team', 'ควรตั้ง Security Champions ในแต่ละทีมพัฒนา เพื่อให้มีคนรับผิดชอบเรื่อง security โดยเฉพาะ', 'ลองใช้ SAST (Static Application Security Testing) เช่น SonarQube ร่วมกับ CI/CD pipeline เพื่อตรวจจับช่องโหว่ตั้งแต่ขั้นตอนเขียนโค้ด', 'สำหรับ API ควรเพิ่ม rate limiting, input validation, และ authentication ให้ครบทุก endpoint', ]; // ===== SCENARIO BUTTONS ===== document.getElementById('scenario-grid').addEventListener('click', e => { const btn = e.target.closest('.glitch-btn'); if (!btn) return; document.querySelectorAll('.glitch-btn').forEach(b => b.classList.remove('active')); btn.classList.add('active'); scenario = btn.dataset.scenario; }); // ===== CHART SETUP ===== const ctx = document.getElementById('chart').getContext('2d'); const chart = new Chart(ctx, { type: 'doughnut', data: { labels: ['Critical', 'High', 'Medium', 'Low', 'Info'], datasets: [{ data: [0, 0, 0, 0, 0], backgroundColor: ['#f87171', '#f0883e', '#fbbf24', '#58a6ff', '#8b949e'], borderColor: '#161b22', borderWidth: 3, }], }, options: { responsive: true, maintainAspectRatio: false, cutout: '60%', plugins: { legend: { position: 'bottom', labels: { color: '#8b949e', font: { size: 10 }, boxWidth: 12, padding: 8 } }, }, }, }); // ===== SCAN PHASES ===== const PHASES = [ { label: 'Reconnaissance — สำรวจเป้าหมาย', duration: 1500 }, { label: 'Spider — ค้นหา URL ทั้งหมด', duration: 2000 }, { label: 'Active Scan — ทดสอบช่องโหว่', duration: 3000 }, { label: 'Analysis — วิเคราะห์ผลลัพธ์', duration: 1500 }, ]; // ===== START SCAN ===== window.startScan = async function() { if (scanning) return; scanning = true; const btn = document.getElementById('btn-scan'); btn.textContent = 'Scanning...'; btn.className = 'start-btn on'; btn.disabled = true; // Reset state foundVulns = []; stats = { total: 0, critical: 0, high: 0, medium: 0, low: 0, info: 0, pages: 0, risk: 0 }; updateStats(); chart.data.datasets[0].data = [0, 0, 0, 0, 0]; chart.update(); document.getElementById('vuln-list').innerHTML = ''; document.getElementById('ai-content').innerHTML = '
Scanning...
'; document.getElementById('btn-ask-ai').disabled = true; // Reset OWASP coverage for (let i = 1; i <= 10; i++) { document.getElementById('ow-a' + (i < 10 ? '0' + i : i)).textContent = '-'; document.getElementById('ow-a' + (i < 10 ? '0' + i : i)).style.color = '#484f58'; } const vulns = VULN_DB[scenario] || VULN_DB.normal; let progress = 0; const totalDuration = PHASES.reduce((a, p) => a + p.duration, 0); // Run phases for (const phase of PHASES) { document.getElementById('phase-label').textContent = phase.label; const steps = 20; const stepDur = phase.duration / steps; const stepPct = (phase.duration / totalDuration * 100) / steps; for (let i = 0; i < steps; i++) { progress += stepPct; document.getElementById('progress-fill').style.width = Math.min(progress, 100) + '%'; document.getElementById('progress-fill').textContent = Math.round(progress) + '%'; // Randomly add pages crawled if (Math.random() < 0.3) { stats.pages += Math.floor(Math.random() * 3) + 1; updateStats(); } await sleep(stepDur); } // Add vulns during Active Scan phase if (phase.label.includes('Active Scan')) { for (let v = 0; v < vulns.length; v++) { await sleep(300 + Math.random() * 400); addVuln(vulns[v]); } } } // Complete document.getElementById('progress-fill').style.width = '100%'; document.getElementById('progress-fill').textContent = '100%'; document.getElementById('phase-label').textContent = 'Scan complete'; // Calculate risk score stats.risk = Math.min(100, stats.critical * 30 + stats.high * 15 + stats.medium * 5 + stats.low * 1); updateStats(); // Show AI analysis await typeAI(AI_EXPLANATIONS[scenario] || AI_EXPLANATIONS.normal); document.getElementById('btn-ask-ai').disabled = false; // Publish summary via MQTT if (mqttClient?.connected) { const scanId = document.getElementById('scan-id').value; mqttClient.publish('pentest/' + scanId + '/summary', JSON.stringify({ url: document.getElementById('target-url').value, scenario, total: stats.total, critical: stats.critical, high: stats.high, medium: stats.medium, risk: stats.risk, })); } btn.textContent = 'Start Scan'; btn.className = 'start-btn off'; btn.disabled = false; scanning = false; }; function addVuln(vuln) { foundVulns.push(vuln); stats.total++; stats[vuln.severity]++; // Update chart const sevMap = { critical: 0, high: 1, medium: 2, low: 3, info: 4 }; chart.data.datasets[0].data[sevMap[vuln.severity]]++; chart.update(); // Update OWASP coverage const owNum = vuln.category.replace('A', ''); const owEl = document.getElementById('ow-a' + owNum); if (owEl) { const sevColors = { critical: '#f87171', high: '#f0883e', medium: '#fbbf24', low: '#58a6ff', info: '#8b949e' }; owEl.textContent = vuln.severity.toUpperCase(); owEl.style.color = sevColors[vuln.severity]; } // Add card const list = document.getElementById('vuln-list'); if (stats.total === 1) list.innerHTML = ''; const card = document.createElement('div'); card.className = 'vuln-card'; card.innerHTML = '
' + '
' + vuln.severity + '
' + '
' + vuln.category + '
' + '
' + vuln.title + '
' + '
' + '
' + vuln.desc + '
' + '
' + escapeHtml(vuln.url) + '
'; list.appendChild(card); // Publish via MQTT if (mqttClient?.connected) { const scanId = document.getElementById('scan-id').value; mqttClient.publish('pentest/' + scanId + '/result', JSON.stringify({ url: document.getElementById('target-url').value, severity: vuln.severity, category: vuln.category, description: vuln.title, })); } updateStats(); } function escapeHtml(text) { const d = document.createElement('div'); d.textContent = text; return d.innerHTML; } // ===== AI TYPING ===== async function typeAI(text) { const el = document.getElementById('ai-content'); el.innerHTML = ''; const lines = text.split('\n'); let fullHtml = ''; for (const line of lines) { for (let i = 0; i < line.length; i++) { fullHtml += line[i]; el.innerHTML = fullHtml + '
'; await sleep(15 + Math.random() * 15); } fullHtml += '
'; el.innerHTML = fullHtml + '
'; await sleep(100); } el.innerHTML = fullHtml; } window.askAI = async function() { const btn = document.getElementById('btn-ask-ai'); btn.disabled = true; const extra = AI_EXTRA[Math.floor(Math.random() * AI_EXTRA.length)]; await typeAI(extra); btn.disabled = false; }; function updateStats() { document.getElementById('s-total').textContent = stats.total; document.getElementById('s-critical').textContent = stats.critical; document.getElementById('s-high').textContent = stats.high; document.getElementById('s-medium').textContent = stats.medium; document.getElementById('s-pages').textContent = stats.pages; document.getElementById('s-risk').textContent = stats.risk; } function sleep(ms) { return new Promise(r => setTimeout(r, ms)); } // ===== MQTT ===== function initMQTT() { mqttClient = mqtt.connect(MQTT_BROKER); mqttClient.on('connect', () => { document.getElementById('dot-mqtt').className = 'dot on'; document.getElementById('status-mqtt').textContent = 'mqtt: connected'; }); mqttClient.on('offline', () => { document.getElementById('dot-mqtt').className = 'dot off'; document.getElementById('status-mqtt').textContent = 'mqtt: offline'; }); } // ===== INIT ===== initMQTT();