打印这一块

历史前言

在CUPS出现之前,Unix/Linux系统的打印功能存在但十分低效。Windows和MacOS对打印机的适配比较好,因为厂家往往不愿意让自己的驱动程序开源,同时Linux系统分支复杂种类繁多,用户较少,使得打印机很少对Linux系统进行适配。

早期打印与LPD协议

在个人计算机普及之前,打印机是昂贵且集中的资源,通常由多个用户共享。这种共享环境催生了最早的打印管理系统。

LPD(Line Printer Daemon,行式打印守护程序) 它是一个轻量、简单的协议,专门为打印任务设计,功能单一。LPD 的一个显著优势是配置简单,且跨平台性很好,能在不同操作系统间轻松共享打印机。但同时,由于它诞生于早期的互联网环境,设计上几乎没有考虑安全性,缺乏身份验证和数据加密机制,数据传输是明文的,因此被认为“安全性较弱”。

在 Windows 上添加打印机时,如果选择“创建新端口”为 LPR Port,就是在使用 LPD 协议。在 Linux 等 Unix-like 系统中,使用 lpr 命令提交作业时,后台走的也是这个流程。虽然现在 CUPS 更通用,但在很多嵌入式设备(如路由器、NAS)和旧系统中,LPD 依然是标配。

然而,LPD的设计带着浓厚的“字符打印时代”的特征。当时的打印机多为击打式打印机(类似打字机),使用固定的字符集,打印数据流简单,数据量也小。随着图形用户界面的普及和激光打印机的出现,打印技术从基于字符的时代迈入了图形化时代。PostScript等页面描述语言的发明,使得打印机可以打印任意字体和图像,但这同时也带来了巨大的数据传输和复杂的处理需求。

技术演进带来的挑战

LPD协议的局限性在此背景下逐渐暴露:

  • 协议功能单一:LPD不支持现代打印所需的双向通信,无法从打印机获取状态信息、错误报告或耗材余量等反馈。
  • 安全机制薄弱:LPD协议缺乏强大的用户认证和数据加密功能,在现代网络环境中存在安全隐患。
  • 设备支持受限:LPD最初主要为行式打印机设计,难以很好地支持具备各种高级功能的图形化打印机。
  • 管理方式固化:它无法优雅地支持同时连接和管理多个打印设备,尤其在不同厂商、不同接口标准的设备混合使用的场景下。

此外,市场上还存在其他接口标准,如HP公司为网络打印机开发的JetDirect(使用TCP 9100端口)等,它们各自为政,进一步加剧了打印系统的碎片化。

从BSD到System V

除了LPD,Unix系统中还存在另一套源自System V的打印系统。它使用 lplpstatcancel 等命令。虽然命令和LPD系统不同,但其底层架构和设计理念同样古老,并未解决打印技术发展所带来的根本性挑战。

总的来说,在CUPS诞生之前,Unix/Linux的打印世界是分散、低效且充满兼容性问题的。LPD作为服务端,其功能和协议标准都已远远落后于硬件的发展。用户和系统管理员常常需要面对复杂的配置、不稳定的连接和有限的功能支持,“在Linux下成功打印”甚至一度成为一项颇具挑战性的任务。

CUPS的出现,正是为了终结这种混乱,为Unix/Linux带来一种现代、可靠且统一的打印解决方案。

CUPS (通用UNIX打印系统,Common UNIX Printing System)

诞生背景与发展历程

CUPS的诞生可以追溯到1997年,由Michael Sweet创办的Easy Software Products公司开始开发。1999年,第一个公开测试版面世。CUPS的设计初衷是解决当时Unix/Linux打印系统面临的碎片化困境——LPD协议功能单一、缺乏双向通信、安全管理薄弱,而不同厂商又各自推出JetDirect等互不兼容的接口标准。

