mirror of
https://github.com/qi4L/JYso.git
synced 2026-09-26 16:51:52 +08:00
feat:文件管理添加上传,输入不存在的链子正确的报错
This commit is contained in:
@@ -90,4 +90,8 @@ export function deleteFile(name) {
|
|||||||
return api.post('/files/delete', { name })
|
return api.post('/files/delete', { name })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function uploadFile(formData) {
|
||||||
|
return api.post('/files/upload', formData)
|
||||||
|
}
|
||||||
|
|
||||||
export default api
|
export default api
|
||||||
|
|||||||
@@ -906,6 +906,32 @@ select {
|
|||||||
font-family: 'Inter', sans-serif;
|
font-family: 'Inter', sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.file-upload-zone {
|
||||||
|
border: 2px dashed var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 18px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all .2s;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
font-size: 13px;
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
}
|
||||||
|
.file-upload-zone:hover {
|
||||||
|
border-color: var(--primary);
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
.file-upload-zone.drag-over {
|
||||||
|
border-color: var(--primary);
|
||||||
|
background: rgba(99, 102, 241, 0.08);
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
.file-upload-zone.uploading {
|
||||||
|
opacity: 0.6;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
*, *::before, *::after {
|
*, *::before, *::after {
|
||||||
transition-duration: 0ms !important;
|
transition-duration: 0ms !important;
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import { useTheme } from '../context/ThemeContext'
|
|||||||
import {
|
import {
|
||||||
getStatus, toggleServer, getGadgets,
|
getStatus, toggleServer, getGadgets,
|
||||||
generatePayload as apiGenerate, updateConfig, setAuthToken, getLogs,
|
generatePayload as apiGenerate, updateConfig, setAuthToken, getLogs,
|
||||||
getFiles, downloadFile, deleteFile as apiDeleteFile
|
getFiles, downloadFile, deleteFile as apiDeleteFile,
|
||||||
|
uploadFile
|
||||||
} from '../api'
|
} from '../api'
|
||||||
|
|
||||||
export default function Dashboard() {
|
export default function Dashboard() {
|
||||||
@@ -53,6 +54,9 @@ export default function Dashboard() {
|
|||||||
const logEndRef = useRef(null)
|
const logEndRef = useRef(null)
|
||||||
const [files, setFiles] = useState([])
|
const [files, setFiles] = useState([])
|
||||||
const [filesLoading, setFilesLoading] = useState(false)
|
const [filesLoading, setFilesLoading] = useState(false)
|
||||||
|
const [dragOver, setDragOver] = useState(false)
|
||||||
|
const [uploading, setUploading] = useState(false)
|
||||||
|
const fileInputRef = useRef(null)
|
||||||
|
|
||||||
const ROUTING_OPTIONS = ['Basic', 'ELProcessor', 'Groovy', 'jdbcBypass1', 'jdbcBypass2', 'ldap2rmi', 'SnakeYaml', 'XStream', 'MemoryXXE']
|
const ROUTING_OPTIONS = ['Basic', 'ELProcessor', 'Groovy', 'jdbcBypass1', 'jdbcBypass2', 'ldap2rmi', 'SnakeYaml', 'XStream', 'MemoryXXE']
|
||||||
|
|
||||||
@@ -149,7 +153,7 @@ export default function Dashboard() {
|
|||||||
setMsg({ success: '', error: res.data.error || 'Generation failed' })
|
setMsg({ success: '', error: res.data.error || 'Generation failed' })
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setMsg({ success: '', error: 'Failed to generate payload' })
|
setMsg({ success: '', error: e.response?.data?.error || 'Failed to generate payload' })
|
||||||
} finally { setLoading(false) }
|
} finally { setLoading(false) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,6 +283,56 @@ export default function Dashboard() {
|
|||||||
} catch { /* ignore */ }
|
} catch { /* ignore */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleDragOver(e) {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
setDragOver(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDragLeave(e) {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
setDragOver(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDrop(e) {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
setDragOver(false)
|
||||||
|
const droppedFiles = e.dataTransfer.files
|
||||||
|
if (droppedFiles.length > 0) {
|
||||||
|
doUpload(droppedFiles[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFileSelect(e) {
|
||||||
|
const selectedFiles = e.target.files
|
||||||
|
if (selectedFiles.length > 0) {
|
||||||
|
doUpload(selectedFiles[0])
|
||||||
|
}
|
||||||
|
e.target.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
async function doUpload(file) {
|
||||||
|
setUploading(true)
|
||||||
|
setMsg({ success: '', error: '' })
|
||||||
|
try {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
const res = await uploadFile(formData)
|
||||||
|
if (res.data.success) {
|
||||||
|
setMsg({ success: 'Uploaded: ' + res.data.name, error: '' })
|
||||||
|
fetchFiles()
|
||||||
|
} else {
|
||||||
|
setMsg({ success: '', error: res.data.error || 'Upload failed' })
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
setMsg({ success: '', error: 'Upload failed' })
|
||||||
|
} finally {
|
||||||
|
setUploading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (activeJndiTab !== 'logs') return
|
if (activeJndiTab !== 'logs') return
|
||||||
fetchLogs()
|
fetchLogs()
|
||||||
@@ -814,6 +868,25 @@ export default function Dashboard() {
|
|||||||
{filesLoading ? 'Loading...' : 'Refresh'}
|
{filesLoading ? 'Loading...' : 'Refresh'}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
className={'file-upload-zone' + (dragOver ? ' drag-over' : '') + (uploading ? ' uploading' : '')}
|
||||||
|
onDragOver={handleDragOver}
|
||||||
|
onDragLeave={handleDragLeave}
|
||||||
|
onDrop={handleDrop}
|
||||||
|
onClick={() => fileInputRef.current?.click()}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
ref={fileInputRef}
|
||||||
|
type="file"
|
||||||
|
style={{ display: 'none' }}
|
||||||
|
onChange={handleFileSelect}
|
||||||
|
/>
|
||||||
|
{uploading ? (
|
||||||
|
<span>Uploading...</span>
|
||||||
|
) : (
|
||||||
|
<span>Drop file here or click to upload</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<div className="file-list">
|
<div className="file-list">
|
||||||
{files.length === 0 ? (
|
{files.length === 0 ? (
|
||||||
<div className="file-empty">No saved files</div>
|
<div className="file-empty">No saved files</div>
|
||||||
|
|||||||
@@ -38,7 +38,12 @@ public class Starter {
|
|||||||
|
|
||||||
if (args[0].equals("-y")) {
|
if (args[0].equals("-y")) {
|
||||||
JYsoMode = true;
|
JYsoMode = true;
|
||||||
ysoserial.run(args);
|
try {
|
||||||
|
ysoserial.run(args);
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("[!] " + e.getMessage());
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,9 +84,7 @@ public class ysoserial {
|
|||||||
//载入gadget
|
//载入gadget
|
||||||
final Class<? extends ObjectPayload<?>> payloadClass = ObjectPayload.Utils.getPayloadClass(payloadType);
|
final Class<? extends ObjectPayload<?>> payloadClass = ObjectPayload.Utils.getPayloadClass(payloadType);
|
||||||
if (payloadClass == null) {
|
if (payloadClass == null) {
|
||||||
System.err.println("Invalid payload type '" + payloadType + "'");
|
throw new IllegalArgumentException("Invalid payload type '" + payloadType + "'");
|
||||||
printUsage(options);
|
|
||||||
System.exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import org.eclipse.jetty.servlet.ServletContextHandler;
|
|||||||
import org.eclipse.jetty.servlet.ServletHolder;
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
|
|
||||||
import javax.servlet.DispatcherType;
|
import javax.servlet.DispatcherType;
|
||||||
|
import javax.servlet.MultipartConfigElement;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.datatransfer.StringSelection;
|
import java.awt.datatransfer.StringSelection;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
@@ -70,7 +71,9 @@ public class JYsoWebApplication {
|
|||||||
|
|
||||||
context.addServlet(new ServletHolder(new AuthServlet()), "/api/auth/login");
|
context.addServlet(new ServletHolder(new AuthServlet()), "/api/auth/login");
|
||||||
|
|
||||||
context.addServlet(new ServletHolder(new JettyApiServlet()), "/api/*");
|
ServletHolder apiHolder = new ServletHolder(new JettyApiServlet());
|
||||||
|
apiHolder.getRegistration().setMultipartConfig(new MultipartConfigElement(""));
|
||||||
|
context.addServlet(apiHolder, "/api/*");
|
||||||
|
|
||||||
context.addFilter(new FilterHolder(new SpaFallbackFilter()), "/*", EnumSet.of(DispatcherType.REQUEST));
|
context.addFilter(new FilterHolder(new SpaFallbackFilter()), "/*", EnumSet.of(DispatcherType.REQUEST));
|
||||||
|
|
||||||
|
|||||||
@@ -11,16 +11,21 @@ import com.qi4l.JYso.gadgets.Config.Config;
|
|||||||
import com.qi4l.JYso.gadgets.Config.ysoserial;
|
import com.qi4l.JYso.gadgets.Config.ysoserial;
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.annotation.MultipartConfig;
|
||||||
import javax.servlet.http.HttpServlet;
|
import javax.servlet.http.HttpServlet;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.http.Part;
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
@MultipartConfig
|
||||||
public class JettyApiServlet extends HttpServlet {
|
public class JettyApiServlet extends HttpServlet {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -58,6 +63,8 @@ public class JettyApiServlet extends HttpServlet {
|
|||||||
handleFileDownload(req, resp);
|
handleFileDownload(req, resp);
|
||||||
} else if ("/files/delete".equals(path) && "POST".equalsIgnoreCase(req.getMethod())) {
|
} else if ("/files/delete".equals(path) && "POST".equalsIgnoreCase(req.getMethod())) {
|
||||||
handleFileDelete(req, resp);
|
handleFileDelete(req, resp);
|
||||||
|
} else if ("/files/upload".equals(path) && "POST".equalsIgnoreCase(req.getMethod())) {
|
||||||
|
handleFileUpload(req, resp);
|
||||||
} else {
|
} else {
|
||||||
resp.setStatus(404);
|
resp.setStatus(404);
|
||||||
resp.getWriter().write("{\"error\":\"Not found\"}");
|
resp.getWriter().write("{\"error\":\"Not found\"}");
|
||||||
@@ -159,6 +166,34 @@ public class JettyApiServlet extends HttpServlet {
|
|||||||
resp.getWriter().write(JSON.toJSONString(result));
|
resp.getWriter().write(JSON.toJSONString(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleFileUpload(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
||||||
|
Map<String, Object> result = new LinkedHashMap<>();
|
||||||
|
try {
|
||||||
|
Part filePart = req.getPart("file");
|
||||||
|
if (filePart == null || filePart.getSize() == 0) {
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("error", "no file uploaded");
|
||||||
|
resp.getWriter().write(JSON.toJSONString(result));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String fileName = filePart.getSubmittedFileName();
|
||||||
|
if (fileName == null || fileName.isEmpty()) {
|
||||||
|
fileName = "uploaded_file";
|
||||||
|
}
|
||||||
|
Path targetPath = Paths.get(fileName);
|
||||||
|
try (InputStream input = filePart.getInputStream()) {
|
||||||
|
Files.copy(input, targetPath, StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
}
|
||||||
|
result.put("success", true);
|
||||||
|
result.put("name", fileName);
|
||||||
|
result.put("size", Files.size(targetPath));
|
||||||
|
} catch (ServletException e) {
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("error", "upload failed: " + e.getMessage());
|
||||||
|
}
|
||||||
|
resp.getWriter().write(JSON.toJSONString(result));
|
||||||
|
}
|
||||||
|
|
||||||
private void handleServersStart(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
private void handleServersStart(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
||||||
JSONObject json = readJson(req);
|
JSONObject json = readJson(req);
|
||||||
Map<String, Object> result = new LinkedHashMap<>();
|
Map<String, Object> result = new LinkedHashMap<>();
|
||||||
@@ -236,27 +271,39 @@ public class JettyApiServlet extends HttpServlet {
|
|||||||
resp.getWriter().write(JSON.toJSONString(result));
|
resp.getWriter().write(JSON.toJSONString(result));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
boolean nowRunning;
|
boolean nowRunning = false;
|
||||||
switch (server.toLowerCase()) {
|
switch (server.toLowerCase()) {
|
||||||
case "ldap":
|
case "ldap":
|
||||||
if (LdapServer.isRunning) LdapServer.stop();
|
if (LdapServer.isRunning) {
|
||||||
else new Thread(() -> { try { LdapServer.start(); } catch (Exception ignored) {} }, "ldap-toggler").start();
|
LdapServer.stop();
|
||||||
nowRunning = !LdapServer.isRunning;
|
} else {
|
||||||
|
new Thread(() -> { try { LdapServer.start(); } catch (Exception ignored) {} }, "ldap-toggler").start();
|
||||||
|
nowRunning = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "ldaps":
|
case "ldaps":
|
||||||
if (LdapsServer.isRunning) LdapsServer.stop();
|
if (LdapsServer.isRunning) {
|
||||||
else new Thread(() -> { try { LdapsServer.start(); } catch (Exception ignored) {} }, "ldaps-toggler").start();
|
LdapsServer.stop();
|
||||||
nowRunning = !LdapsServer.isRunning;
|
} else {
|
||||||
|
new Thread(() -> { try { LdapsServer.start(); } catch (Exception ignored) {} }, "ldaps-toggler").start();
|
||||||
|
nowRunning = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "http":
|
case "http":
|
||||||
if (HTTPServer.isRunning) HTTPServer.stop();
|
if (HTTPServer.isRunning) {
|
||||||
else new Thread(() -> { try { HTTPServer.start(); } catch (Exception ignored) {} }, "http-toggler").start();
|
HTTPServer.stop();
|
||||||
nowRunning = !HTTPServer.isRunning;
|
} else {
|
||||||
|
new Thread(() -> { try { HTTPServer.start(); } catch (Exception ignored) {} }, "http-toggler").start();
|
||||||
|
nowRunning = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "rmi":
|
case "rmi":
|
||||||
if (RMIServer.isRunning) RMIServer.stop();
|
if (RMIServer.isRunning) {
|
||||||
else new Thread(() -> { try { RMIServer.start(); } catch (Exception ignored) {} }, "rmi-toggler").start();
|
RMIServer.stop();
|
||||||
nowRunning = !RMIServer.isRunning;
|
} else {
|
||||||
|
new Thread(() -> { try { RMIServer.start(); } catch (Exception ignored) {} }, "rmi-toggler").start();
|
||||||
|
nowRunning = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
result.put("success", false);
|
result.put("success", false);
|
||||||
|
|||||||
Reference in New Issue
Block a user