mirror of
https://github.com/qi4L/JYso.git
synced 2026-09-26 00:31:52 +08:00
perf:前端大改1
This commit is contained in:
@@ -10,7 +10,6 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.6.0",
|
"axios": "^1.6.0",
|
||||||
"liquid-glass-react": "^1.1.1",
|
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-router-dom": "^6.28.0"
|
"react-router-dom": "^6.28.0"
|
||||||
|
|||||||
@@ -44,6 +44,14 @@ export function startServers(data) {
|
|||||||
return api.post('/servers/start', 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() {
|
export function getGadgets() {
|
||||||
return api.get('/gadgets')
|
return api.get('/gadgets')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,10 @@
|
|||||||
--card-radius: 24px;
|
--card-radius: 24px;
|
||||||
--btn-radius: 13px;
|
--btn-radius: 13px;
|
||||||
--input-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;
|
--font-mono: 'SF Mono', 'JetBrains Mono', 'Fira Code', monospace;
|
||||||
--transition-fast: 200ms cubic-bezier(0.22, 0.1, 0.22, 1);
|
--transition-fast: 200ms cubic-bezier(0.22, 0.1, 0.22, 1);
|
||||||
--transition-fluid: 420ms 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);
|
--success-bg: rgba(48, 209, 88, 0.10);
|
||||||
--danger: #FF453A;
|
--danger: #FF453A;
|
||||||
--danger-bg: rgba(255, 69, 58, 0.08);
|
--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 {
|
html {
|
||||||
@@ -171,6 +179,89 @@ code, pre, .mono {
|
|||||||
|
|
||||||
.glass-card {
|
.glass-card {
|
||||||
padding: 28px;
|
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 {
|
.glass-card h2 {
|
||||||
@@ -201,6 +292,28 @@ code, pre, .mono {
|
|||||||
transition: background var(--transition-fluid), border-color var(--transition-fluid);
|
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 {
|
.status-label {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@@ -389,6 +502,78 @@ code, pre, .mono {
|
|||||||
|
|
||||||
.control-bar .btn { padding: 8px 16px; font-size: 13px; }
|
.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 {
|
.config-form {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
|
|||||||
@@ -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 { useNavigate } from 'react-router-dom'
|
||||||
import LiquidGlass from 'liquid-glass-react'
|
|
||||||
import { useTheme } from '../context/ThemeContext'
|
import { useTheme } from '../context/ThemeContext'
|
||||||
import {
|
import {
|
||||||
getStatus, startServers as apiStartServers, getGadgets,
|
getStatus, toggleServer, getGadgets,
|
||||||
generatePayload as apiGenerate, updateConfig, setAuthToken
|
generatePayload as apiGenerate, updateConfig, setAuthToken
|
||||||
} from '../api'
|
} from '../api'
|
||||||
|
|
||||||
export default function Dashboard() {
|
export default function Dashboard() {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const { theme, toggleTheme } = useTheme()
|
const { theme, toggleTheme } = useTheme()
|
||||||
const containerRef = useRef(null)
|
const [mode, setMode] = useState('jndi')
|
||||||
|
const [animKey, setAnimKey] = useState(0)
|
||||||
const [status, setStatus] = useState({})
|
const [status, setStatus] = useState({})
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [msg, setMsg] = useState({ success: '', error: '' })
|
const [msg, setMsg] = useState({ success: '', error: '' })
|
||||||
const [activeTab, setActiveTab] = useState('servers')
|
const [toggling, setToggling] = useState(null)
|
||||||
const [gadgets, setGadgets] = useState([])
|
const [gadgets, setGadgets] = useState([])
|
||||||
const [gadgetSearch, setGadgetSearch] = useState('')
|
const [gadgetSearch, setGadgetSearch] = useState('')
|
||||||
const [selectedGadget, setSelectedGadget] = useState('')
|
const [selectedGadget, setSelectedGadget] = useState('')
|
||||||
const [payloadCmd, setPayloadCmd] = useState('')
|
const [payloadCmd, setPayloadCmd] = useState('')
|
||||||
const [payloadResult, setPayloadResult] = useState('')
|
const [payloadResult, setPayloadResult] = useState('')
|
||||||
|
|
||||||
const [serverCfg, setServerCfg] = useState({
|
|
||||||
ldap: false, ldaps: false, http: false, rmi: false
|
|
||||||
})
|
|
||||||
|
|
||||||
const [configForm, setConfigForm] = useState({
|
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(() => {
|
const filteredGadgets = useMemo(() => {
|
||||||
@@ -34,6 +31,13 @@ export default function Dashboard() {
|
|||||||
return gadgets.filter(g => g.name.toLowerCase().includes(gadgetSearch.toLowerCase()))
|
return gadgets.filter(g => g.name.toLowerCase().includes(gadgetSearch.toLowerCase()))
|
||||||
}, [gadgetSearch, gadgets])
|
}, [gadgetSearch, gadgets])
|
||||||
|
|
||||||
|
const switchMode = useCallback((next) => {
|
||||||
|
if (next === mode) return
|
||||||
|
setMode(next)
|
||||||
|
setAnimKey(k => k + 1)
|
||||||
|
setMsg({ success: '', error: '' })
|
||||||
|
}, [mode])
|
||||||
|
|
||||||
async function loadStatus() {
|
async function loadStatus() {
|
||||||
try {
|
try {
|
||||||
const res = await getStatus()
|
const res = await getStatus()
|
||||||
@@ -45,7 +49,12 @@ export default function Dashboard() {
|
|||||||
ldapsPort: res.data.ldapsPort || 1669,
|
ldapsPort: res.data.ldapsPort || 1669,
|
||||||
httpPort: res.data.httpPort || 3456,
|
httpPort: res.data.httpPort || 3456,
|
||||||
rmiPort: res.data.rmiPort || 1099,
|
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) {
|
} catch (e) {
|
||||||
if (e.response && e.response.status === 401) {
|
if (e.response && e.response.status === 401) {
|
||||||
@@ -62,24 +71,18 @@ export default function Dashboard() {
|
|||||||
} catch (e) { /* ignore */ }
|
} catch (e) { /* ignore */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleStartServers() {
|
async function handleToggleServer(server) {
|
||||||
setLoading(true)
|
setToggling(server)
|
||||||
setMsg({ success: '', error: '' })
|
setMsg({ success: '', error: '' })
|
||||||
try {
|
try {
|
||||||
const res = await apiStartServers({
|
const res = await toggleServer(server)
|
||||||
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
|
|
||||||
})
|
|
||||||
if (res.data.success) {
|
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) {
|
} catch (e) {
|
||||||
setMsg({ success: '', error: 'Failed to start servers' })
|
setMsg({ success: '', error: 'Failed to toggle ' + server.toUpperCase() })
|
||||||
} finally { setLoading(false) }
|
} finally { setToggling(null) }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleSaveConfig() {
|
async function handleSaveConfig() {
|
||||||
@@ -118,27 +121,31 @@ export default function Dashboard() {
|
|||||||
|
|
||||||
useEffect(() => { loadStatus(); loadGadgets() }, [])
|
useEffect(() => { loadStatus(); loadGadgets() }, [])
|
||||||
|
|
||||||
const liquidProps = {
|
|
||||||
mouseContainer: containerRef,
|
|
||||||
elasticity: 0.07,
|
|
||||||
blurAmount: 0.042,
|
|
||||||
saturation: 120,
|
|
||||||
displacementScale: 36,
|
|
||||||
cornerRadius: 24,
|
|
||||||
overLight: theme === 'light',
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={containerRef}>
|
<div>
|
||||||
<div className="dashboard-bg" />
|
<div className="dashboard-bg" />
|
||||||
|
|
||||||
<div className="page-shell">
|
<div className="page-shell">
|
||||||
<LiquidGlass {...liquidProps} style={{ marginBottom: 20 }}>
|
<div className="glass-card" style={{ marginBottom: 20 }}>
|
||||||
<div className="header">
|
<div className="header">
|
||||||
<h1>
|
<h1>
|
||||||
<span className="logo-dot" />
|
<span className="logo-dot" />
|
||||||
JYso
|
JYso
|
||||||
</h1>
|
</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">
|
<div className="header-right">
|
||||||
<button
|
<button
|
||||||
className="theme-toggle"
|
className="theme-toggle"
|
||||||
@@ -156,136 +163,130 @@ export default function Dashboard() {
|
|||||||
<button className="logout-btn" onClick={logout}>Logout</button>
|
<button className="logout-btn" onClick={logout}>Logout</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</LiquidGlass>
|
</div>
|
||||||
|
|
||||||
{msg.success && <div className="success-msg">{msg.success}</div>}
|
{msg.success && <div className="success-msg section-enter">{msg.success}</div>}
|
||||||
{msg.error && <div className="error-msg">{msg.error}</div>}
|
{msg.error && <div className="error-msg section-enter">{msg.error}</div>}
|
||||||
|
|
||||||
<LiquidGlass {...liquidProps} style={{ marginBottom: 20 }}>
|
<div key={animKey}>
|
||||||
<div className="glass-card">
|
{mode === 'jndi' && (
|
||||||
<h2>Server Status</h2>
|
<>
|
||||||
<div className="status-grid">
|
<div className="glass-card section-enter" style={{ marginBottom: 20 }}>
|
||||||
<div className="status-item">
|
<h2>Server Status</h2>
|
||||||
<span className="status-label">LDAP ({status.ldapPort})</span>
|
<div className="status-grid">
|
||||||
<span className={'status-value ' + (status.ldapRunning ? 'status-online' : 'status-offline')}>
|
<div className={'status-item status-clickable' + (toggling === 'ldap' ? ' status-toggling' : '')}
|
||||||
{status.ldapRunning ? 'ONLINE' : 'OFFLINE'}
|
onClick={() => handleToggleServer('ldap')}
|
||||||
</span>
|
title="Click to toggle LDAP server">
|
||||||
</div>
|
<span className="status-label">LDAP ({status.ldapPort})</span>
|
||||||
<div className="status-item">
|
<span className={'status-value ' + (status.ldapRunning ? 'status-online' : 'status-offline')}>
|
||||||
<span className="status-label">LDAPS ({status.ldapsPort})</span>
|
{status.ldapRunning ? 'ONLINE' : 'OFFLINE'}
|
||||||
<span className={'status-value ' + (status.ldapsRunning ? 'status-online' : 'status-offline')}>
|
</span>
|
||||||
{status.ldapsRunning ? 'ONLINE' : 'OFFLINE'}
|
</div>
|
||||||
</span>
|
<div className={'status-item status-clickable' + (toggling === 'ldaps' ? ' status-toggling' : '')}
|
||||||
</div>
|
onClick={() => handleToggleServer('ldaps')}
|
||||||
<div className="status-item">
|
title="Click to toggle LDAPS server">
|
||||||
<span className="status-label">HTTP ({status.httpPort})</span>
|
<span className="status-label">LDAPS ({status.ldapsPort})</span>
|
||||||
<span className={'status-value ' + (status.httpRunning ? 'status-online' : 'status-offline')}>
|
<span className={'status-value ' + (status.ldapsRunning ? 'status-online' : 'status-offline')}>
|
||||||
{status.httpRunning ? 'ONLINE' : 'OFFLINE'}
|
{status.ldapsRunning ? 'ONLINE' : 'OFFLINE'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="status-item">
|
<div className={'status-item status-clickable' + (toggling === 'http' ? ' status-toggling' : '')}
|
||||||
<span className="status-label">RMI ({status.rmiPort})</span>
|
onClick={() => handleToggleServer('http')}
|
||||||
<span className={'status-value ' + (status.rmiRunning ? 'status-online' : 'status-offline')}>
|
title="Click to toggle HTTP server">
|
||||||
{status.rmiRunning ? 'ONLINE' : 'OFFLINE'}
|
<span className="status-label">HTTP ({status.httpPort})</span>
|
||||||
</span>
|
<span className={'status-value ' + (status.httpRunning ? 'status-online' : 'status-offline')}>
|
||||||
</div>
|
{status.httpRunning ? 'ONLINE' : 'OFFLINE'}
|
||||||
<div className="status-item">
|
</span>
|
||||||
<span className="status-label">IP Address</span>
|
</div>
|
||||||
<span className="status-value" style={{ color: 'var(--accent)' }}>{status.ip || '0.0.0.0'}</span>
|
<div className={'status-item status-clickable' + (toggling === 'rmi' ? ' status-toggling' : '')}
|
||||||
</div>
|
onClick={() => handleToggleServer('rmi')}
|
||||||
<div className="status-item">
|
title="Click to toggle RMI server">
|
||||||
<span className="status-label">Version</span>
|
<span className="status-label">RMI ({status.rmiPort})</span>
|
||||||
<span className="status-value" style={{ color: 'var(--accent)' }}>{status.version || '-'}</span>
|
<span className={'status-value ' + (status.rmiRunning ? 'status-online' : 'status-offline')}>
|
||||||
</div>
|
{status.rmiRunning ? 'ONLINE' : 'OFFLINE'}
|
||||||
</div>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</LiquidGlass>
|
<div className="status-item">
|
||||||
|
<span className="status-label">IP Address</span>
|
||||||
<LiquidGlass {...liquidProps}>
|
<span className="status-value" style={{ color: 'var(--accent)' }}>{status.ip || '0.0.0.0'}</span>
|
||||||
<div className="glass-card">
|
</div>
|
||||||
<h2>Controls</h2>
|
<div className="status-item">
|
||||||
<div className="control-bar">
|
<span className="status-label">Version</span>
|
||||||
<button className="btn btn-primary" style={{ width: 'auto' }} onClick={loadStatus}>
|
<span className="status-value" style={{ color: 'var(--accent)' }}>1.3.8</span>
|
||||||
Refresh Status
|
</div>
|
||||||
</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>
|
</div>
|
||||||
<button className="btn btn-primary" style={{ marginTop: 16 }} onClick={handleStartServers} disabled={loading}>
|
|
||||||
{loading ? 'Starting...' : 'Start Selected Servers'}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
|
||||||
{activeTab === 'config' && (
|
<div className="glass-card section-enter">
|
||||||
<div>
|
<h2>Config</h2>
|
||||||
<div className="config-form">
|
<div className="config-form">
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>IP Address</label>
|
<label>IP Address</label>
|
||||||
<input type="text" value={configForm.ip}
|
<input type="text" value={configForm.ip}
|
||||||
onChange={e => setConfigForm({ ...configForm, ip: e.target.value })} />
|
onChange={e => setConfigForm({ ...configForm, ip: e.target.value })} />
|
||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>LDAP Port</label>
|
<label>LDAP Port</label>
|
||||||
<input type="number" value={configForm.ldapPort}
|
<input type="number" value={configForm.ldapPort}
|
||||||
onChange={e => setConfigForm({ ...configForm, ldapPort: parseInt(e.target.value) || 1389 })} />
|
onChange={e => setConfigForm({ ...configForm, ldapPort: parseInt(e.target.value) || 1389 })} />
|
||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>LDAPS Port</label>
|
<label>LDAPS Port</label>
|
||||||
<input type="number" value={configForm.ldapsPort}
|
<input type="number" value={configForm.ldapsPort}
|
||||||
onChange={e => setConfigForm({ ...configForm, ldapsPort: parseInt(e.target.value) || 1669 })} />
|
onChange={e => setConfigForm({ ...configForm, ldapsPort: parseInt(e.target.value) || 1669 })} />
|
||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>HTTP Port</label>
|
<label>HTTP Port</label>
|
||||||
<input type="number" value={configForm.httpPort}
|
<input type="number" value={configForm.httpPort}
|
||||||
onChange={e => setConfigForm({ ...configForm, httpPort: parseInt(e.target.value) || 3456 })} />
|
onChange={e => setConfigForm({ ...configForm, httpPort: parseInt(e.target.value) || 3456 })} />
|
||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>RMI Port</label>
|
<label>RMI Port</label>
|
||||||
<input type="number" value={configForm.rmiPort}
|
<input type="number" value={configForm.rmiPort}
|
||||||
onChange={e => setConfigForm({ ...configForm, rmiPort: parseInt(e.target.value) || 1099 })} />
|
onChange={e => setConfigForm({ ...configForm, rmiPort: parseInt(e.target.value) || 1099 })} />
|
||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>CodeBase URL</label>
|
<label>AES Key</label>
|
||||||
<input type="text" value={configForm.codeBase} placeholder="http://x.x.x.x:port/"
|
<input type="text" value={configForm.AESkey}
|
||||||
onChange={e => setConfigForm({ ...configForm, codeBase: e.target.value })} />
|
onChange={e => setConfigForm({ ...configForm, AESkey: e.target.value })} />
|
||||||
</div>
|
</div>
|
||||||
<button className="btn btn-primary" onClick={handleSaveConfig} disabled={loading}>
|
<div className="form-group">
|
||||||
{loading ? 'Saving...' : 'Save Configuration'}
|
<label>LDAP User</label>
|
||||||
</button>
|
<input type="text" value={configForm.user} placeholder="ldap bind account"
|
||||||
</div>
|
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>
|
</div>
|
||||||
)}
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{activeTab === 'payload' && (
|
{mode === 'gadget' && (
|
||||||
|
<div className="glass-card section-enter">
|
||||||
|
<h2>Payload Generator</h2>
|
||||||
<div className="grid-2">
|
<div className="grid-2">
|
||||||
<div>
|
<div>
|
||||||
<h3 style={{ fontSize: 13, marginBottom: 10, color: 'var(--text-secondary)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: 0.5 }}>
|
<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>
|
</div>
|
||||||
)}
|
</div>
|
||||||
</div>
|
)}
|
||||||
</LiquidGlass>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
import { useState, useRef } from 'react'
|
import { useState } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import LiquidGlass from 'liquid-glass-react'
|
|
||||||
import { login, setAuthToken } from '../api'
|
import { login, setAuthToken } from '../api'
|
||||||
import { useTheme } from '../context/ThemeContext'
|
import { useTheme } from '../context/ThemeContext'
|
||||||
|
|
||||||
export default function Login() {
|
export default function Login() {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const { theme, toggleTheme } = useTheme()
|
const { theme, toggleTheme } = useTheme()
|
||||||
const containerRef = useRef(null)
|
|
||||||
const [username, setUsername] = useState('qi')
|
const [username, setUsername] = useState('qi')
|
||||||
const [password, setPassword] = useState('')
|
const [password, setPassword] = useState('')
|
||||||
const [error, setError] = useState('')
|
const [error, setError] = useState('')
|
||||||
@@ -44,7 +42,7 @@ export default function Login() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="login-wrapper" ref={containerRef}>
|
<div className="login-wrapper">
|
||||||
<div style={{ position: 'fixed', top: 22, right: 22, zIndex: 200 }}>
|
<div style={{ position: 'fixed', top: 22, right: 22, zIndex: 200 }}>
|
||||||
<button
|
<button
|
||||||
className="theme-toggle"
|
className="theme-toggle"
|
||||||
@@ -53,17 +51,7 @@ export default function Login() {
|
|||||||
title={theme === 'light' ? 'Dark mode' : 'Light mode'}
|
title={theme === 'light' ? 'Dark mode' : 'Light mode'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<LiquidGlass
|
<div className="glass-card" style={{ width: 380 }}>
|
||||||
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="login-inner">
|
<div className="login-inner">
|
||||||
<h1>
|
<h1>
|
||||||
<span className="logo-dot" style={{ width: 12, height: 12, borderRadius: 6 }} />
|
<span className="logo-dot" style={{ width: 12, height: 12, borderRadius: 6 }} />
|
||||||
@@ -96,7 +84,7 @@ export default function Login() {
|
|||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</LiquidGlass>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,10 +30,11 @@ public class HTTPServer {
|
|||||||
private static final Logger log = LogManager.getLogger(HTTPServer.class);
|
private static final Logger log = LogManager.getLogger(HTTPServer.class);
|
||||||
public static boolean isRunning = false;
|
public static boolean isRunning = false;
|
||||||
public static String cwd = System.getProperty("user.dir");
|
public static String cwd = System.getProperty("user.dir");
|
||||||
|
private static HttpServer httpServer;
|
||||||
|
|
||||||
public static void start() throws IOException {
|
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 -> {
|
httpServer.createContext("/", httpExchange -> {
|
||||||
try {
|
try {
|
||||||
System.out.println(ansi().render("@|green [+]|@ New HTTP Request From >>" + httpExchange.getRemoteAddress() + " " + httpExchange.getRequestURI()));
|
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 + "..."));
|
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 {
|
private static void handleFileRequest(HttpExchange exchange) throws Exception {
|
||||||
System.out.println("[-] 请求的后缀不对");
|
System.out.println("[-] 请求的后缀不对");
|
||||||
String path = exchange.getRequestURI().getPath();
|
String path = exchange.getRequestURI().getPath();
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ public class LdapServer extends InMemoryOperationInterceptor {
|
|||||||
private static final Logger log = LogManager.getLogger(LdapServer.class);
|
private static final Logger log = LogManager.getLogger(LdapServer.class);
|
||||||
public static TreeMap<String, LdapController> routes = new TreeMap<>();
|
public static TreeMap<String, LdapController> routes = new TreeMap<>();
|
||||||
public static boolean isRunning = false;
|
public static boolean isRunning = false;
|
||||||
|
private static InMemoryDirectoryServer ds;
|
||||||
|
|
||||||
public LdapServer() throws Exception {
|
public LdapServer() throws Exception {
|
||||||
|
|
||||||
@@ -71,7 +72,7 @@ public class LdapServer extends InMemoryOperationInterceptor {
|
|||||||
//添加操作拦截器
|
//添加操作拦截器
|
||||||
//将提供的操作拦截器添加到操作拦截器列表中,该列表可用于在请求被内存目录服务器处理之前转换请求,和/或在响应返回给客户端之前转换响应。
|
//将提供的操作拦截器添加到操作拦截器列表中,该列表可用于在请求被内存目录服务器处理之前转换请求,和/或在响应返回给客户端之前转换响应。
|
||||||
serverConfig.addInMemoryOperationInterceptor(new LdapServer());
|
serverConfig.addInMemoryOperationInterceptor(new LdapServer());
|
||||||
InMemoryDirectoryServer ds = new InMemoryDirectoryServer(serverConfig);
|
ds = new InMemoryDirectoryServer(serverConfig);
|
||||||
ds.startListening();
|
ds.startListening();
|
||||||
isRunning = true;
|
isRunning = true;
|
||||||
System.out.println(ansi().render("@|green [+]|@ LDAP Server Start Listening on >> " + Config.ldapPort + "..."));
|
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
|
@Override
|
||||||
public void processSearchResult(InMemoryInterceptedSearchResult result) {
|
public void processSearchResult(InMemoryInterceptedSearchResult result) {
|
||||||
String base;
|
String base;
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import static org.fusesource.jansi.Ansi.ansi;
|
|||||||
public class LdapsServer {
|
public class LdapsServer {
|
||||||
private static final Logger log = LogManager.getLogger(LdapsServer.class);
|
private static final Logger log = LogManager.getLogger(LdapsServer.class);
|
||||||
public static boolean isRunning = false;
|
public static boolean isRunning = false;
|
||||||
|
private static InMemoryDirectoryServer ds;
|
||||||
private final String certFile;
|
private final String certFile;
|
||||||
private final String keyPass;
|
private final String keyPass;
|
||||||
|
|
||||||
@@ -50,7 +51,7 @@ public class LdapsServer {
|
|||||||
));
|
));
|
||||||
config.addInMemoryOperationInterceptor(new LdapServer());
|
config.addInMemoryOperationInterceptor(new LdapServer());
|
||||||
|
|
||||||
InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
|
ds = new InMemoryDirectoryServer(config);
|
||||||
ds.startListening();
|
ds.startListening();
|
||||||
isRunning = true;
|
isRunning = true;
|
||||||
System.out.println(ansi().render("@|green [+]|@ LDAPS Server Start Listening on >> " + Config.ldapsPort + "..."));
|
System.out.println(ansi().render("@|green [+]|@ LDAPS Server Start Listening on >> " + Config.ldapsPort + "..."));
|
||||||
@@ -58,4 +59,12 @@ public class LdapsServer {
|
|||||||
log.error("e: ", e);
|
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"));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -50,6 +50,7 @@ import static org.fusesource.jansi.Ansi.ansi;
|
|||||||
public class RMIServer implements Runnable {
|
public class RMIServer implements Runnable {
|
||||||
|
|
||||||
public static boolean isRunning = false;
|
public static boolean isRunning = false;
|
||||||
|
private static RMIServer instance;
|
||||||
|
|
||||||
private final ServerSocket ss;
|
private final ServerSocket ss;
|
||||||
private final Object waitLock = new Object();
|
private final Object waitLock = new Object();
|
||||||
@@ -67,15 +68,23 @@ public class RMIServer implements Runnable {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
System.out.println(ansi().render("@|green [+]|@ RMI Server Start Listening on >> " + rmiPort + "..."));
|
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;
|
isRunning = true;
|
||||||
c.run();
|
instance.run();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.err.println("Listener error");
|
System.err.println("Listener error");
|
||||||
e.printStackTrace(System.err);
|
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 {
|
private static void handleDGC(ObjectInputStream ois) throws IOException, ClassNotFoundException {
|
||||||
ois.readInt(); // method
|
ois.readInt(); // method
|
||||||
ois.readLong(); // hash
|
ois.readLong(); // hash
|
||||||
|
|||||||
@@ -29,8 +29,6 @@ public class Config {
|
|||||||
|
|
||||||
@Parameter(names = {"-hP", "--httpPort"}, description = "Http bind port", order = 3)
|
@Parameter(names = {"-hP", "--httpPort"}, description = "Http bind port", order = 3)
|
||||||
public static int httpPort = 3456;
|
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)
|
@Parameter(names = {"-v", " --version"}, help = true, description = "Show version", order = 5)
|
||||||
public static boolean showVersion;
|
public static boolean showVersion;
|
||||||
@Parameter(names = {"-ga", " --gadgets"}, help = true, description = "Show gadgets", order = 5)
|
@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.boot.builder.SpringApplicationBuilder;
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.datatransfer.StringSelection;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.concurrent.CountDownLatch;
|
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 [+]|@ Password: " + webPassword));
|
||||||
System.out.println(ansi().render("@|yellow [+]|@ ============================================"));
|
System.out.println(ansi().render("@|yellow [+]|@ ============================================"));
|
||||||
|
|
||||||
|
copyToClipboard(webPassword);
|
||||||
|
|
||||||
startJndiServers(args);
|
startJndiServers(args);
|
||||||
|
|
||||||
Properties props = new Properties();
|
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) {
|
private static String[] filterWebArgs(String[] args) {
|
||||||
java.util.List<String> filtered = new java.util.ArrayList<>();
|
java.util.List<String> filtered = new java.util.ArrayList<>();
|
||||||
for (int i = 0; i < args.length; i++) {
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
|||||||
@@ -29,7 +29,13 @@ public class ApiController {
|
|||||||
status.put("httpPort", Config.httpPort);
|
status.put("httpPort", Config.httpPort);
|
||||||
status.put("rmiPort", Config.rmiPort);
|
status.put("rmiPort", Config.rmiPort);
|
||||||
status.put("codeBase", Config.codeBase);
|
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;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,6 +87,83 @@ public class ApiController {
|
|||||||
return result;
|
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")
|
@GetMapping("/gadgets")
|
||||||
public List<Map<String, Object>> listGadgets() {
|
public List<Map<String, Object>> listGadgets() {
|
||||||
List<Map<String, Object>> list = new ArrayList<>();
|
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("ldapsPort")) Config.ldapsPort = (Integer) body.get("ldapsPort");
|
||||||
if (body.containsKey("httpPort")) Config.httpPort = (Integer) body.get("httpPort");
|
if (body.containsKey("httpPort")) Config.httpPort = (Integer) body.get("httpPort");
|
||||||
if (body.containsKey("rmiPort")) Config.rmiPort = (Integer) body.get("rmiPort");
|
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("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("success", true);
|
||||||
result.put("config", getStatus());
|
result.put("config", getStatus());
|
||||||
|
|||||||
Reference in New Issue
Block a user