值得注意的是,CUPS最初的设计也使用了LPD协议,但由于LPD的固有局限和厂商之间的不兼容问题,开发团队最终选择了 IPP (互联网打印协议,Internet Printing Protocol) 作为核心协议。这一决策成为CUPS成功的关键。

CUPS的普及速度很快。它迅速成为大多数Linux发行版的默认打印系统。2002年3月,苹果公司(Apple Inc.)采纳CUPS作为Mac OS X 10.2的打印系统。2007年2月,苹果聘请了首席开发者Michael Sweet并购买了CUPS的源代码。2019年底,Michael Sweet离开苹果后,OpenPrinting组织接手了该项目,继续推动其发展。CUPS这个名称最初是“Common UNIX Printing System”的缩写,但从CUPS 1.4版本开始,由于UNIX商标的法律问题,名称被简化为单纯的“CUPS”。

与Windows的适配 (SMB协议)

**SMB (Server Message Block, 服务器消息块协议) **是一个复杂的、集成度高的协议,它最大的优点是无需单独安装客户端,Windows、macOS 和 Linux 都内置了对它的支持,对普通用户非常友好。其现代版本(如 SMB 2.0/3.0)内置了强大的加密和身份验证机制,安全性远高于 LPD,是Windows网络环境下共享文件和打印机的核心协议。在传统的Linux打印方案中,连接Windows共享打印机往往需要复杂的配置和额外的中间层。CUPS通过集成SMB后端,使得Linux用户可以像访问本地打印机一样,直接向Windows共享的打印机提交任务。这一能力在实际的企业混合网络环境中尤为重要,它让Linux系统能够融入以Windows为中心的办公网络,而无需改变既有的打印共享架构。

CUPS核心架构

感性地来说,当你使用CUPS服务配置好打印机后,你便可以正常在Linux系统上使用该打印机进行打印,如果开启share选项,在能访问CUPS服务的范围内,其他设备便可以通过网络连接到你共享出去的打印机,就像他们添加一台USB打印机一样。

CUPS的核心可以概括为一个模块化的开源打印系统,它以IPP作为管理打印机、打印请求和打印队列的基础协议。

其工作流程可以分解为以下几个关键环节:

  1. 任务接收与队列管理:当用户通过应用程序发起打印时,CUPS首先会创建一个打印任务(Job)。这个任务包含了目标打印队列、文档名称以及页面描述信息。CUPS为每个任务分配编号(如queue-1、queue-2),以便用户后续监控或取消任务。
  2. 格式转换(过滤器系统):这是CUPS最核心的能力之一。它接收应用程序产生的页面描述(如“在此处换行”、“在此处画线”),然后自动确定并使用最佳的**过滤器(filters)**和打印机驱动程序,将这些描述转换成打印机能够理解的语言(如PostScript、PCL或打印机特定的光栅格式)。
  3. 数据传输(后端系统):经过转换后的可打印数据,最后被传递给**后端(backend)**程序。后端负责通过具体的通信接口(如USB、TCP/IP网络)将数据发送到目标打印机。任务完全打印完成后,CUPS会将其从队列中移除,并继续处理下一个任务。

为了兼容旧有系统,CUPS还提供了对LPD协议的有限支持,并保留了传统的System V(lp)和Berkeley(lpr)打印命令接口,使得用户和应用程序几乎无需改变使用习惯。

主要特性与优势

