😍
信息安全笔记
  • 序言
  • 引用
  • devsecops
    • checkmarx
    • codeql
    • coverity
    • fortifiy
    • sca
      • Dependency Check
      • Dependency Track
    • 案例
      • SCA在得物DevSecOps平台上应用
    • 漏洞修复
      • Hello Java Sec
  • 书籍
    • 编译原理
      • 第一章 引论
  • 代码审计
    • JAVA漏洞
      • CRLF注入
      • Java RMI
      • JSONP
      • JWT
      • Log4j2
      • SPEL
      • SQL注入
      • SSRF
      • SSTI
      • XSS
      • XStream
      • XXE
      • 反序列化
      • 命令执行
      • 文件操作
    • 准备
      • 远程调试
      • 配置IDEA
  • 安全测试
    • APP渗透
      • 安卓5版本抓包
      • 安卓7版本抓包
      • 安卓9版本抓包
    • Linux提权
    • WEB应用
    • Windows提权
    • 信息收集
    • 免杀技巧
    • 其他
      • 反弹shell总结
    • 前端绕过
    • 后渗透
    • 容器渗透
    • 攻击绕过
    • 木马病毒
    • 横向移动
      • AS-REP Roasting攻击
      • Kerberoasting 攻击
    • 缓冲区溢出
  • 安全漏洞
    • Linux提权漏洞
    • Linux漏洞
    • Windows提权漏洞
    • Windows漏洞
    • 应用漏洞
    • 未授权漏洞
      • ActiveMQ未授权访问漏洞
      • Apache Flink未授权访问漏洞
      • Atlassian Crowd 未授权访问漏洞
      • clickhouse 未授权访问漏洞
      • CouchDB未授权访问漏洞
      • Docker未授权访问漏洞
      • druid 监控页未授权访问漏洞
      • Dubbo 未授权访问漏洞
      • Hadoop YARN resourcemanager 未授权访问漏洞
      • Hadoop Yarn RPC未授权访问漏洞
      • InfluxDB API 未授权访问漏洞
      • JBoss未授权访问漏洞
      • Jenkins未授权访问漏洞
      • Jupyter Notebook 未授权访问漏洞
      • Kafka Manager 未授权访问漏洞
      • Kibana 未授权访问漏洞
      • Kong未授权访问漏洞
      • Kubernetes Api Server 未授权访问
      • LDAP未授权访问漏洞
      • Memcached未授权访问漏洞
      • MongoDB未授权访问漏洞
      • NFS未授权访问漏洞
      • RabbitMQ 未授权访问漏洞
      • Redis未授权访问漏洞
      • Rsync未授权访问漏洞
      • Spark 未授权访问漏洞
      • Spring Cloud Gateway Server 未授权访问漏洞
      • SpringBoot Actuator未授权访问漏洞
      • VNC Server 未授权访问漏洞
      • Weblogic 未授权访问漏洞
      • Zabbix未授权访问漏洞
      • ZooKeeper未授权访问漏洞
  • 安全证书
    • CISSP
    • CRTO
      • 考证经验分享
    • OSCP
      • 考证经验分享
  • 社会工程学
    • 网络钓鱼
  • 运维配置
    • Kubernetes
      • 安装部署
  • 靶场环境
    • attackdefense
    • HTB
    • tryhackme
    • vulnhub
      • ACID RELOADED
      • ACID SERVER
      • Assertion101
      • BBSCute 1.0.2
      • BILLY MADISON 1.1
      • Bob 1.0.1
      • Born2Root 2
      • Born2Root:1
      • BossPlayersCTF
      • Bottleneck
      • Brainpan 1
      • Breach 1
      • Breach 2.1
      • Breach 3.0.1
      • BSides Vancouver 2018
      • BTRSys2.1
      • Covfefe
      • CYBERSPLOIT 1
      • Darknet:1.0
      • Dawn
      • Dawn2
      • Dawn3
      • DC 1
      • DC 2
      • DC 3.2
      • DC 4
      • DC 6
      • DC 8
      • DC 5
      • DC 7
      • DC 9
      • Deception
      • DEFCON Toronto Galahad
      • DERPNSTINK 1
      • DevGuru 1
      • DEVRANDOM SLEEPY
      • digitalworld.local BRAVERY
      • digitalworld.local DEVELOPMENT
      • digitalworld.local FALL
      • digitalworld.local JOY
      • digitalworld.local MERCY v2
      • digitalworld.local snakeoil
      • digitalworld.local TORMENT
      • DJINN 1
      • Djinn3
      • Election 1
      • Escalate Linux:1
      • EVM 1
      • Five86.2
      • FristiLeaks:1.3
      • Funbox
      • FunboxEasy
      • FunboxEasyEnum
      • FunboxRookie
      • Gaara
      • Geisha
      • Gitroot
      • Glasglow 1.1
      • GoldenEye 1
      • GREENOPTIC 1
      • Ha-natraj
      • Hack Me Please
      • Hacker kid 1.0.1
      • HackLAB:vulnix
      • HACKME 1
      • HACKME 2
      • HA:WORDY
      • Healthcare 1
      • IMF
      • Inclusiveness
      • Infosec Prep OSCP Box
      • InsanityHosting
      • Katana
      • Kioptrix Level 1.1
      • Kioptrix Level 1
      • Kioptrix 2014
      • Kioptrix Level 1.2
      • Kioptrix Level 1.3
      • Kvasir
      • Lampiao
      • LazySysAdmin
      • LemonSqueezy
      • Lin.Security
      • Loly
      • Lord of the Root 1.0.1
      • Metasploitable 3
      • Monitoring
      • MORIA 1.1
      • Mr-Robot:1
      • My-CMSMS
      • Node 1
      • NoName
      • NullByte
      • OZ
      • Photographer 1
      • Pinkys Palace v1
      • Pinkys Palace v2
      • Pinkys Palace v3
      • Pinkys Palace v4
      • Potato
      • Powergrid
      • Prime 1
      • Pwned1
      • PwnLab:init
      • PWNOS:1.0
      • PWNOS:2.0
      • PyExp
      • Raven 1
      • Raven 2
      • Readme 1
      • RICKDICULOUSLYEASY 1
      • Sar:1
      • Sedna
      • Seppuku
      • SickOs 1.2
      • Simple
      • Sky Tower
      • SolidState
      • Solstice
      • SoSimple
      • Spydersec
      • Stapler 1
      • Sumo
      • SUNSET MIDNIGHT
      • SunsetMidnight
      • SunsetNoontide
      • Sunset:Decoy
      • Ted
      • Temple of Doom
      • Tiki-1
      • TOMMY BOY 1
      • Toppo 1
      • TRE 1
      • Troll 1
      • Troll 2
      • Troll 3
      • Vegeta1
      • Violator
      • Vulnerable Docker 1
      • VulnOS 2
      • W34kn3ss 1
      • Wallaby's Nightmare
      • Web Developer 1
      • Wintermute
      • Wpwn
      • xxe
      • Y0usef
      • ZICO2:1
    • 云原生
      • kubernetes-goat
    • 域环境
      • PowerShell 搭建AD域渗透环境
    • 红日靶场
