import React, { useEffect, useState } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { ArrowLeft, ChevronLeft, ChevronRight, Crosshair, Fingerprint, Shield, Clock, Wifi, Lock, FileKey } from 'lucide-react'; import api from '../utils/api'; import './Dashboard.css'; interface AttackerData { uuid: string; ip: string; first_seen: string; last_seen: string; event_count: number; service_count: number; decky_count: number; services: string[]; deckies: string[]; traversal_path: string | null; is_traversal: boolean; bounty_count: number; credential_count: number; fingerprints: any[]; commands: { service: string; decky: string; command: string; timestamp: string }[]; updated_at: string; } // ─── Fingerprint rendering ─────────────────────────────────────────────────── const fpTypeLabel: Record = { ja3: 'TLS FINGERPRINT', ja4l: 'LATENCY (JA4L)', tls_resumption: 'SESSION RESUMPTION', tls_certificate: 'CERTIFICATE', http_useragent: 'HTTP USER-AGENT', vnc_client_version: 'VNC CLIENT', jarm: 'JARM', hassh_server: 'HASSH SERVER', tcpfp: 'TCP/IP STACK', }; const fpTypeIcon: Record = { ja3: , ja4l: , tls_resumption: , tls_certificate: , http_useragent: , vnc_client_version: , jarm: , hassh_server: , tcpfp: , }; function getPayload(bounty: any): any { if (bounty?.payload && typeof bounty.payload === 'object') return bounty.payload; if (bounty?.payload && typeof bounty.payload === 'string') { try { return JSON.parse(bounty.payload); } catch { return bounty; } } return bounty; } const HashRow: React.FC<{ label: string; value?: string | null }> = ({ label, value }) => { if (!value) return null; return (
{label} {value}
); }; const Tag: React.FC<{ children: React.ReactNode; color?: string }> = ({ children, color }) => ( {children} ); const FpTlsHashes: React.FC<{ p: any }> = ({ p }) => (
{(p.tls_version || p.sni || p.alpn) && (
{p.tls_version && {p.tls_version}} {p.sni && SNI: {p.sni}} {p.alpn && ALPN: {p.alpn}} {p.dst_port && :{p.dst_port}}
)}
); const FpLatency: React.FC<{ p: any }> = ({ p }) => (
RTT {p.rtt_ms} ms
{p.client_ttl && (
TTL {p.client_ttl}
)}
); const FpResumption: React.FC<{ p: any }> = ({ p }) => { const mechanisms = typeof p.mechanisms === 'string' ? p.mechanisms.split(',') : Array.isArray(p.mechanisms) ? p.mechanisms : []; return (
{mechanisms.map((m: string) => ( {m.trim().toUpperCase().replace(/_/g, ' ')} ))}
); }; const FpCertificate: React.FC<{ p: any }> = ({ p }) => (
{p.subject_cn} {p.self_signed === 'true' && ( SELF-SIGNED )}
{p.issuer && (
ISSUER: {p.issuer}
)} {(p.not_before || p.not_after) && (
VALIDITY: {p.not_before || '?'} — {p.not_after || '?'}
)} {p.sans && (
SANs: {(typeof p.sans === 'string' ? p.sans.split(',') : p.sans).map((san: string) => ( {san.trim()} ))}
)}
); const FpJarm: React.FC<{ p: any }> = ({ p }) => (
{(p.target_ip || p.target_port) && (
{p.target_ip && {p.target_ip}} {p.target_port && :{p.target_port}}
)}
); const FpHassh: React.FC<{ p: any }> = ({ p }) => (
{p.ssh_banner && (
BANNER: {p.ssh_banner}
)} {p.kex_algorithms && (
KEX ALGORITHMS
{p.kex_algorithms.split(',').map((algo: string) => ( {algo.trim()} ))}
)} {p.encryption_s2c && (
ENCRYPTION (S→C)
{p.encryption_s2c.split(',').map((algo: string) => ( {algo.trim()} ))}
)} {(p.target_ip || p.target_port) && (
{p.target_ip && {p.target_ip}} {p.target_port && :{p.target_port}}
)}
); const FpTcpStack: React.FC<{ p: any }> = ({ p }) => (
{p.ttl && (
TTL {p.ttl}
)} {p.window_size && (
WIN {p.window_size}
)} {p.mss && (
MSS {p.mss}
)}
{p.df_bit === '1' && DF} {p.sack_ok === '1' && SACK} {p.timestamp === '1' && TS} {p.window_scale && p.window_scale !== '-1' && WSCALE:{p.window_scale}}
{p.options_order && (
OPTS: {p.options_order}
)} {(p.target_ip || p.target_port) && (
{p.target_ip && {p.target_ip}} {p.target_port && :{p.target_port}}
)}
); const FpGeneric: React.FC<{ p: any }> = ({ p }) => (
{p.value ? ( {p.value} ) : ( {JSON.stringify(p)} )}
); const FingerprintCard: React.FC<{ bounty: any }> = ({ bounty }) => { const p = getPayload(bounty); const fpType: string = p.fingerprint_type || 'unknown'; const label = fpTypeLabel[fpType] || fpType.toUpperCase().replace(/_/g, ' '); const icon = fpTypeIcon[fpType] || ; let content: React.ReactNode; switch (fpType) { case 'ja3': content = ; break; case 'ja4l': content = ; break; case 'tls_resumption': content = ; break; case 'tls_certificate': content = ; break; case 'jarm': content = ; break; case 'hassh_server': content = ; break; case 'tcpfp': content = ; break; default: content = ; } return (
{icon} {label}
{content}
); }; // ─── Main component ───────────────────────────────────────────────────────── const AttackerDetail: React.FC = () => { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const [attacker, setAttacker] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [serviceFilter, setServiceFilter] = useState(null); // Commands pagination state const [commands, setCommands] = useState([]); const [cmdTotal, setCmdTotal] = useState(0); const [cmdPage, setCmdPage] = useState(1); const cmdLimit = 50; useEffect(() => { const fetchAttacker = async () => { setLoading(true); try { const res = await api.get(`/attackers/${id}`); setAttacker(res.data); } catch (err: any) { if (err.response?.status === 404) { setError('ATTACKER NOT FOUND'); } else { setError('FAILED TO LOAD ATTACKER PROFILE'); } } finally { setLoading(false); } }; fetchAttacker(); }, [id]); useEffect(() => { if (!id) return; const fetchCommands = async () => { try { const offset = (cmdPage - 1) * cmdLimit; let url = `/attackers/${id}/commands?limit=${cmdLimit}&offset=${offset}`; if (serviceFilter) url += `&service=${encodeURIComponent(serviceFilter)}`; const res = await api.get(url); setCommands(res.data.data); setCmdTotal(res.data.total); } catch (err: any) { if (err.response?.status === 422) { alert("Fuck off."); } setCommands([]); setCmdTotal(0); } }; fetchCommands(); }, [id, cmdPage, serviceFilter]); // Reset command page when service filter changes useEffect(() => { setCmdPage(1); }, [serviceFilter]); if (loading) { return (
LOADING THREAT PROFILE...
); } if (error || !attacker) { return (
{error || 'ATTACKER NOT FOUND'}
); } return (
{/* Back Button */} {/* Header */}

{attacker.ip}

{attacker.is_traversal && ( TRAVERSAL )}
{/* Stats Row */}
{attacker.event_count}
EVENTS
{attacker.bounty_count}
BOUNTIES
{attacker.credential_count}
CREDENTIALS
{attacker.service_count}
SERVICES
{attacker.decky_count}
DECKIES
{/* Timestamps */}

TIMELINE

FIRST SEEN: {new Date(attacker.first_seen).toLocaleString()}
LAST SEEN: {new Date(attacker.last_seen).toLocaleString()}
UPDATED: {new Date(attacker.updated_at).toLocaleString()}
{/* Services */}

SERVICES TARGETED

{attacker.services.length > 0 ? attacker.services.map((svc) => { const isActive = serviceFilter === svc; return ( setServiceFilter(isActive ? null : svc)} title={isActive ? 'Clear filter' : `Filter by ${svc.toUpperCase()}`} > {svc.toUpperCase()} ); }) : ( No services recorded )}
{/* Deckies & Traversal */}

