diff --git a/src/main/frontend/package.json b/src/main/frontend/package.json index 1cff438..48cf0d1 100644 --- a/src/main/frontend/package.json +++ b/src/main/frontend/package.json @@ -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" diff --git a/src/main/frontend/src/api/index.js b/src/main/frontend/src/api/index.js index f102e88..681563e 100644 --- a/src/main/frontend/src/api/index.js +++ b/src/main/frontend/src/api/index.js @@ -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') } diff --git a/src/main/frontend/src/index.css b/src/main/frontend/src/index.css index 023d48c..94976ae 100644 --- a/src/main/frontend/src/index.css +++ b/src/main/frontend/src/index.css @@ -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; diff --git a/src/main/frontend/src/pages/Dashboard.jsx b/src/main/frontend/src/pages/Dashboard.jsx index 8c335a7..9f89c88 100644 --- a/src/main/frontend/src/pages/Dashboard.jsx +++ b/src/main/frontend/src/pages/Dashboard.jsx @@ -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 ( -
+
- +

JYso

+
+ + +
- +
- {msg.success &&
{msg.success}
} - {msg.error &&
{msg.error}
} + {msg.success &&
{msg.success}
} + {msg.error &&
{msg.error}
} - -
-

Server Status

-
-
- LDAP ({status.ldapPort}) - - {status.ldapRunning ? 'ONLINE' : 'OFFLINE'} - -
-
- LDAPS ({status.ldapsPort}) - - {status.ldapsRunning ? 'ONLINE' : 'OFFLINE'} - -
-
- HTTP ({status.httpPort}) - - {status.httpRunning ? 'ONLINE' : 'OFFLINE'} - -
-
- RMI ({status.rmiPort}) - - {status.rmiRunning ? 'ONLINE' : 'OFFLINE'} - -
-
- IP Address - {status.ip || '0.0.0.0'} -
-
- Version - {status.version || '-'} -
-
-
-
- - -
-

Controls

