米桃安全漏洞讲堂系列第3期:任意文件上传漏洞

一、概述

信息系统的建设过程中,经常会涉及文件上传的业务功能需求。如果在系统研发过程中未对上传的文件进行合理限制,或限制功能存在绕过漏洞,则系统投产后存在“任意文件上传漏洞”。
恶意攻击者可以通过上传恶意文件并在服务器端执行,造成严重危害。如下图某WebShell所示,攻击者可浏览并查看服务器文件,并对服务器拥有管理权限。
米桃安全漏洞讲堂系列第3期:任意文件上传漏洞_第1张图片

历年企业护网行动中,红队(攻击队)经常利用系统的“文件上传”功能,上传木马工具,最终实现对攻击目标的控制,攻陷目标。因此,对于“任意文件上传漏洞”的防护,平台提供方需要高度重视。

二、文件上传漏洞原理

2.1 漏洞原理

文件上传漏洞的出现,是由于代码没有严格限制用户上传文件后缀以及文件类型,导致攻击者向某个可通过Web访问的目录上传任意脚本文件,并能够将这些文件传递给解释器,在远程服务器上执行任意脚本。
存在文件上传功能的地方都有可能存在文件上传漏洞。比如用户头像、视频、照片分享、用户意见反馈等。如果移动端APP端也存在类似的操作,同理也存在文件上传漏洞风险。
米桃安全漏洞讲堂系列第3期:任意文件上传漏洞_第2张图片

2.2 文件上传漏洞产生原因

一个完整的文件漏洞利用过程,需要三个步骤:

(1)攻击者通过文件上传功能,成功上传恶意文件至后端服务器
用户上传文件时,系统前后端均未进行文件格式检查,造成用户可以上传非功能设定的文件。如jsp、asp、php等可执行文件。

(2)攻击者获得上传恶意文件所在路径,并能成功访问
成功上传可执行文件后,系统服务端返回上传文件访问路径。用户通过浏览器访问该链接,即可查看自己上传的文件。
米桃安全漏洞讲堂系列第3期:任意文件上传漏洞_第3张图片
高级别攻击者,可以在服务端不返回文件目录的情况下,主动去构造上传文件访问路径+文件名。或者结合其他漏洞访问服务器文件目录,获取文件在服务器上的绝对路径。

(3)上传的恶意文件,可执行
上传恶意的可执行文件后(jsp、php、asp等),上传目录允许该文件作执行操作。访问该文件拉起可执行木马文件,获得当前主机命令执行权限或者文件管理权限,进而控制整台主机。该步骤就是红蓝攻防中所称的getshell操作。
米桃安全漏洞讲堂系列第3期:任意文件上传漏洞_第4张图片

在可执行恶意文件这一领域中,随着安全软件检测手段的更新,攻击者的攻击手段也在不断进化。从最初的直接上传多功能木马(俗称大马),改成仅包含上传功能的木马(俗称小马)再二次上传大马,到更精简版的一句话木马,甚至近些年流行的内存马。都是在攻守双方在不断博弈期间的升级手段。
木马相关内容,本期只作简要说明,详情将在下一期内容中说明。
米桃安全漏洞讲堂系列第3期:任意文件上传漏洞_第5张图片

2.3 漏洞危害

“文件上传”本身功能没有危害,危害发生在恶意文件上传后在服务器端执行,常见危害包括:

  1. 执行任意代码: 攻击者可以上传包含恶意代码的文件,例如 Web shell(用于远程执行命令的脚本文件),从而在受影响的服务器上执行任意命令。这使得攻击者能够控制服务器、窃取数据、修改文件等。
  2. 窃取敏感信息: 攻击者可以上传恶意脚本,用于窃取服务器上的敏感信息,包括数据库凭证、用户信息、密码等。
  3. 横向移动: 通过上传恶意文件,攻击者可以在服务器上建立后门,从而在网络内部进行横向移动,攻击其他系统。
  4. 拒绝服务攻击(DoS): 攻击者可以上传大文件或者耗尽服务器资源的文件,导致服务器过载,无法提供正常服务。
  5. 恶意文件传播: 攻击者可以上传恶意文件,然后将下载链接传播给其他用户,使得这些用户受到攻击。
  6. 绕过权限和访问控制: 攻击者可以上传包含针对安全漏洞的攻击代码的文件,绕过服务器上的权限和访问控制,从而执行未经授权的操作。
  7. 破坏文件完整性: 攻击者可以上传恶意文件,修改、删除或篡改服务器上的文件,破坏文件的完整性。
  8. 利用服务器作为攻击平台: 攻击者可以利用受感染的服务器发送垃圾邮件、发起DDoS攻击、托管恶意网站等,使用服务器作为攻击平台。