DECKY INTERACTIONS

{attacker.traversal_path ? (
TRAVERSAL PATH: {attacker.traversal_path}
) : (
{attacker.deckies.map((d) => ( {d} ))} {attacker.deckies.length === 0 && No deckies recorded}
)}
{/* Commands */} {(() => { const cmdTotalPages = Math.ceil(cmdTotal / cmdLimit); return (

COMMANDS ({cmdTotal}{serviceFilter ? ` ${serviceFilter.toUpperCase()}` : ''})

{cmdTotalPages > 1 && (
Page {cmdPage} of {cmdTotalPages}
)}
{commands.length > 0 ? (
{commands.map((cmd, i) => ( ))}
TIMESTAMP SERVICE DECKY COMMAND
{cmd.timestamp ? new Date(cmd.timestamp).toLocaleString() : '-'} {cmd.service} {cmd.decky} {cmd.command}
) : (
{serviceFilter ? `NO ${serviceFilter.toUpperCase()} COMMANDS CAPTURED` : 'NO COMMANDS CAPTURED'}
)}
); })()} {/* Fingerprints */} {(() => { const filteredFps = serviceFilter ? attacker.fingerprints.filter((fp) => { const p = getPayload(fp); return p.service === serviceFilter; }) : attacker.fingerprints; return (

FINGERPRINTS ({filteredFps.length}{serviceFilter ? ` / ${attacker.fingerprints.length}` : ''})

{filteredFps.length > 0 ? (
{filteredFps.map((fp, i) => ( ))}
) : (
{serviceFilter ? `NO ${serviceFilter.toUpperCase()} FINGERPRINTS CAPTURED` : 'NO FINGERPRINTS CAPTURED'}
)}
); })()} {/* UUID footer */}
UUID: {attacker.uuid}
); }; export default AttackerDetail;