perf:前端大改1

This commit is contained in:
qi4l
2026-04-30 15:07:24 +08:00
parent 3043a43b5b
commit 680d0cb333
12 changed files with 507 additions and 187 deletions
-1
View File
@@ -10,7 +10,6 @@
},
"dependencies": {
"axios": "^1.6.0",
"liquid-glass-react": "^1.1.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.28.0"
+8
View File
@@ -44,6 +44,14 @@ export function startServers(data) {
return api.post('/servers/start', data)
}
export function stopServer(server) {
return api.post('/servers/stop', { server })
}
export function toggleServer(server) {
return api.post('/servers/toggle', { server })
}
export function getGadgets() {
return api.get('/gadgets')
}
+185
View File
@@ -31,6 +31,10 @@
--card-radius: 24px;
--btn-radius: 13px;
--input-radius: 13px;
--card-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.06);
--card-shadow-md: 0 4px 16px rgba(0, 0, 0, 0.04), 0 2px 6px rgba(0, 0, 0, 0.06), 0 0 0 0.5px rgba(0, 0, 0, 0.04);
--card-shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.05), 0 3px 10px rgba(0, 0, 0, 0.07), 0 0 0 1px rgba(0, 0, 0, 0.04);
--card-bg: rgba(255, 255, 255, 0.72);
--font-mono: 'SF Mono', 'JetBrains Mono', 'Fira Code', monospace;
--transition-fast: 200ms cubic-bezier(0.22, 0.1, 0.22, 1);
--transition-fluid: 420ms cubic-bezier(0.22, 0.1, 0.22, 1);
@@ -50,6 +54,10 @@
--success-bg: rgba(48, 209, 88, 0.10);
--danger: #FF453A;
--danger-bg: rgba(255, 69, 58, 0.08);
--card-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.20), 0 1px 2px rgba(0, 0, 0, 0.30);
--card-shadow-md: 0 4px 16px rgba(0, 0, 0, 0.24), 0 2px 6px rgba(0, 0, 0, 0.32), 0 0 0 0.5px rgba(255, 255, 255, 0.06);
--card-shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.28), 0 3px 10px rgba(0, 0, 0, 0.36), 0 0 0 1px rgba(255, 255, 255, 0.06);
--card-bg: rgba(28, 28, 30, 0.68);
}
html {
@@ -171,6 +179,89 @@ code, pre, .mono {
.glass-card {
padding: 28px;
background: var(--card-bg);
backdrop-filter: blur(28px) saturate(180%);
-webkit-backdrop-filter: blur(28px) saturate(180%);
border-radius: var(--card-radius);
box-shadow: var(--card-shadow-md);
border: 1px solid var(--border-color);
transition: background var(--transition-fluid), box-shadow var(--transition-fluid), border-color var(--transition-fluid), transform var(--transition-fluid);
}
.mode-segment-control {
position: relative;
display: flex;
align-items: center;
background: var(--bg-input);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 3px;
gap: 2px;
transition: background var(--transition-fluid), border-color var(--transition-fluid);
}
.mode-segment-control::before {
content: '';
position: absolute;
top: 3px;
left: 3px;
width: calc(50% - 4px);
height: calc(100% - 6px);
background: var(--accent);
border-radius: 10px;
transition: transform 420ms cubic-bezier(0.34, 1.56, 0.64, 1);
box-shadow: 0 2px 12px var(--accent-glow);
z-index: 0;
}
.mode-segment-control.gadget-mode::before {
transform: translateX(calc(100% + 2px));
}
.mode-segment-btn {
position: relative;
z-index: 1;
flex: 1;
padding: 7px 16px;
border: none;
border-radius: 10px;
background: transparent;
color: var(--text-secondary);
font-family: 'Inter', sans-serif;
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: color 320ms cubic-bezier(0.22, 0.1, 0.22, 1);
white-space: nowrap;
letter-spacing: -0.2px;
-webkit-tap-highlight-color: transparent;
}
.mode-segment-btn:active {
transform: scale(0.96);
transition: transform 100ms cubic-bezier(0.22, 0.1, 0.22, 1);
}
.mode-segment-btn.active {
color: #FFFFFF;
}
.section-enter {
animation: sectionSlideIn 520ms cubic-bezier(0.22, 0.1, 0, 1) both;
}
.section-enter:nth-child(2) { animation-delay: 40ms; }
.section-enter:nth-child(3) { animation-delay: 80ms; }
@keyframes sectionSlideIn {
from {
opacity: 0;
transform: translateY(12px) scale(0.985);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.glass-card h2 {
@@ -201,6 +292,28 @@ code, pre, .mono {
transition: background var(--transition-fluid), border-color var(--transition-fluid);
}
.status-clickable {
cursor: pointer;
transition: background var(--transition-fast), border-color var(--transition-fast), transform var(--transition-fast), box-shadow var(--transition-fast);
}
.status-clickable:hover {
background: var(--bg-primary);
border-color: var(--accent);
box-shadow: var(--card-shadow-sm);
transform: translateY(-1px);
}
.status-clickable:active {
transform: scale(0.98);
transition: transform 100ms ease;
}
.status-toggling {
opacity: 0.6;
pointer-events: none;
}
.status-label {
font-size: 11px;
font-weight: 500;
@@ -389,6 +502,78 @@ code, pre, .mono {
.control-bar .btn { padding: 8px 16px; font-size: 13px; }
.tab-segment-control {
position: relative;
display: inline-flex;
align-items: center;
background: var(--bg-input);
border: 1px solid var(--border-color);
border-radius: 11px;
padding: 3px;
gap: 2px;
transition: background var(--transition-fluid), border-color var(--transition-fluid);
}
.tab-segment-control::before {
content: '';
position: absolute;
top: 3px;
left: 3px;
width: calc(50% - 4px);
height: calc(100% - 6px);
background: var(--accent);
border-radius: 9px;
transition: transform 380ms cubic-bezier(0.34, 1.50, 0.64, 1);
box-shadow: 0 2px 10px var(--accent-glow);
z-index: 0;
}
.tab-segment-control.config-tab::before {
transform: translateX(calc(100% + 2px));
}
.tab-segment-btn {
position: relative;
z-index: 1;
padding: 6px 20px;
border: none;
border-radius: 9px;
background: transparent;
color: var(--text-secondary);
font-family: 'Inter', sans-serif;
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: color 280ms cubic-bezier(0.22, 0.1, 0.22, 1);
white-space: nowrap;
letter-spacing: -0.2px;
-webkit-tap-highlight-color: transparent;
}
.tab-segment-btn:active {
transform: scale(0.96);
transition: transform 100ms cubic-bezier(0.22, 0.1, 0.22, 1);
}
.tab-segment-btn.active {
color: #FFFFFF;
}
.tab-content-enter {
animation: tabContentSlide 380ms cubic-bezier(0.22, 0.1, 0, 1) both;
}
@keyframes tabContentSlide {
from {
opacity: 0;
transform: translateY(6px) scale(0.99);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.config-form {
display: grid;
grid-template-columns: 1fr 1fr;
+162 -161
View File
@@ -1,32 +1,29 @@
import { useState, useEffect, useMemo, useRef } from 'react'
import { useState, useEffect, useMemo, useCallback } from 'react'
import { useNavigate } from 'react-router-dom'
import LiquidGlass from 'liquid-glass-react'
import { useTheme } from '../context/ThemeContext'
import {
getStatus, startServers as apiStartServers, getGadgets,
getStatus, toggleServer, getGadgets,
generatePayload as apiGenerate, updateConfig, setAuthToken
} from '../api'
export default function Dashboard() {
const navigate = useNavigate()
const { theme, toggleTheme } = useTheme()
const containerRef = useRef(null)
const [mode, setMode] = useState('jndi')
const [animKey, setAnimKey] = useState(0)
const [status, setStatus] = useState({})
const [loading, setLoading] = useState(false)
const [msg, setMsg] = useState({ success: '', error: '' })
const [activeTab, setActiveTab] = useState('servers')
const [toggling, setToggling] = useState(null)
const [gadgets, setGadgets] = useState([])
const [gadgetSearch, setGadgetSearch] = useState('')
const [selectedGadget, setSelectedGadget] = useState('')
const [payloadCmd, setPayloadCmd] = useState('')
const [payloadResult, setPayloadResult] = useState('')
const [serverCfg, setServerCfg] = useState({
ldap: false, ldaps: false, http: false, rmi: false
})
const [configForm, setConfigForm] = useState({
ip: '', ldapPort: 1389, ldapsPort: 1669, httpPort: 3456, rmiPort: 1099, codeBase: ''
ip: '', ldapPort: 1389, ldapsPort: 1669, httpPort: 3456, rmiPort: 1099,
AESkey: '123', user: '', PASSWD: '', TLSProxy: false, keyPass: '', certFile: ''
})
const filteredGadgets = useMemo(() => {
@@ -34,6 +31,13 @@ export default function Dashboard() {
return gadgets.filter(g => g.name.toLowerCase().includes(gadgetSearch.toLowerCase()))
}, [gadgetSearch, gadgets])
const switchMode = useCallback((next) => {
if (next === mode) return
setMode(next)
setAnimKey(k => k + 1)
setMsg({ success: '', error: '' })
}, [mode])
async function loadStatus() {
try {
const res = await getStatus()
@@ -45,7 +49,12 @@ export default function Dashboard() {
ldapsPort: res.data.ldapsPort || 1669,
httpPort: res.data.httpPort || 3456,
rmiPort: res.data.rmiPort || 1099,
codeBase: res.data.codeBase || ''
AESkey: res.data.AESkey || '123',
user: res.data.user || '',
PASSWD: res.data.PASSWD || '',
TLSProxy: res.data.TLSProxy || false,
keyPass: res.data.keyPass || '',
certFile: res.data.certFile || ''
}))
} catch (e) {
if (e.response && e.response.status === 401) {
@@ -62,24 +71,18 @@ export default function Dashboard() {
} catch (e) { /* ignore */ }
}
async function handleStartServers() {
setLoading(true)
async function handleToggleServer(server) {
setToggling(server)
setMsg({ success: '', error: '' })
try {
const res = await apiStartServers({
ldap: serverCfg.ldap, ldaps: serverCfg.ldaps,
http: serverCfg.http, rmi: serverCfg.rmi,
ip: configForm.ip, ldapPort: configForm.ldapPort,
ldapsPort: configForm.ldapsPort, httpPort: configForm.httpPort,
rmiPort: configForm.rmiPort
})
const res = await toggleServer(server)
if (res.data.success) {
setMsg({ success: 'Servers starting: ' + (res.data.started.join(', ') || 'none'), error: '' })
setMsg({ success: server.toUpperCase() + ' ' + (res.data.running ? 'started' : 'stopped'), error: '' })
setStatus(res.data.status)
}
setTimeout(loadStatus, 2000)
} catch (e) {
setMsg({ success: '', error: 'Failed to start servers' })
} finally { setLoading(false) }
setMsg({ success: '', error: 'Failed to toggle ' + server.toUpperCase() })
} finally { setToggling(null) }
}
async function handleSaveConfig() {
@@ -118,27 +121,31 @@ export default function Dashboard() {
useEffect(() => { loadStatus(); loadGadgets() }, [])
const liquidProps = {
mouseContainer: containerRef,
elasticity: 0.07,
blurAmount: 0.042,
saturation: 120,
displacementScale: 36,
cornerRadius: 24,
overLight: theme === 'light',
}
return (
<div ref={containerRef}>
<div>
<div className="dashboard-bg" />
<div className="page-shell">
<LiquidGlass {...liquidProps} style={{ marginBottom: 20 }}>
<div className="glass-card" style={{ marginBottom: 20 }}>
<div className="header">
<h1>
<span className="logo-dot" />
JYso
</h1>
<div className={'mode-segment-control' + (mode === 'gadget' ? ' gadget-mode' : '')}>
<button
className={'mode-segment-btn' + (mode === 'jndi' ? ' active' : '')}
onClick={() => switchMode('jndi')}
>
JNDI EXP
</button>
<button
className={'mode-segment-btn' + (mode === 'gadget' ? ' active' : '')}
onClick={() => switchMode('gadget')}
>
Gadget
</button>
</div>
<div className="header-right">
<button
className="theme-toggle"
@@ -156,136 +163,130 @@ export default function Dashboard() {
<button className="logout-btn" onClick={logout}>Logout</button>
</div>
</div>
</LiquidGlass>
</div>
{msg.success && <div className="success-msg">{msg.success}</div>}
{msg.error && <div className="error-msg">{msg.error}</div>}
{msg.success && <div className="success-msg section-enter">{msg.success}</div>}
{msg.error && <div className="error-msg section-enter">{msg.error}</div>}
<LiquidGlass {...liquidProps} style={{ marginBottom: 20 }}>
<div className="glass-card">
<h2>Server Status</h2>
<div className="status-grid">
<div className="status-item">
<span className="status-label">LDAP ({status.ldapPort})</span>
<span className={'status-value ' + (status.ldapRunning ? 'status-online' : 'status-offline')}>
{status.ldapRunning ? 'ONLINE' : 'OFFLINE'}
</span>
</div>
<div className="status-item">
<span className="status-label">LDAPS ({status.ldapsPort})</span>
<span className={'status-value ' + (status.ldapsRunning ? 'status-online' : 'status-offline')}>
{status.ldapsRunning ? 'ONLINE' : 'OFFLINE'}
</span>
</div>
<div className="status-item">
<span className="status-label">HTTP ({status.httpPort})</span>
<span className={'status-value ' + (status.httpRunning ? 'status-online' : 'status-offline')}>
{status.httpRunning ? 'ONLINE' : 'OFFLINE'}
</span>
</div>
<div className="status-item">
<span className="status-label">RMI ({status.rmiPort})</span>
<span className={'status-value ' + (status.rmiRunning ? 'status-online' : 'status-offline')}>
{status.rmiRunning ? 'ONLINE' : 'OFFLINE'}
</span>
</div>
<div className="status-item">
<span className="status-label">IP Address</span>
<span className="status-value" style={{ color: 'var(--accent)' }}>{status.ip || '0.0.0.0'}</span>
</div>
<div className="status-item">
<span className="status-label">Version</span>
<span className="status-value" style={{ color: 'var(--accent)' }}>{status.version || '-'}</span>
</div>
</div>
</div>
</LiquidGlass>
<LiquidGlass {...liquidProps}>
<div className="glass-card">
<h2>Controls</h2>
<div className="control-bar">
<button className="btn btn-primary" style={{ width: 'auto' }} onClick={loadStatus}>
Refresh Status
</button>
<button
className={'btn btn-secondary' + (activeTab === 'servers' ? ' active-tab' : '')}
onClick={() => setActiveTab('servers')}>Servers</button>
<button
className={'btn btn-secondary' + (activeTab === 'config' ? ' active-tab' : '')}
onClick={() => setActiveTab('config')}>Configuration</button>
<button
className={'btn btn-secondary' + (activeTab === 'payload' ? ' active-tab' : '')}
onClick={() => setActiveTab('payload')}>Payload Generator</button>
</div>
{activeTab === 'servers' && (
<div>
<div className="grid-2">
<label className="form-group">
<input type="checkbox" checked={serverCfg.ldap}
onChange={e => setServerCfg({ ...serverCfg, ldap: e.target.checked })} /> LDAP
</label>
<label className="form-group">
<input type="checkbox" checked={serverCfg.ldaps}
onChange={e => setServerCfg({ ...serverCfg, ldaps: e.target.checked })} /> LDAPS
</label>
<label className="form-group">
<input type="checkbox" checked={serverCfg.http}
onChange={e => setServerCfg({ ...serverCfg, http: e.target.checked })} /> HTTP
</label>
<label className="form-group">
<input type="checkbox" checked={serverCfg.rmi}
onChange={e => setServerCfg({ ...serverCfg, rmi: e.target.checked })} /> RMI
</label>
<div key={animKey}>
{mode === 'jndi' && (
<>
<div className="glass-card section-enter" style={{ marginBottom: 20 }}>
<h2>Server Status</h2>
<div className="status-grid">
<div className={'status-item status-clickable' + (toggling === 'ldap' ? ' status-toggling' : '')}
onClick={() => handleToggleServer('ldap')}
title="Click to toggle LDAP server">
<span className="status-label">LDAP ({status.ldapPort})</span>
<span className={'status-value ' + (status.ldapRunning ? 'status-online' : 'status-offline')}>
{status.ldapRunning ? 'ONLINE' : 'OFFLINE'}
</span>
</div>
<div className={'status-item status-clickable' + (toggling === 'ldaps' ? ' status-toggling' : '')}
onClick={() => handleToggleServer('ldaps')}
title="Click to toggle LDAPS server">
<span className="status-label">LDAPS ({status.ldapsPort})</span>
<span className={'status-value ' + (status.ldapsRunning ? 'status-online' : 'status-offline')}>
{status.ldapsRunning ? 'ONLINE' : 'OFFLINE'}
</span>
</div>
<div className={'status-item status-clickable' + (toggling === 'http' ? ' status-toggling' : '')}
onClick={() => handleToggleServer('http')}
title="Click to toggle HTTP server">
<span className="status-label">HTTP ({status.httpPort})</span>
<span className={'status-value ' + (status.httpRunning ? 'status-online' : 'status-offline')}>
{status.httpRunning ? 'ONLINE' : 'OFFLINE'}
</span>
</div>
<div className={'status-item status-clickable' + (toggling === 'rmi' ? ' status-toggling' : '')}
onClick={() => handleToggleServer('rmi')}
title="Click to toggle RMI server">
<span className="status-label">RMI ({status.rmiPort})</span>
<span className={'status-value ' + (status.rmiRunning ? 'status-online' : 'status-offline')}>
{status.rmiRunning ? 'ONLINE' : 'OFFLINE'}
</span>
</div>
<div className="status-item">
<span className="status-label">IP Address</span>
<span className="status-value" style={{ color: 'var(--accent)' }}>{status.ip || '0.0.0.0'}</span>
</div>
<div className="status-item">
<span className="status-label">Version</span>
<span className="status-value" style={{ color: 'var(--accent)' }}>1.3.8</span>
</div>
</div>
<button className="btn btn-primary" style={{ marginTop: 16 }} onClick={handleStartServers} disabled={loading}>
{loading ? 'Starting...' : 'Start Selected Servers'}
</button>
</div>
)}
{activeTab === 'config' && (
<div>
<div className="glass-card section-enter">
<h2>Config</h2>
<div className="config-form">
<div className="form-group">
<label>IP Address</label>
<input type="text" value={configForm.ip}
onChange={e => setConfigForm({ ...configForm, ip: e.target.value })} />
</div>
<div className="form-group">
<label>LDAP Port</label>
<input type="number" value={configForm.ldapPort}
onChange={e => setConfigForm({ ...configForm, ldapPort: parseInt(e.target.value) || 1389 })} />
</div>
<div className="form-group">
<label>LDAPS Port</label>
<input type="number" value={configForm.ldapsPort}
onChange={e => setConfigForm({ ...configForm, ldapsPort: parseInt(e.target.value) || 1669 })} />
</div>
<div className="form-group">
<label>HTTP Port</label>
<input type="number" value={configForm.httpPort}
onChange={e => setConfigForm({ ...configForm, httpPort: parseInt(e.target.value) || 3456 })} />
</div>
<div className="form-group">
<label>RMI Port</label>
<input type="number" value={configForm.rmiPort}
onChange={e => setConfigForm({ ...configForm, rmiPort: parseInt(e.target.value) || 1099 })} />
</div>
<div className="form-group">
<label>CodeBase URL</label>
<input type="text" value={configForm.codeBase} placeholder="http://x.x.x.x:port/"
onChange={e => setConfigForm({ ...configForm, codeBase: e.target.value })} />
</div>
<button className="btn btn-primary" onClick={handleSaveConfig} disabled={loading}>
{loading ? 'Saving...' : 'Save Configuration'}
</button>
</div>
<div className="form-group">
<label>IP Address</label>
<input type="text" value={configForm.ip}
onChange={e => setConfigForm({ ...configForm, ip: e.target.value })} />
</div>
<div className="form-group">
<label>LDAP Port</label>
<input type="number" value={configForm.ldapPort}
onChange={e => setConfigForm({ ...configForm, ldapPort: parseInt(e.target.value) || 1389 })} />
</div>
<div className="form-group">
<label>LDAPS Port</label>
<input type="number" value={configForm.ldapsPort}
onChange={e => setConfigForm({ ...configForm, ldapsPort: parseInt(e.target.value) || 1669 })} />
</div>
<div className="form-group">
<label>HTTP Port</label>
<input type="number" value={configForm.httpPort}
onChange={e => setConfigForm({ ...configForm, httpPort: parseInt(e.target.value) || 3456 })} />
</div>
<div className="form-group">
<label>RMI Port</label>
<input type="number" value={configForm.rmiPort}
onChange={e => setConfigForm({ ...configForm, rmiPort: parseInt(e.target.value) || 1099 })} />
</div>
<div className="form-group">
<label>AES Key</label>
<input type="text" value={configForm.AESkey}
onChange={e => setConfigForm({ ...configForm, AESkey: e.target.value })} />
</div>
<div className="form-group">
<label>LDAP User</label>
<input type="text" value={configForm.user} placeholder="ldap bind account"
onChange={e => setConfigForm({ ...configForm, user: e.target.value })} />
</div>
<div className="form-group">
<label>LDAP Password</label>
<input type="password" value={configForm.PASSWD} placeholder="ldap bind password"
onChange={e => setConfigForm({ ...configForm, PASSWD: e.target.value })} />
</div>
<div className="form-group">
<label>JKS Key Password</label>
<input type="password" value={configForm.keyPass} placeholder="JKS key password"
onChange={e => setConfigForm({ ...configForm, keyPass: e.target.value })} />
</div>
<div className="form-group">
<label>JKS Cert File</label>
<input type="text" value={configForm.certFile} placeholder="/path/to/cert.jks"
onChange={e => setConfigForm({ ...configForm, certFile: e.target.value })} />
</div>
<div className="form-group" style={{ gridColumn: 'span 2' }}>
<label className="form-group" style={{ marginBottom: 0 }}>
<input type="checkbox" checked={configForm.TLSProxy}
onChange={e => setConfigForm({ ...configForm, TLSProxy: e.target.checked })} /> TLS Proxy (LDAPS Port Forwarding)
</label>
</div>
<button className="btn btn-primary" onClick={handleSaveConfig} disabled={loading}>
{loading ? 'Saving...' : 'Save Configuration'}
</button>
</div>
</div>
)}
</>
)}
{activeTab === 'payload' && (
{mode === 'gadget' && (
<div className="glass-card section-enter">
<h2>Payload Generator</h2>
<div className="grid-2">
<div>
<h3 style={{ fontSize: 13, marginBottom: 10, color: 'var(--text-secondary)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: 0.5 }}>
@@ -320,9 +321,9 @@ export default function Dashboard() {
)}
</div>
</div>
)}
</div>
</LiquidGlass>
</div>
)}
</div>
</div>
</div>
)
+4 -16
View File
@@ -1,13 +1,11 @@
import { useState, useRef } from 'react'
import { useState } from 'react'
import { useNavigate } from 'react-router-dom'
import LiquidGlass from 'liquid-glass-react'
import { login, setAuthToken } from '../api'
import { useTheme } from '../context/ThemeContext'
export default function Login() {
const navigate = useNavigate()
const { theme, toggleTheme } = useTheme()
const containerRef = useRef(null)
const [username, setUsername] = useState('qi')
const [password, setPassword] = useState('')
const [error, setError] = useState('')
@@ -44,7 +42,7 @@ export default function Login() {
}
return (
<div className="login-wrapper" ref={containerRef}>
<div className="login-wrapper">
<div style={{ position: 'fixed', top: 22, right: 22, zIndex: 200 }}>
<button
className="theme-toggle"
@@ -53,17 +51,7 @@ export default function Login() {
title={theme === 'light' ? 'Dark mode' : 'Light mode'}
/>
</div>
<LiquidGlass
mouseContainer={containerRef}
elasticity={0.10}
blurAmount={0.045}
saturation={128}
displacementScale={44}
cornerRadius={28}
aberrationIntensity={1.8}
overLight={theme === 'light'}
style={{ width: 380 }}
>
<div className="glass-card" style={{ width: 380 }}>
<div className="login-inner">
<h1>
<span className="logo-dot" style={{ width: 12, height: 12, borderRadius: 6 }} />
@@ -96,7 +84,7 @@ export default function Login() {
</button>
</form>
</div>
</LiquidGlass>
</div>
</div>
)
}
+10 -1
View File
@@ -30,10 +30,11 @@ public class HTTPServer {
private static final Logger log = LogManager.getLogger(HTTPServer.class);
public static boolean isRunning = false;
public static String cwd = System.getProperty("user.dir");
private static HttpServer httpServer;
public static void start() throws IOException {
HttpServer httpServer = HttpServer.create(new InetSocketAddress(Config.httpPort), 0);
httpServer = HttpServer.create(new InetSocketAddress(Config.httpPort), 0);
httpServer.createContext("/", httpExchange -> {
try {
System.out.println(ansi().render("@|green [+]|@ New HTTP Request From >>" + httpExchange.getRemoteAddress() + " " + httpExchange.getRequestURI()));
@@ -82,6 +83,14 @@ public class HTTPServer {
System.out.println(ansi().render("@|green [+]|@ HTTP Server Start Listening on >> " + Config.httpPort + "..."));
}
public static void stop() {
if (httpServer != null) {
httpServer.stop(0);
isRunning = false;
System.out.println(ansi().render("@|yellow [!]|@ HTTP Server stopped"));
}
}
private static void handleFileRequest(HttpExchange exchange) throws Exception {
System.out.println("[-] 请求的后缀不对");
String path = exchange.getRequestURI().getPath();
+10 -1
View File
@@ -31,6 +31,7 @@ public class LdapServer extends InMemoryOperationInterceptor {
private static final Logger log = LogManager.getLogger(LdapServer.class);
public static TreeMap<String, LdapController> routes = new TreeMap<>();
public static boolean isRunning = false;
private static InMemoryDirectoryServer ds;
public LdapServer() throws Exception {
@@ -71,7 +72,7 @@ public class LdapServer extends InMemoryOperationInterceptor {
//添加操作拦截器
//将提供的操作拦截器添加到操作拦截器列表中,该列表可用于在请求被内存目录服务器处理之前转换请求,和/或在响应返回给客户端之前转换响应。
serverConfig.addInMemoryOperationInterceptor(new LdapServer());
InMemoryDirectoryServer ds = new InMemoryDirectoryServer(serverConfig);
ds = new InMemoryDirectoryServer(serverConfig);
ds.startListening();
isRunning = true;
System.out.println(ansi().render("@|green [+]|@ LDAP Server Start Listening on >> " + Config.ldapPort + "..."));
@@ -80,6 +81,14 @@ public class LdapServer extends InMemoryOperationInterceptor {
}
}
public static void stop() {
if (ds != null) {
ds.shutDown(true);
isRunning = false;
System.out.println(ansi().render("@|yellow [!]|@ LDAP Server stopped"));
}
}
@Override
public void processSearchResult(InMemoryInterceptedSearchResult result) {
String base;
+10 -1
View File
@@ -15,6 +15,7 @@ import static org.fusesource.jansi.Ansi.ansi;
public class LdapsServer {
private static final Logger log = LogManager.getLogger(LdapsServer.class);
public static boolean isRunning = false;
private static InMemoryDirectoryServer ds;
private final String certFile;
private final String keyPass;
@@ -50,7 +51,7 @@ public class LdapsServer {
));
config.addInMemoryOperationInterceptor(new LdapServer());
InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
ds = new InMemoryDirectoryServer(config);
ds.startListening();
isRunning = true;
System.out.println(ansi().render("@|green [+]|@ LDAPS Server Start Listening on >> " + Config.ldapsPort + "..."));
@@ -58,4 +59,12 @@ public class LdapsServer {
log.error("e: ", e);
}
}
public static void stop() {
if (ds != null) {
ds.shutDown(true);
isRunning = false;
System.out.println(ansi().render("@|yellow [!]|@ LDAPS Server stopped"));
}
}
}
+11 -2
View File
@@ -50,6 +50,7 @@ import static org.fusesource.jansi.Ansi.ansi;
public class RMIServer implements Runnable {
public static boolean isRunning = false;
private static RMIServer instance;
private final ServerSocket ss;
private final Object waitLock = new Object();
@@ -67,15 +68,23 @@ public class RMIServer implements Runnable {
try {
System.out.println(ansi().render("@|green [+]|@ RMI Server Start Listening on >> " + rmiPort + "..."));
RMIServer c = new RMIServer(rmiPort, new URL(url));
instance = new RMIServer(rmiPort, new URL(url));
isRunning = true;
c.run();
instance.run();
} catch (Exception e) {
System.err.println("Listener error");
e.printStackTrace(System.err);
}
}
public static void stop() {
if (instance != null) {
instance.close();
isRunning = false;
System.out.println(ansi().render("@|yellow [!]|@ RMI Server stopped"));
}
}
private static void handleDGC(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.readInt(); // method
ois.readLong(); // hash
@@ -29,8 +29,6 @@ public class Config {
@Parameter(names = {"-hP", "--httpPort"}, description = "Http bind port", order = 3)
public static int httpPort = 3456;
@Parameter(names = {"-c", " --command"}, help = true, description = "RMI this command")
public static String command = "whoami";
@Parameter(names = {"-v", " --version"}, help = true, description = "Show version", order = 5)
public static boolean showVersion;
@Parameter(names = {"-ga", " --gadgets"}, help = true, description = "Show gadgets", order = 5)
@@ -15,6 +15,8 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import java.awt.*;
import java.awt.datatransfer.StringSelection;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
@@ -52,6 +54,8 @@ public class JYsoWebApplication {
System.out.println(ansi().render("@|yellow [+]|@ Password: " + webPassword));
System.out.println(ansi().render("@|yellow [+]|@ ============================================"));
copyToClipboard(webPassword);
startJndiServers(args);
Properties props = new Properties();
@@ -77,6 +81,19 @@ public class JYsoWebApplication {
}
}
private static void copyToClipboard(String text) {
try {
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(
new StringSelection(text), null
);
System.out.println(ansi().render("@|cyan [+]|@ Password copied to clipboard"));
} catch (HeadlessException e) {
// headless environment, clipboard unavailable
} catch (Exception e) {
System.out.println(ansi().render("@|yellow [!]|@ Could not copy to clipboard: " + e.getMessage()));
}
}
private static String[] filterWebArgs(String[] args) {
java.util.List<String> filtered = new java.util.ArrayList<>();
for (int i = 0; i < args.length; i++) {
@@ -29,7 +29,13 @@ public class ApiController {
status.put("httpPort", Config.httpPort);
status.put("rmiPort", Config.rmiPort);
status.put("codeBase", Config.codeBase);
status.put("version", "1.3.7");
status.put("AESkey", Config.AESkey);
status.put("user", Config.USER);
status.put("PASSWD", Config.PASSWD);
status.put("TLSProxy", Config.TLSProxy);
status.put("keyPass", Config.keyPass);
status.put("certFile", Config.certFile);
status.put("version", "1.3.8");
return status;
}
@@ -81,6 +87,83 @@ public class ApiController {
return result;
}
@PostMapping("/servers/stop")
public Map<String, Object> stopServer(@RequestBody Map<String, Object> body) {
Map<String, Object> result = new LinkedHashMap<>();
String server = (String) body.get("server");
if (server == null) {
result.put("success", false);
result.put("error", "server name required");
return result;
}
switch (server.toLowerCase()) {
case "ldap":
LdapServer.stop();
break;
case "ldaps":
LdapsServer.stop();
break;
case "http":
HTTPServer.stop();
break;
case "rmi":
RMIServer.stop();
break;
default:
result.put("success", false);
result.put("error", "unknown server: " + server);
return result;
}
result.put("success", true);
result.put("server", server);
result.put("status", getStatus());
return result;
}
@PostMapping("/servers/toggle")
public Map<String, Object> toggleServer(@RequestBody Map<String, Object> body) {
Map<String, Object> result = new LinkedHashMap<>();
String server = (String) body.get("server");
if (server == null) {
result.put("success", false);
result.put("error", "server name required");
return result;
}
boolean nowRunning;
switch (server.toLowerCase()) {
case "ldap":
if (LdapServer.isRunning) LdapServer.stop();
else new Thread(() -> { try { LdapServer.start(); } catch (Exception ignored) {} }, "ldap-toggler").start();
nowRunning = !LdapServer.isRunning;
break;
case "ldaps":
if (LdapsServer.isRunning) LdapsServer.stop();
else new Thread(() -> { try { LdapsServer.start(); } catch (Exception ignored) {} }, "ldaps-toggler").start();
nowRunning = !LdapsServer.isRunning;
break;
case "http":
if (HTTPServer.isRunning) HTTPServer.stop();
else new Thread(() -> { try { HTTPServer.start(); } catch (Exception ignored) {} }, "http-toggler").start();
nowRunning = !HTTPServer.isRunning;
break;
case "rmi":
if (RMIServer.isRunning) RMIServer.stop();
else new Thread(() -> { try { RMIServer.start(); } catch (Exception ignored) {} }, "rmi-toggler").start();
nowRunning = !RMIServer.isRunning;
break;
default:
result.put("success", false);
result.put("error", "unknown server: " + server);
return result;
}
try { Thread.sleep(800); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
result.put("success", true);
result.put("server", server);
result.put("running", nowRunning);
result.put("status", getStatus());
return result;
}
@GetMapping("/gadgets")
public List<Map<String, Object>> listGadgets() {
List<Map<String, Object>> list = new ArrayList<>();
@@ -134,8 +217,13 @@ public class ApiController {
if (body.containsKey("ldapsPort")) Config.ldapsPort = (Integer) body.get("ldapsPort");
if (body.containsKey("httpPort")) Config.httpPort = (Integer) body.get("httpPort");
if (body.containsKey("rmiPort")) Config.rmiPort = (Integer) body.get("rmiPort");
if (body.containsKey("command")) Config.command = (String) body.get("command");
if (body.containsKey("codeBase")) Config.codeBase = (String) body.get("codeBase");
if (body.containsKey("AESkey")) Config.AESkey = (String) body.get("AESkey");
if (body.containsKey("user")) Config.USER = (String) body.get("user");
if (body.containsKey("PASSWD")) Config.PASSWD = (String) body.get("PASSWD");
if (body.containsKey("TLSProxy")) Config.TLSProxy = Boolean.TRUE.equals(body.get("TLSProxy"));
if (body.containsKey("keyPass")) Config.keyPass = (String) body.get("keyPass");
if (body.containsKey("certFile")) Config.certFile = (String) body.get("certFile");
result.put("success", true);
result.put("config", getStatus());