2.4 文件上传漏洞案例

2.4.1 文件上传漏洞+webshell,完成主机权限控制
  1. 针对目标网站,使用弱密码爆破方式,登录web后台。账号密码为admin/passw0rd。
    米桃安全漏洞讲堂系列第3期:任意文件上传漏洞_第6张图片

  2. 在界面风格>编辑模板/css文件>添加模板处将aspx一句话木马添加成html文件,1.asp
    此处即为文件上传漏洞的利用方式。新增模板地址即为木马访问路径。
    米桃安全漏洞讲堂系列第3期:任意文件上传漏洞_第7张图片

  3. 使用木马连接工具连接上传的一句话木马文件1.asp地址。成功完成对目标主机的文件控制。攻陷主机为Windows系统。
    米桃安全漏洞讲堂系列第3期:任意文件上传漏洞_第8张图片

  4. 利用该木马管理工具,再次上传大马(具有多种功能的木马),完成对主机、文件、数据库的完全控制。
    米桃安全漏洞讲堂系列第3期:任意文件上传漏洞_第9张图片
    此时,即完成了一次getshell的攻击过程。后续的内网扩展内容,不在本文讨论范围。

三、漏洞检测方法

“任意文件上传漏洞”通常按以下测试思路进行检测

检测服务端文件后缀名过滤是否严格
如黑名单模式下过滤了.php、.jsp、.asp等可执行脚本文件,但却未过滤.php2、.php3、.php5等。以及双扩展名绕过。如上传xxx.jsp.jsp文件。 常见可执行文件后缀扩容包括:

PHP: phtml, .php, .php3, .php4, .php5, and .inc
ASP: asp, .aspx
PERL: .pl, .pm, .cgi, .lib
JSP: .jsp, .jspx, .jsw, .jsv, .jspf
Python:.py, .py3, .pyw, .pyx、
Coldfusion: .cfm, .cfml, .cfc, .dbm

检测文件上传限制是否仅在浏览器端JS(前端)限制
可通过抓包修改上传文件后缀名进行测试。

在检测过程中使用字符截断技术
如“\0”、“?”、“%00”等,绕过文件后缀名过滤逻辑,如aaa.jsp%00.jpg,在只可上传jpg文件的位置上传jsp文件,在可以控制文件路径的情况下,使用超长的文件名也有可能会导致文件路径截断,绕过服务端限制。

检测Web中间件是否存在文件解析漏洞(如IIS解析漏洞、Apache 文件解析问题)。
当存在漏洞时,可能导致原本不可被执行的图片,.htaccess等文件被视作asp,jsp等文件进行解析,从而可上传包含恶意代码的图片文件。

检测第三方组件是否包含文件上传漏洞,典型案例有CKEditor。

检测服务器是否支持HTTP PUT方法,能否在无文件上传功能的场景下上传文件。

检查HTTPHeader中的Content-Type,修改为text/html进行绕过。

只要能通过上述方式,上传可执行脚本文件,并正常访问文件,即证明该功能存在“任意文件上传漏洞”。

四、漏洞防护措施

4.1 研发设计视角

任意文件上传漏洞的防护思路。从研发设计角度,可以从其攻击流程三步骤入手。“允许上传恶意文件——访问上传文件——文件被执行”。只需要打断其攻击链,均可实现对文件上传漏洞攻击的防护。

4.1.1 文件上传角度防护

1、后端白名单检查文件扩展名
在判断文件类型时,可以结合使用MIME Type、后缀检查等方式。在文件类型检查中,强烈推荐白名单方式,不属于白名单范围的文件类型,不允许上传。黑名单的方式已经无数次被证明是不可靠的。此外,对于图片的处理,可以使用压缩函数或者resize函数,在处理图片的同时破坏图片中可能包含的HTML代码。不建议通过前端实现校验,攻击者可通过抓包改包方式绕过前端验证。

2、后端限制文件上传大小
辅助手段。明确上传的文件场景,可预先限定文件大小。防止上传大马等非正常文件。

4.1.2 文件访问角度防护

3、文件名随机命名 文件上传至服务器后,对文件随机命名。如UUID、GUID,不允许用户自定义。可增加攻击者访问上传文件难度。像shell.php.rar.rar和crossdomain.xml这种文件,都将因为重命名而无法攻击。

4、隐藏上传文件目录 在文件上传后,服务端不返回文件访问路径。使攻击者无法找到上传文件,切掉其攻击路径。

4.1.3 文件执行角度防护

