merge testing->tomerge/main #7

Open
anti wants to merge 242 commits from testing into tomerge/main
2 changed files with 228 additions and 0 deletions
Showing only changes of commit 09d9c0ec74 - Show all commits

View File

@@ -32,6 +32,9 @@ const fpTypeLabel: Record<string, string> = {
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<string, React.ReactNode> = {
@@ -41,6 +44,9 @@ const fpTypeIcon: Record<string, React.ReactNode> = {
tls_certificate: <FileKey size={14} />,
http_useragent: <Shield size={14} />,
vnc_client_version: <Lock size={14} />,
jarm: <Crosshair size={14} />,
hassh_server: <Lock size={14} />,
tcpfp: <Wifi size={14} />,
};
function getPayload(bounty: any): any {
@@ -159,6 +165,104 @@ const FpCertificate: React.FC<{ p: any }> = ({ p }) => (
</div>
);
const FpJarm: React.FC<{ p: any }> = ({ p }) => (
<div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
<HashRow label="HASH" value={p.hash} />
{(p.target_ip || p.target_port) && (
<div style={{ display: 'flex', gap: '8px', marginTop: '4px', flexWrap: 'wrap' }}>
{p.target_ip && <Tag color="var(--accent-color)">{p.target_ip}</Tag>}
{p.target_port && <Tag>:{p.target_port}</Tag>}
</div>
)}
</div>
);
const FpHassh: React.FC<{ p: any }> = ({ p }) => (
<div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
<HashRow label="HASH" value={p.hash} />
{p.ssh_banner && (
<div>
<span className="dim" style={{ fontSize: '0.7rem' }}>BANNER: </span>
<span className="matrix-text" style={{ fontFamily: 'monospace', fontSize: '0.8rem' }}>{p.ssh_banner}</span>
</div>
)}
{p.kex_algorithms && (
<details style={{ marginTop: '2px' }}>
<summary className="dim" style={{ fontSize: '0.7rem', cursor: 'pointer', letterSpacing: '1px' }}>
KEX ALGORITHMS
</summary>
<div style={{ display: 'flex', gap: '6px', flexWrap: 'wrap', marginTop: '4px' }}>
{p.kex_algorithms.split(',').map((algo: string) => (
<Tag key={algo}>{algo.trim()}</Tag>
))}
</div>
</details>
)}
{p.encryption_s2c && (
<details>
<summary className="dim" style={{ fontSize: '0.7rem', cursor: 'pointer', letterSpacing: '1px' }}>
ENCRYPTION (SC)
</summary>
<div style={{ display: 'flex', gap: '6px', flexWrap: 'wrap', marginTop: '4px' }}>
{p.encryption_s2c.split(',').map((algo: string) => (
<Tag key={algo}>{algo.trim()}</Tag>
))}
</div>
</details>
)}
{(p.target_ip || p.target_port) && (
<div style={{ display: 'flex', gap: '8px', marginTop: '4px', flexWrap: 'wrap' }}>
{p.target_ip && <Tag color="var(--accent-color)">{p.target_ip}</Tag>}
{p.target_port && <Tag>:{p.target_port}</Tag>}
</div>
)}
</div>
);
const FpTcpStack: React.FC<{ p: any }> = ({ p }) => (
<div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
<HashRow label="HASH" value={p.hash} />
<div style={{ display: 'flex', gap: '24px', alignItems: 'center', flexWrap: 'wrap' }}>
{p.ttl && (
<div>
<span className="dim" style={{ fontSize: '0.7rem' }}>TTL </span>
<span className="matrix-text" style={{ fontSize: '1.2rem', fontWeight: 'bold' }}>{p.ttl}</span>
</div>
)}
{p.window_size && (
<div>
<span className="dim" style={{ fontSize: '0.7rem' }}>WIN </span>
<span className="matrix-text" style={{ fontSize: '1.2rem', fontWeight: 'bold' }}>{p.window_size}</span>
</div>
)}
{p.mss && (
<div>
<span className="dim" style={{ fontSize: '0.7rem' }}>MSS </span>
<span className="matrix-text" style={{ fontSize: '1rem' }}>{p.mss}</span>
</div>
)}
</div>
<div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap' }}>
{p.df_bit === '1' && <Tag color="#ff6b6b">DF</Tag>}
{p.sack_ok === '1' && <Tag>SACK</Tag>}
{p.timestamp === '1' && <Tag>TS</Tag>}
{p.window_scale && p.window_scale !== '-1' && <Tag>WSCALE:{p.window_scale}</Tag>}
</div>
{p.options_order && (
<div>
<span className="dim" style={{ fontSize: '0.7rem' }}>OPTS: </span>
<span className="matrix-text" style={{ fontFamily: 'monospace', fontSize: '0.8rem' }}>{p.options_order}</span>
</div>
)}
{(p.target_ip || p.target_port) && (
<div style={{ display: 'flex', gap: '8px', marginTop: '2px', flexWrap: 'wrap' }}>
{p.target_ip && <Tag color="var(--accent-color)">{p.target_ip}</Tag>}
{p.target_port && <Tag>:{p.target_port}</Tag>}
</div>
)}
</div>
);
const FpGeneric: React.FC<{ p: any }> = ({ p }) => (
<div>
{p.value ? (
@@ -193,6 +297,15 @@ const FingerprintCard: React.FC<{ bounty: any }> = ({ bounty }) => {
case 'tls_certificate':
content = <FpCertificate p={p} />;
break;
case 'jarm':
content = <FpJarm p={p} />;
break;
case 'hassh_server':
content = <FpHassh p={p} />;
break;
case 'tcpfp':
content = <FpTcpStack p={p} />;
break;
default:
content = <FpGeneric p={p} />;
}

View File

@@ -14,6 +14,118 @@ interface BountyEntry {
payload: any;
}
const _FINGERPRINT_LABELS: Record<string, string> = {
fingerprint_type: 'TYPE',
ja3: 'JA3',
ja3s: 'JA3S',
ja4: 'JA4',
ja4s: 'JA4S',
ja4l: 'JA4L',
sni: 'SNI',
alpn: 'ALPN',
dst_port: 'PORT',
mechanisms: 'MECHANISM',
raw_ciphers: 'CIPHERS',
hash: 'HASH',
target_ip: 'TARGET',
target_port: 'PORT',
ssh_banner: 'BANNER',
kex_algorithms: 'KEX',
encryption_s2c: 'ENC (S→C)',
mac_s2c: 'MAC (S→C)',
compression_s2c: 'COMP (S→C)',
raw: 'RAW',
ttl: 'TTL',
window_size: 'WINDOW',
df_bit: 'DF',
mss: 'MSS',
window_scale: 'WSCALE',
sack_ok: 'SACK',
timestamp: 'TS',
options_order: 'OPTS ORDER',
};
const _TAG_STYLE: React.CSSProperties = {
fontSize: '0.65rem',
padding: '1px 6px',
borderRadius: '3px',
border: '1px solid rgba(238, 130, 238, 0.4)',
backgroundColor: 'rgba(238, 130, 238, 0.08)',
color: 'var(--accent-color)',
whiteSpace: 'nowrap',
flexShrink: 0,
};
const _HASH_STYLE: React.CSSProperties = {
fontSize: '0.75rem',
fontFamily: 'monospace',
opacity: 0.85,
wordBreak: 'break-all',
};
const FingerprintPayload: React.FC<{ payload: any }> = ({ payload }) => {
if (!payload || typeof payload !== 'object') {
return <span className="dim" style={{ fontSize: '0.8rem' }}>{JSON.stringify(payload)}</span>;
}
// For simple payloads like tls_resumption with just type + mechanism
const keys = Object.keys(payload);
const isSimple = keys.length <= 3;
if (isSimple) {
return (
<div style={{ display: 'flex', gap: '10px', alignItems: 'center', flexWrap: 'wrap' }}>
{keys.map((k) => {
const val = payload[k];
if (val === null || val === undefined) return null;
const label = _FINGERPRINT_LABELS[k] || k.toUpperCase();
return (
<span key={k} style={{ display: 'inline-flex', alignItems: 'center', gap: '5px' }}>
<span style={_TAG_STYLE}>{label}</span>
<span style={_HASH_STYLE}>{String(val)}</span>
</span>
);
})}
</div>
);
}
// Full fingerprint — show priority fields as labeled rows
const priorityKeys = ['fingerprint_type', 'ja3', 'ja3s', 'ja4', 'ja4s', 'ja4l', 'sni', 'alpn', 'dst_port', 'mechanisms', 'hash', 'target_ip', 'target_port', 'ssh_banner', 'ttl', 'window_size', 'mss', 'options_order'];
const shown = priorityKeys.filter((k) => payload[k] !== undefined && payload[k] !== null);
const rest = keys.filter((k) => !priorityKeys.includes(k) && payload[k] !== null && payload[k] !== undefined);
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
{shown.map((k) => {
const label = _FINGERPRINT_LABELS[k] || k.toUpperCase();
const val = String(payload[k]);
return (
<div key={k} style={{ display: 'flex', alignItems: 'flex-start', gap: '6px' }}>
<span style={_TAG_STYLE}>{label}</span>
<span style={_HASH_STYLE}>{val}</span>
</div>
);
})}
{rest.length > 0 && (
<details style={{ marginTop: '2px' }}>
<summary className="dim" style={{ fontSize: '0.7rem', cursor: 'pointer', letterSpacing: '1px' }}>
+{rest.length} MORE FIELDS
</summary>
<div style={{ display: 'flex', flexDirection: 'column', gap: '3px', marginTop: '4px' }}>
{rest.map((k) => (
<div key={k} style={{ display: 'flex', alignItems: 'flex-start', gap: '6px' }}>
<span style={_TAG_STYLE}>{(_FINGERPRINT_LABELS[k] || k).toUpperCase()}</span>
<span style={{ ..._HASH_STYLE, fontSize: '0.7rem', opacity: 0.6 }}>{String(payload[k])}</span>
</div>
))}
</div>
</details>
)}
</div>
);
};
const Bounty: React.FC = () => {
const [searchParams, setSearchParams] = useSearchParams();
const query = searchParams.get('q') || '';
@@ -83,6 +195,7 @@ const Bounty: React.FC = () => {
>
<option value="">ALL TYPES</option>
<option value="credential">CREDENTIALS</option>
<option value="fingerprint">FINGERPRINTS</option>
<option value="payload">PAYLOADS</option>
</select>
</div>
@@ -167,6 +280,8 @@ const Bounty: React.FC = () => {
<span><span className="dim" style={{ marginRight: '4px' }}>user:</span>{b.payload.username}</span>
<span><span className="dim" style={{ marginRight: '4px' }}>pass:</span>{b.payload.password}</span>
</div>
) : b.bounty_type === 'fingerprint' ? (
<FingerprintPayload payload={b.payload} />
) : (
<span className="dim" style={{ fontSize: '0.8rem' }}>{JSON.stringify(b.payload)}</span>
)}