feat:文件管理添加上传,输入不存在的链子正确的报错

This commit is contained in:
qi4l
2026-05-01 19:08:02 +08:00
parent ea96225f17
commit 616bf8330a
7 changed files with 176 additions and 20 deletions
+4
View File
@@ -90,4 +90,8 @@ export function deleteFile(name) {
return api.post('/files/delete', { name })
}
export function uploadFile(formData) {
return api.post('/files/upload', formData)
}
export default api
+26
View File
@@ -906,6 +906,32 @@ select {
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) {
*, *::before, *::after {
transition-duration: 0ms !important;
+75 -2
View File
@@ -4,7 +4,8 @@ import { useTheme } from '../context/ThemeContext'
import {
getStatus, toggleServer, getGadgets,
generatePayload as apiGenerate, updateConfig, setAuthToken, getLogs,
getFiles, downloadFile, deleteFile as apiDeleteFile
getFiles, downloadFile, deleteFile as apiDeleteFile,
uploadFile
} from '../api'
export default function Dashboard() {
@@ -53,6 +54,9 @@ export default function Dashboard() {
const logEndRef = useRef(null)
const [files, setFiles] = useState([])
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']
@@ -149,7 +153,7 @@ export default function Dashboard() {
setMsg({ success: '', error: res.data.error || 'Generation failed' })
}
} catch (e) {
setMsg({ success: '', error: 'Failed to generate payload' })
setMsg({ success: '', error: e.response?.data?.error || 'Failed to generate payload' })
} finally { setLoading(false) }
}
@@ -279,6 +283,56 @@ export default function Dashboard() {
} 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(() => {
if (activeJndiTab !== 'logs') return
fetchLogs()
@@ -814,6 +868,25 @@ export default function Dashboard() {
{filesLoading ? 'Loading...' : 'Refresh'}
</button>
</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">
{files.length === 0 ? (
<div className="file-empty">No saved files</div>
+6 -1
View File
@@ -38,7 +38,12 @@ public class Starter {
if (args[0].equals("-y")) {
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
final Class<? extends ObjectPayload<?>> payloadClass = ObjectPayload.Utils.getPayloadClass(payloadType);
if (payloadClass == null) {
System.err.println("Invalid payload type '" + payloadType + "'");
printUsage(options);
System.exit(1);
throw new IllegalArgumentException("Invalid payload type '" + payloadType + "'");
}
@@ -15,6 +15,7 @@ import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import javax.servlet.DispatcherType;
import javax.servlet.MultipartConfigElement;
import java.awt.*;
import java.awt.datatransfer.StringSelection;
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 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));
@@ -11,16 +11,21 @@ import com.qi4l.JYso.gadgets.Config.Config;
import com.qi4l.JYso.gadgets.Config.ysoserial;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.*;
@MultipartConfig
public class JettyApiServlet extends HttpServlet {
@Override
@@ -58,6 +63,8 @@ public class JettyApiServlet extends HttpServlet {
handleFileDownload(req, resp);
} else if ("/files/delete".equals(path) && "POST".equalsIgnoreCase(req.getMethod())) {
handleFileDelete(req, resp);
} else if ("/files/upload".equals(path) && "POST".equalsIgnoreCase(req.getMethod())) {
handleFileUpload(req, resp);
} else {
resp.setStatus(404);
resp.getWriter().write("{\"error\":\"Not found\"}");
@@ -159,6 +166,34 @@ public class JettyApiServlet extends HttpServlet {
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 {
JSONObject json = readJson(req);
Map<String, Object> result = new LinkedHashMap<>();
@@ -236,27 +271,39 @@ public class JettyApiServlet extends HttpServlet {
resp.getWriter().write(JSON.toJSONString(result));
return;
}
boolean nowRunning;
boolean nowRunning = false;
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;
if (LdapServer.isRunning) {
LdapServer.stop();
} else {
new Thread(() -> { try { LdapServer.start(); } catch (Exception ignored) {} }, "ldap-toggler").start();
nowRunning = true;
}
break;
case "ldaps":
if (LdapsServer.isRunning) LdapsServer.stop();
else new Thread(() -> { try { LdapsServer.start(); } catch (Exception ignored) {} }, "ldaps-toggler").start();
nowRunning = !LdapsServer.isRunning;
if (LdapsServer.isRunning) {
LdapsServer.stop();
} else {
new Thread(() -> { try { LdapsServer.start(); } catch (Exception ignored) {} }, "ldaps-toggler").start();
nowRunning = true;
}
break;
case "http":
if (HTTPServer.isRunning) HTTPServer.stop();
else new Thread(() -> { try { HTTPServer.start(); } catch (Exception ignored) {} }, "http-toggler").start();
nowRunning = !HTTPServer.isRunning;
if (HTTPServer.isRunning) {
HTTPServer.stop();
} else {
new Thread(() -> { try { HTTPServer.start(); } catch (Exception ignored) {} }, "http-toggler").start();
nowRunning = true;
}
break;
case "rmi":
if (RMIServer.isRunning) RMIServer.stop();
else new Thread(() -> { try { RMIServer.start(); } catch (Exception ignored) {} }, "rmi-toggler").start();
nowRunning = !RMIServer.isRunning;
if (RMIServer.isRunning) {
RMIServer.stop();
} else {
new Thread(() -> { try { RMIServer.start(); } catch (Exception ignored) {} }, "rmi-toggler").start();
nowRunning = true;
}
break;
default:
result.put("success", false);