相较于前文所述的LPD等传统方案,CUPS带来了革命性的进步:

  • 统一的协议标准(IPP):IPP是基于HTTP协议构建的,它天然支持访问控制、用户认证和数据加密,比LPD强大且安全得多。使用IPP,用户可以验证打印机或服务器的状态信息、远程管理打印机,甚至通过浏览器直接打印作业。
  • 广泛的协议兼容性:CUPS不仅支持IPP,同时提供对LPD、SMB、JetDirect和AppSocket等多种通信协议的支持。这使得CUPS能够无缝接入各种网络打印环境。
  • 自动设备发现:CUPS能够自动检测网络上的打印机。它支持使用mDNS(Bonjour)和SNMP等协议搜索打印机,也能通过CUPS浏览功能发现其他CUPS服务器共享的打印机。需要说明的是,CUPS的浏览能力随版本有所变化——1.6.x至2.1.x期间曾一度被移除,由独立的cups-browsed守护进程接管;该进程可创建持久队列,并支持集群和负载均衡等高级功能。从CUPS 2.2.4开始,原生浏览能力恢复(通过avahi-daemon实现,但只创建临时队列),cups-browsed则转向处理更复杂的发现需求。对于直接连接的USB打印机,热插拔事件也能被自动识别并创建打印队列。
  • 便捷的Web管理界面:CUPS提供了一个内置的、基于Web浏览器的管理界面。配置和使用 CUPS 打印服务器(Red Hat)基本配置完成后,用户在浏览器中访问对应ip地址的631端口,即可完成添加打印机、配置选项、管理任务等几乎所有操作。
  • 标准化的驱动模型(PPD):CUPS使用Adobe的PostScript打印机描述(PPD)文件作为驱动模型。这为厂商提供了一种标准化的方式来描述其打印机的特性和选项,简化了驱动开发。不过需要说明的是,CUPS官方已宣布传统的驱动和PPD文件方式已弃用,未来将全面转向基于IPP的现代标准。

PPD文件

PPD (PostScript Printer Description, PostScript打印机描述) 文件是一种文本格式的配置文件,由打印机厂商为其PostScript打印机或打印机系列创建,用于完整描述设备的功能和特性。

CUPS对打印机的配置要使用PPD文件。正是由于PPD是文本文件,所以可以在不同的系统中通用,也可以在厂商提供的驱动中找到。

PPD文件本质上充当了打印机的驱动描述层——它告诉操作系统打印机“能做什么”。具体来说,它包含以下关键信息:

  • 打印机支持的语言级别(如PostScript Level 2)
  • 是否支持彩色打印
  • 允许的纸张尺寸(A4、Letter等)
  • 输入纸盒选项
  • 双面打印能力
  • 字体信息
  • 分辨率选项

例如:

*LanguageLevel: "2"
*ColorDevice: True
*DefaultColorSpace: CMYK
*Throughput: "10"

需要说明的是,PPD文件并非完整的驱动程序。用户在使用CUPS配置打印机时,虽然只需选择或上传PPD文件,但真正完成数据转换工作的是CUPS背后的过滤器系统(如pdftopdfpstopsgstoraster等)和后端程序(如usbsocket后端等)。PPD文件更像一份“说明书”,告诉系统应该调用哪些组件以及如何调用。

打印流程

整体的打印流程大概是:

步骤1:生成源文件 —— 用户执行打印操作后,应用程序将待打印内容生成PDF文件(如原格式非PDF则先转换),并提交至CUPS。

步骤2:确定转换方案 —— CUPS查阅目标打印机的PPD文件,根据文件描述判断应使用哪种过滤器,以便将PDF转换为打印机支持的语言(如PJL、PCL、位图或原生PDF)。

步骤3:执行格式转换 —— 选定的过滤器将PDF文件转换成打印机能够识别的数据格式。

步骤4:数据输出 —— 转换完成的数据被交给对应的后端程序。例如,USB连接打印机时,系统会调用USB后端完成最终发送。

IPP (互联网打印协议,Internet Printing Protocol)

IPP 是一个基于HTTP的应用层协议,专门设计用于在客户端和打印机(或打印服务器)之间进行打印任务的管理和控制。它由IETF(互联网工程任务组)于1997年开始制定,与CUPS的诞生几乎同期。

如果说LPD是为“字符打印时代”设计的轻量协议,那么IPP就是为“互联网时代的打印需求”的现代方案。

IPP的核心设计理念

IPP的设计借鉴了HTTP的成功经验,具有以下几个核心理念:

理念 说明
基于HTTP IPP使用HTTP作为传输协议,继承了HTTP的请求/响应模型、内容类型协商和认证机制
面向对象 将打印机、打印任务等抽象为对象,每个对象都有相应的属性和操作
可扩展 通过定义新的属性和操作来扩展功能,而不破坏已有实现
双向通信 客户端不仅可以发送任务,还能查询打印机状态、任务进度等信息

IPP的URL格式

在IPP协议中,打印机通过URL来标识。常见的IPP URL格式有:

ipp://printer-ip-address:631/ipp/port
ipps://printer-ip-address:631/ipp/port   (基于TLS/SSL加密版本)
  • 631 是IPP服务的默认端口号
  • /ipp/port 是常见的打印机资源路径(不同厂商可能略有差异)

你可能见过 i//ipps:// 开头的地址,这就是在访问IPP打印机。而现代macOS和iOS系统广泛使用的AirPrint,其底层核心就是IPP。

IPP的主要操作

IPP定义了一套完整的操作集合,涵盖了打印任务的全生命周期:

操作类型 操作名 说明
打印机操作 Get-Printer-Attributes 查询打印机的能力和状态
Get-Jobs 查询指定打印机的所有任务
Pause-Printer / Resume-Printer 暂停/恢复打印机
任务操作 Print-Job 提交打印任务(最核心的操作)
Cancel-Job 取消指定的打印任务
Get-Job-Attributes 查询某个任务的详细状态
Hold-Job / Release-Job 暂停/恢复某个任务

就像图片中的:

Wireshark_mju5PWBSYH

IPP打印流程大概如下:

  1. 发现打印机(mDNS/DNS-SD广播)
  2. Get-Printer-Attributes(查询打印机能力)
  3. 打印机返回支持的功能(纸张尺寸、双面、彩色等)
  4. Print-Job(提交打印数据)
  5. 打印机返回任务ID和状态
  6. Get-Job-Attributes(可选:轮询任务进度)
  7. 返回任务状态(pending/processing/completed)

IPP的安全性可以这样理解——HTTP : HTTPS = IPP : IPPS

IPP与无驱动打印

IPP最重要的贡献之一,是推动了无驱动打印的普及。

传统打印流程依赖PPD文件和过滤器进行格式转换,而现代IPP打印机可以直接接受标准化的页面描述格式(如PDF、PWG Raster)。客户端和打印机之间通过IPP协商:

  1. 客户端询问打印机:“你能接收什么格式的数据?”
  2. 打印机回答:“我支持PDF、PCL、PWG Raster……”
  3. 客户端选择双方都支持的格式(通常是PDF),直接发送

这意味着用户不再需要为每款打印机安装驱动程序。只要打印机支持IPP Everywhere或AirPrint,添加打印机就像连接WiFi一样简单。

IPP Everywhere

IPP Everywhere 是由打印机工作组(PWG)制定的IPP认证计划,旨在定义一套现代打印机的通用标准。

符合IPP Everywhere标准的打印机必须满足以下要求:

  • 支持PDF作为文档格式
  • 支持PWG Raster作为光栅格式
  • 支持mDNS/DNS-SD进行网络发现
  • 实现一组核心的IPP操作

目前,大多数新出厂的网络打印机都已支持IPP Everywhere。这也是CUPS官方弃用PPD文件的底气所在。

IPP协议流量分析

IPP消息采用二进制格式传输,不是纯文本。版本号固定为0x0101,操作ID占2字节(如Print-Job=0x0002),属性以value-tag+name+value的结构排列。Wireshark已经帮我们解析好了,所以看到的是可读的字段名。

我们找到ipp的包,图示这个为Print-Job

Wireshark_RTksq2TRu0

看到左下角展开的Internet Printing Protocol,我们仔细分析

Wireshark_9hdwofjlmf

可以看到这里列出了