由 GitBook 提供支持
在本页
  • 文件上传
  • 文件读取
  • 文件写入
  • 文件下载
  • 文件删除

这有帮助吗?

  1. 代码审计
  2. JAVA漏洞

文件操作

文件上传

FileController.java
package com.example.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

@RestController
public class FileController {

    @GetMapping("/upload")
    public ModelAndView  showUploadForm() {

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("upload");

        return modelAndView;
    }

    @PostMapping("/upload")
    public String handleFileUpload(@RequestParam("file") MultipartFile file) {
        try {
            // 将上传的文件保存到本地文件系统
            byte[] bytes = file.getBytes();
            Path path = Paths.get(file.getOriginalFilename());
            Files.write(path, bytes);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "redirect:/success";
    }
    
}
upload.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>文件上传</title>
</head>
<body>
<h1>文件上传</h1>
<form method="POST" enctype="multipart/form-data" action="/upload">
    <input type="file" name="file">
    <br/><br/>
    <button type="submit">上传</button>
</form>
</body>
</html>

修复代码

@Controller
public class FileUploadController {
 
    private static final List<String> ALLOWED_EXTENSIONS = Arrays.asList(".jpg", ".jpeg", ".png");

    @PostMapping("/upload")
    public String handleFileUpload(@RequestParam("file") MultipartFile file) {
        try {
            // 验证上传的文件是否为图片类型
            String fileName = file.getOriginalFilename();
            String extension = fileName.substring(fileName.lastIndexOf(".")).toLowerCase();
            if (!ALLOWED_EXTENSIONS.contains(extension)) {
                throw new RuntimeException("Invalid file type.");
            }

            // 验证上传的文件是否为合法的图片文件
            BufferedImage image = ImageIO.read(file.getInputStream());
            if (image == null) {
                throw new RuntimeException("Invalid image file.");
            }

            // 限制上传文件的大小和数量
            if (file.getSize() > 10 * 1024 * 1024) { // 10MB
                throw new RuntimeException("File size exceeds the limit.");
            }

            // 将上传的文件保存到指定位置
            String uploadDir = "/uploads";
            String realPath = request.getServletContext().getRealPath(uploadDir);
            if (realPath == null) {
                throw new RuntimeException("Failed to retrieve upload directory.");
            }
            Path path = Paths.get(realPath, fileName);
            Files.write(path, file.getBytes());

        } catch (IOException e) {
            throw new RuntimeException("Failed to upload file: " + e.getMessage());
        }
        return "redirect:/success";
    }
}

在上面的代码中,我们采取了以下措施:

  1. 我们定义了一个ALLOWED_EXTENSIONS列表,用于存储允许上传的图片文件类型后缀名。当上传的文件类型不在此列表中时,会抛出异常并拒绝上传。

  2. 我们在上传前通过BufferedImage读取上传的图片文件,并对其进行验证。如果文件不是合法的图片文件,将抛出异常并拒绝上传。

  3. 我们对上传的文件大小进行了限制,当上传的文件超过10MB时,将抛出异常并拒绝上传。

  4. 我们将上传的文件保存到应用程序的指定位置(此处为/uploads目录),而不是保存在Web根目录下。这有助于避免攻击者通过构造特定的URL访问并执行上传的文件。

  5. 我们还在捕获异常时提供了更具体的错误信息,以便于排查问题。

文件读取

package com.example.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

@RestController
public class FileController {

    @GetMapping("/readFile")
    public String readFile(@RequestParam String filePath) throws IOException {
        File file = new File(filePath);
        BufferedReader reader = new BufferedReader(new FileReader(file));
        String line = null;
        StringBuilder stringBuilder = new StringBuilder();
        while ((line = reader.readLine()) != null) {
            stringBuilder.append(line);
        }
        reader.close();
        return stringBuilder.toString();
    }
}

修复代码

@RestController
public class FileController {

    private static final String UPLOADS_FOLDER = "/uploads/";
    
    @GetMapping("/readFile")
    public String readFile(@RequestParam String filePath) throws IOException {
        File file = new File(ResourceUtils.getFile(filePath).getAbsolutePath());
        String canonicalPath = file.getCanonicalPath();
        if (!canonicalPath.startsWith(ResourceUtils.getFile(UPLOADS_FOLDER).getCanonicalPath())) {
            return "Access denied";
        }
        if (!file.exists() || !file.isFile()) {
            return "File not found";
        }
        if (!file.canRead()) {
            return "Cannot read file";
        }
        BufferedReader reader = new BufferedReader(new FileReader(file));
        String line = null;
        StringBuilder stringBuilder = new StringBuilder();
        while ((line = reader.readLine()) != null) {
            stringBuilder.append(line);
        }
        reader.close();
        return stringBuilder.toString();
    }
}

先将传递的文件路径转化为绝对路径,然后获取限定文件夹的绝对路径,再判断传递的文件路径是否在限定的目录下。如果文件路径不在限定的目录下,则拒绝访问。

文件写入

package com.example.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

@RestController
public class FileController {

    private static final String UPLOADS_FOLDER = "/uploads/";

    @GetMapping("/writeFile")
    public String writeFile(@RequestParam String fileName, @RequestParam String data) throws IOException {
        File file = new File(fileName);
        FileWriter fileWriter = new FileWriter(file);
        fileWriter.write(data);
        fileWriter.flush();
        fileWriter.close();
        return "File created successfully!";
    }
}

修复代码

package com.example.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

@RestController
public class FileController {

    private static final String UPLOADS_FOLDER = "upload/";