5、上传文件保存目录不可执行
要求上传文件的保存目录无可执行权限,不可解析jsp、php、asp、py等脚本语言。只要web容器无法解析该目录下面的文件,即使攻击者上传了脚本文件,服务器本身也不会受到影响。

6、文件内容检测
通过威胁检测等工具对文件进行安全检查,可有效防范图片马和文件二次渲染。

4.1.4 安全编码示例

代码层面,任意文件上传漏洞防护思路,研发同学可参考如下“任意文件上传漏洞”安全编码示例(Java):

import java.io.*;
import java.util.UUID;
public class FileUploadHandler {

    private static final String UPLOAD_DIR = "/path/to/upload/directory"; // 上传文件保存的目录
    private static final String[] ALLOWED_EXTENSIONS = {"jpg", "jpeg", "png", "gif"}; // 允许上传的文件扩展名白名单
    private static final long MAX_FILE_SIZE = 10 * 1024 * 1024; // 最大文件大小限制为10MB
    public boolean isValidFileExtension(String fileName) {
        String extension = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
        for (String allowedExtension : ALLOWED_EXTENSIONS) {
            if (extension.equals(allowedExtension)) {
                return true;
            }
        }
        return false;
    }

    public boolean isValidFileSize(long fileSize) {
        return fileSize <= MAX_FILE_SIZE; //限制上传文件大小
    }

    public String saveUploadedFile(InputStream inputStream, String originalFileName) throws IOException {
        String fileExtension = originalFileName.substring(originalFileName.lastIndexOf("."));
        String randomFileName = UUID.randomUUID().toString() + fileExtension;
        String filePath = UPLOAD_DIR + File.separator + randomFileName;

        try (OutputStream outputStream = new FileOutputStream(filePath)) {
            int bytesRead;
            byte[] buffer = new byte[8192];
            while ((bytesRead = inputStream.read(buffer, 0, 8192)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
        }

        return randomFileName;
    }

    public static void main(String[] args) {
        // 示例用法
        FileUploadHandler uploadHandler = new FileUploadHandler();
        String uploadedFileName = "example.jpg"; // 假设上传的文件名为example.jpg
        long uploadedFileSize = 5000000; // 假设上传的文件大小为5MB

        if (uploadHandler.isValidFileExtension(uploadedFileName) && uploadHandler.isValidFileSize(uploadedFileSize)) {
            try {
                // 假设inputStream是上传文件的输入流
                InputStream inputStream = new FileInputStream("/path/to/uploaded/file/example.jpg");
                String savedFileName = uploadHandler.saveUploadedFile(inputStream, uploadedFileName);
                System.out.println("File saved with random name: " + savedFileName);
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            System.out.println("Invalid file. File extension or size is not allowed.");
        }
    }
}

4.2 安全开发流程SDL视角

SDL的角度来看,“任意文件上传漏洞”可通过以下措施进行防范或缓解:

4.2.1 设计阶段
  • 考虑在服务端加入通用的文件上传校验逻辑;
  • 使用白名单等的方法对文件名进行过滤,并过滤存在安全隐患的特殊字符;
  • 使用随机数改写文件名和文件路径;
  • 将上传文件限制在某一路径下,并对此文件上传目录进行限制;
  • 对文件内容进行校验。
4.2.2 开发阶段
  • 开发完成后对源代码和环境配置进行审核,确保相关逻辑按照设计实现。
4.2.3 测试阶段
  • 对文件上传功能进行严格测试,修复必要的缺陷。
4.2.4 运维阶段
  • 定期对应用和Web中间件等安排检查,及时更新并加固中间件,可以有效避免因为中间件的漏洞而导致的文件上传漏洞;
  • 使用入侵检测系统、防病毒软件等监测恶意文件上传情况;
  • 采用WAF等安全防护设备可以有效的防御常见漏洞

五、总结展望

综上,文件上传漏洞指攻击者利用程序缺陷,绕过系统对文件的验证与处理策略,将恶意程序上传到服务器并获得执行服务器端命令的能力。本文简要讲述了任意文件上传漏洞原理、危害、测试方法及防御措施。
该漏洞可与其他漏洞结合利用,尤其是与WebShell工具结合,可造成更大危害,如直接完成对主机权限控制。企业在研发、测试阶段,需重点关注。

本系列往期链接

米桃安全漏洞讲堂系列第2期:XSS跨站脚本攻击漏洞-CSDN博客
米桃安全漏洞讲堂系列第1期:SQL注入漏洞-CSDN博客

你可能感兴趣的:(信息安全,安全漏洞,web安全,网络,安全,tcp/ip)