字段 含义
operation-attributes-tag (组标记) 标记操作属性组的开始,值是0x01(十六进制),表示后面是操作属性
attributes-charset utf-8 字符集,指定请求中字符串的编码方式(通常是UTF-8)
attributes-natural-language en-us 自然语言,用于错误消息等的人类语言(美式英语)
printer-uri ipp://192.168.2.1:631/printers/SCX-4623-Series 目标打印机,指定要将作业发送到哪台打印机
requesting-user-name anonymous 请求用户,作业是由谁提交的(这里是匿名用户)
document-name fake.png 文档名称,正在打印的文件名(只是信息性字段)
document-format application/octet-stream 文档格式,告诉CUPS这个数据是原始字节流(不指定具体格式)
end-of-attributes-tag (组结束标记) 操作属性组结束,后面可能跟着作业属性组或文档数据

找到一个successful-ok包,分析打印机属性组(部分)

explorer_jmvpCs2txu

在这部分的属性里,我们能得到这些信息

URI信息(攻击面关键)

属性名 安全含义
printer-uri-supported ipp://192.168.2.1:631/printers/SCX-4623-Series 打印机主端点,接收打印任务
printer-icons http://192.168.2.1:631/icons/SCX-4623-Series.png 图标URL,可被利用进行SSRF
printer-more-info http://192.168.2.1:631/printers/SCX-4623-Series 管理页面URL
printer-strings-uri http://192.168.2.1:631/strings/SCX-4623-Series.strings 本地化字符串文件

攻击价值:这些URI暴露了CUPS Web管理界面(631端口),是后续攻击的入口点。

认证与安全配置

属性名 安全含义
uri-authentication-supported 'requesting-user-name' 关键:只验证用户名,不验证密码!
uri-security-supported 'none' 无TLS/加密,IPP over HTTP(而非HTTPS)

安全风险

  • uri-authentication-supported: requesting-user-name 表示仅需提供用户名即可通过认证
  • 攻击者可以伪造任意用户名发送打印任务
  • 结合 uri-security-supported: none,所有通信都是明文

打印机状态(可用性判断)

属性名 含义
printer-state idle 空闲,可接收任务
printer-is-accepting-jobs true 接受新任务
printer-state-reasons 'none' 无故障
queued-job-count 0 队列为空

攻击价值:打印机空闲,是发起攻击的最佳时机。

时间戳信息(便于追踪规避)

属性名 用途
printer-current-time 2026-06-10T14:52:23.0+0000 打印机当前时间
printer-up-time 1781103143 运行秒数(自启动)
printer-config-change-time 1781061688 上次配置变更时间

型号信息(漏洞匹配)

属性名 含义
printer-dns-sd-name 'Samsung_SCX-4623_Series_Aurora @ lambda' DNS-SD广播名称
printer-type 143428 位掩码,表示打印机能力
printer-is-shared true 打印机被共享(可被其他设备发现)

这里只展示了部分内容,以下还有打印机的location等信息。

然后我们找到了一个Creat-Job的包,分析作业属性组

Wireshark_YMINQ0FpKs

属性名 类型 含义
copies 1 integer 打印份数:只打印1份
finishings none enum 装订/整理选项:不使用任何装订、打孔、钉书等功能
job-cancel-after 10800 integer 自动取消时间:作业在10800秒(3小时)后自动取消,如果还未打印
job-hold-until 'no-hold' keyword 保持直到no-hold表示不保持,立即打印
job-priority 50 integer 作业优先级:50(通常范围1-100,50是中等优先级)
job-sheets 'none', 'none' 1setOf name 起始/结束页:打印开始时和结束时没有额外的“横幅页”
number-up 1 integer 每张纸页数:1表示一页打印在单张纸上(不合并多页)
print-color-mode 'monochrome' keyword 色彩模式:黑白打印(不是彩色)

这里的Data就是实际被打印的内容

Wireshark_Rlne5i7BU7

与网络安全的联系

我觉得了解一下这个打印服务有助于拓宽攻击面认知,认识新的攻击入口

CVE漏洞(仅举几例)