-
- - - - -
- - {activeTab === 'servers' && ( -
-
- - - - +
+ {mode === 'jndi' && ( + <> +
+

Server Status

+
+
handleToggleServer('ldap')} + title="Click to toggle LDAP server"> + LDAP ({status.ldapPort}) + + {status.ldapRunning ? 'ONLINE' : 'OFFLINE'} + +
+
handleToggleServer('ldaps')} + title="Click to toggle LDAPS server"> + LDAPS ({status.ldapsPort}) + + {status.ldapsRunning ? 'ONLINE' : 'OFFLINE'} + +
+
handleToggleServer('http')} + title="Click to toggle HTTP server"> + HTTP ({status.httpPort}) + + {status.httpRunning ? 'ONLINE' : 'OFFLINE'} + +
+
handleToggleServer('rmi')} + title="Click to toggle RMI server"> + RMI ({status.rmiPort}) + + {status.rmiRunning ? 'ONLINE' : 'OFFLINE'} + +
+
+ IP Address + {status.ip || '0.0.0.0'} +
+
+ Version + 1.3.8 +
-
- )} - {activeTab === 'config' && ( -
+
+

Config

-
- - setConfigForm({ ...configForm, ip: e.target.value })} /> -
-
- - setConfigForm({ ...configForm, ldapPort: parseInt(e.target.value) || 1389 })} /> -
-
- - setConfigForm({ ...configForm, ldapsPort: parseInt(e.target.value) || 1669 })} /> -
-
- - setConfigForm({ ...configForm, httpPort: parseInt(e.target.value) || 3456 })} /> -
-
- - setConfigForm({ ...configForm, rmiPort: parseInt(e.target.value) || 1099 })} /> -
-
- - setConfigForm({ ...configForm, codeBase: e.target.value })} /> -
- -
+
+ + setConfigForm({ ...configForm, ip: e.target.value })} /> +
+
+ + setConfigForm({ ...configForm, ldapPort: parseInt(e.target.value) || 1389 })} /> +
+
+ + setConfigForm({ ...configForm, ldapsPort: parseInt(e.target.value) || 1669 })} /> +
+
+ + setConfigForm({ ...configForm, httpPort: parseInt(e.target.value) || 3456 })} /> +
+
+ + setConfigForm({ ...configForm, rmiPort: parseInt(e.target.value) || 1099 })} /> +
+
+ + setConfigForm({ ...configForm, AESkey: e.target.value })} /> +
+
+ + setConfigForm({ ...configForm, user: e.target.value })} /> +
+
+ + setConfigForm({ ...configForm, PASSWD: e.target.value })} /> +
+
+ + setConfigForm({ ...configForm, keyPass: e.target.value })} /> +
+
+ + setConfigForm({ ...configForm, certFile: e.target.value })} /> +
+
+ +
+ +
- )} + + )} - {activeTab === 'payload' && ( + {mode === 'gadget' && ( +
+

Payload Generator

@@ -320,9 +321,9 @@ export default function Dashboard() { )}

- )} -
- +
+ )} +
) diff --git a/src/main/frontend/src/pages/Login.jsx b/src/main/frontend/src/pages/Login.jsx index 6b60227..919a447 100644 --- a/src/main/frontend/src/pages/Login.jsx +++ b/src/main/frontend/src/pages/Login.jsx @@ -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 ( -
+
- +

@@ -96,7 +84,7 @@ export default function Login() {

- +
) } diff --git a/src/main/java/com/qi4l/JYso/HTTPServer.java b/src/main/java/com/qi4l/JYso/HTTPServer.java index 95059b5..8d8adae 100644 --- a/src/main/java/com/qi4l/JYso/HTTPServer.java +++ b/src/main/java/com/qi4l/JYso/HTTPServer.java @@ -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(); diff --git a/src/main/java/com/qi4l/JYso/LdapServer.java b/src/main/java/com/qi4l/JYso/LdapServer.java index be81ea6..ab6142a 100644 --- a/src/main/java/com/qi4l/JYso/LdapServer.java +++ b/src/main/java/com/qi4l/JYso/LdapServer.java @@ -31,6 +31,7 @@ public class LdapServer extends InMemoryOperationInterceptor { private static final Logger log = LogManager.getLogger(LdapServer.class); public static TreeMap 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; diff --git a/src/main/java/com/qi4l/JYso/LdapsServer.java b/src/main/java/com/qi4l/JYso/LdapsServer.java index 6fa8f0f..98acc23 100644 --- a/src/main/java/com/qi4l/JYso/LdapsServer.java +++ b/src/main/java/com/qi4l/JYso/LdapsServer.java @@ -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")); + } + } } \ No newline at end of file diff --git a/src/main/java/com/qi4l/JYso/RMIServer.java b/src/main/java/com/qi4l/JYso/RMIServer.java index 28d8e39..86bfb33 100644 --- a/src/main/java/com/qi4l/JYso/RMIServer.java +++ b/src/main/java/com/qi4l/JYso/RMIServer.java @@ -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 diff --git a/src/main/java/com/qi4l/JYso/gadgets/Config/Config.java b/src/main/java/com/qi4l/JYso/gadgets/Config/Config.java index cabc48c..129e2df 100644 --- a/src/main/java/com/qi4l/JYso/gadgets/Config/Config.java +++ b/src/main/java/com/qi4l/JYso/gadgets/Config/Config.java @@ -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) diff --git a/src/main/java/com/qi4l/JYso/web/JYsoWebApplication.java b/src/main/java/com/qi4l/JYso/web/JYsoWebApplication.java index 660a226..73047af 100644 --- a/src/main/java/com/qi4l/JYso/web/JYsoWebApplication.java +++ b/src/main/java/com/qi4l/JYso/web/JYsoWebApplication.java @@ -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 filtered = new java.util.ArrayList<>(); for (int i = 0; i < args.length; i++) { diff --git a/src/main/java/com/qi4l/JYso/web/controller/ApiController.java b/src/main/java/com/qi4l/JYso/web/controller/ApiController.java index fef4259..ea5086e 100644 --- a/src/main/java/com/qi4l/JYso/web/controller/ApiController.java +++ b/src/main/java/com/qi4l/JYso/web/controller/ApiController.java @@ -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 stopServer(@RequestBody Map body) { + Map 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 toggleServer(@RequestBody Map body) { + Map 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> listGadgets() { List> 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());