    @GetMapping("/writeFile")
    public String writeFile(@RequestParam String fileName, @RequestParam String data) throws IOException {
        // 检查文件名是否包含目录遍历字符
        if (fileName.contains("../")) {
            return "Invalid file name";
        }

        // 检查文件名是否合法
        if (!fileName.matches("^[a-zA-Z0-9_]*$")) {
            return "Invalid file name";
        }

        // 确定文件路径
        String filePath = UPLOADS_FOLDER + fileName;

        // 创建文件对象
        File file = new File(filePath);

        // 检查目录是否存在
        if (!file.getParentFile().exists()) {
            // 创建目录
            file.getParentFile().mkdirs();
        }

        // 写入文件
        try (FileOutputStream fos = new FileOutputStream(file)) {
            fos.write(data.getBytes());
        }

        return "File created successfully!";
    }
}

文件下载

@RestController
public class FileController {

    private static final String UPLOADS_FOLDER = "upload/";

    @GetMapping("/download")
    public void downloadFile(@RequestParam String fileName, HttpServletResponse response) throws IOException {
        // 确定文件路径
        String filePath = UPLOADS_FOLDER + fileName;

        // 创建文件对象
        File file = new File(filePath);

        // 检查文件是否存在
        if (!file.exists()) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        // 设置响应头信息
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);

        // 读取文件并输出到响应流
        try (InputStream is = new FileInputStream(file);
             OutputStream os = response.getOutputStream()) {
            byte[] buffer = new byte[1024];
            int length;
            while ((length = is.read(buffer)) != -1) {
                os.write(buffer, 0, length);
            }
            os.flush();
        }
    }
}

payload

../upload/test.txt

修复代码

package com.example.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.*;

@RestController
public class FileController {

    private static final String UPLOADS_FOLDER = "upload/";

    @GetMapping("/download")
    public void downloadFile(@RequestParam String fileName, HttpServletResponse response) throws IOException {

        // 检查文件名是否合法
        if (!fileName.matches("^[a-zA-Z0-9_]*$")) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
            return;
        }

        // 确定文件路径
        String filePath = UPLOADS_FOLDER + fileName;
        // 创建文件对象
        File file = new File(filePath);

        // 检查文件是否存在
        if (!file.exists()) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        // 检查文件是否在指定目录下
        if (!file.getAbsolutePath().startsWith(new File(UPLOADS_FOLDER).getAbsolutePath())) {
            response.sendError(HttpServletResponse.SC_FORBIDDEN);
            return;
        }
        // 设置响应头信息
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);

        // 读取文件并输出到响应流
        try (InputStream is = new FileInputStream(file);
             OutputStream os = response.getOutputStream()) {
            byte[] buffer = new byte[1024];
            int length;
            while ((length = is.read(buffer)) != -1) {
                os.write(buffer, 0, length);
            }
            os.flush();
        }
    }
}

文件删除

package com.example.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.*;

@RestController
public class FileController {

    private static final String UPLOADS_FOLDER = "upload/";

    @GetMapping("/delete")
    public void deleteFile(@RequestParam String fileName, HttpServletResponse response) throws IOException {
        // 确定文件路径
        String filePath = UPLOADS_FOLDER + fileName;

        // 创建文件对象
        File file = new File(filePath);

        // 检查文件是否存在
        if (!file.exists()) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        // 删除文件
        if (!file.delete()) {
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            return;
        }

        // 返回删除成功的响应
        response.setStatus(HttpServletResponse.SC_OK);
    }
}

修复代码

package com.example.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.*;

@RestController
public class FileController {

    private static final String UPLOADS_FOLDER = "upload/";

    @GetMapping("/delete")
    public void deleteFile(@RequestParam String fileName, HttpServletResponse response) throws IOException {
        // 检查文件名是否合法
        if (!fileName.matches("^[a-zA-Z0-9_]*$")) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
            return;
        }

        // 确定文件路径
        String filePath = UPLOADS_FOLDER + fileName;

        // 创建文件对象
        File file = new File(filePath);

        // 检查文件是否存在
        if (!file.exists()) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        // 检查文件是否在指定目录下
        if (!file.getAbsolutePath().startsWith(new File(UPLOADS_FOLDER).getAbsolutePath())) {
            response.sendError(HttpServletResponse.SC_FORBIDDEN);
            return;
        }

        // 删除文件
        if (!file.delete()) {
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            return;
        }

        // 返回删除成功的响应
        response.setStatus(HttpServletResponse.SC_OK);
    }
}
上一页命令执行下一页准备

最后更新于2年前

这有帮助吗?

image-20230314150100526
image-20230314151357759