CVE编号 组件 问题描述
CVE-2024-47176 cups-browsed 将任意UDP包视为可信打印机
CVE-2024-47076 libcupsfilters 未验证PPD中的FoomaticRIPCommandLine字段
CVE-2024-47175 libppd 未验证PPD字段
CVE-2024-47177 cups-filters foomatic-rip过滤器命令注入

CVE-2024-47176细节描述:CUPS 是一个基于标准的开源打印系统,“CUPS 浏览”包含网络打印功能,包括但不限于自动发现打印服务和共享打印机。“cups-browsed” 绑定于 “INADDR_ANY:631”,使其信任任何来源的数据包,并可能导致“Get-Printer-Attributes”的 IPP 请求请求到攻击者控制的 URL。当与其他漏洞如CVE-2024-47076、CVE-2024-47175和CVE-2024-47177结合时,攻击者可以在目标机器上远程执行任意命令,且在恶意打印机被打印时无需身份验证。

  1. UDP 631端口暴露cups-browsed服务默认监听在0.0.0.0:631,接收来自任何来源的UDP包
  2. 打印机自动添加:攻击者发送精心构造的UDP包(mDNS广播),受害系统会自动添加一台恶意打印机
  3. PPD污染:受害系统向攻击者控制的IPP服务器发起请求,攻击者返回一个包含恶意FoomaticRIPCommandLine字段的PPD文件
  4. 命令注入:当用户尝试打印到这台恶意打印机时,foomatic-rip过滤器会执行该字段中的命令

最近的漏洞:CVE-2026-34990

CVE-2026-34990细节描述:OpenPrinting CUPS 是一个面向 Linux 及其他类 Unix 操作系统的开源打印系统。在2.4.16及更早版本中,本地无权限用户可以强制cupsd通过可重用的授权(Authorization: Local …代币。该令牌足以驱动 localhost 上的 /admin/ 请求,攻击者可以将 CUPS-Create-Local-Printer 与 printer-is-shared=true 结合起来,以持久化 file:///…即使正常的文件设备策略会拒绝此类URI,也会排队。打印到该队列会给任意的根文件覆盖;下面的PoC使用该原语来丢弃sudoer片段并演示根命令的执行。

攻击链:

  1. 普通用户创建本地恶意IPP服务
  2. cupsd自动认证并获得一个可重用的Authorization: Local token
  3. 攻击者利用该token访问 /admin/ 管理接口
  4. 通过CUPS-Create-Local-Printer创建file://协议打印机
  5. 向该打印机发送打印任务 → 任意root文件写入
  6. 通过写入sudoers文件实现root权限获取

以下做一个PoC的详细介绍:

┌─────────────────────────────────────────────────────────────────────┐
│                    CVE-2026-34990 攻击链与PoC对应关系                  │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  阶段1: Token钓鱼                                                     │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │ 1. 创建恶意IPP服务监听 localhost:任意端口                        │    │
│  │ 2. 等待cupsd连接并自动发放 Authorization: Local token           │    │
│  │ 3. 捕获token供后续使用                                         │    │
│  └─────────────────────────────────────────────────────────────┘    │
│                              │                                      │
│                              ▼                                      │
│  阶段2: 策略绕过 + 队列持久化                                           │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │ 4. 使用token调用 CUPS-Create-Local-Printer                    │    │
│  │ 5. 设置 printer-is-shared=true 绕过 FileDevice 检查            │    │
│  │ 6. 创建 file:///etc/sudoers.d/exploit 队列                    │    │
│  └─────────────────────────────────────────────────────────────┘    │
│                              │                                      │
│                              ▼                                      │
│  阶段3: 任意root文件写入                                               │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │ 7. 向恶意队列发送打印任务                                       │    │
│  │ 8. cupsd以root权限将数据写入目标文件                            │    │
│  │ 9. sudoers生效 → 获取root权限                                 │    │
│  └─────────────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────────────┘