581 字
3 分钟
CVE-2025-64512分析
CVE
CVE-2025-64512分析,从PDF对象树到RCE
捉襟见肘,对于pdf安全问题,直接的pdf是没有什么威胁的,造就一个事物有没有威胁要看自己的原生库是
怎么对其进行加载的。接下来会说明一些有关PDF的数据结构
整个PDF的结构可以类似于一个对象树,很多时候可以相互引用
比如说入口是
1 0 obj<< /Type /Catalog /Pages 2 0 R>>endobj这里就是1对象,并且说明了/Page的值位于2对象,后面跟的0代表版本,这个版本多数是用于pdf更新之后
的引用
5 0 obj<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica>>endobj字体对象,如果说要引用,那就在其他对象引用对象5
因为对于对象嵌套,数据流压缩,对象流压缩的宽容度很大,这里看python的pdfminer对于其的处理
看看在pdfminer的前些版本,对于字体的Cmap处理
Cmap是映射表,当发现对于/Encoding的值,是可以当作路径去load的,看看逻辑
def _load_data(cls, name: str) -> Any: name = name.replace("\0", "") filename = "%s.pickle.gz" % name log.debug("loading: %r", name) cmap_paths = ( os.environ.get("CMAP_PATH", "/usr/share/pdfminer/"), os.path.join(os.path.dirname(__file__), "cmap"), ) for directory in cmap_paths: path = os.path.join(directory, filename) if os.path.exists(path): gzfile = gzip.open(path) try: return type(str(name), (), pickle.loads(gzfile.read())) finally: gzfile.close() raise CMapDB.CMapNotFound(name)可以看出cmap_paths是可信的,在environ,但是filename貌似是直接拼字段,追溯下
def get_cmap_from_spec(self, spec: Mapping[str, Any], strict: bool) -> CMapBase: """Get cmap from font specification
For certain PDFs, Encoding Type isn't mentioned as an attribute of Encoding but as an attribute of CMapName, where CMapName is an attribute of spec['Encoding']. The horizontal/vertical modes are mentioned with different name such as 'DLIdent-H/V','OneByteIdentityH/V','Identity-H/V'. """ cmap_name = self._get_cmap_name(spec, strict)
try: return CMapDB.get_cmap(cmap_name) except CMapDB.CMapNotFound as e: if strict: raise PDFFontError(e) return CMap()cmap_name也就是name的规范化
def _get_cmap_name(spec: Mapping[str, Any], strict: bool) -> str: """Get cmap name from font specification""" cmap_name = "unknown" # default value
try: spec_encoding = spec["Encoding"] if hasattr(spec_encoding, "name"): cmap_name = literal_name(spec["Encoding"]) else: cmap_name = literal_name(spec_encoding["CMapName"]) except KeyError: if strict: raise PDFFontError("Encoding is unspecified")
if type(cmap_name) is PDFStream: # type: ignore[comparison-overlap] cmap_name_stream: PDFStream = cast(PDFStream, cmap_name) if "CMapName" in cmap_name_stream: cmap_name = cmap_name_stream.get("CMapName").name elif strict: raise PDFFontError("CMapName unspecified for encoding")
return IDENTITY_ENCODER.get(cmap_name, cmap_name)对于encoding的值,可以看看对于其的取值逻辑
其中name就是传入的/Encoding 的值,这两个分支分别代表了原始对象以及复合对象,
并且spec是在最开始PDF进入时就init加载的,于是便可以进行代码执行了
以上
LAST UPDATE / 最后更新