打印这一块
历史前言
在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的打印系统。它使用 lp、lpstat、cancel 等命令。虽然命令和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作为管理打印机、打印请求和打印队列的基础协议。
其工作流程可以分解为以下几个关键环节:
- 任务接收与队列管理:当用户通过应用程序发起打印时,CUPS首先会创建一个打印任务(Job)。这个任务包含了目标打印队列、文档名称以及页面描述信息。CUPS为每个任务分配编号(如queue-1、queue-2),以便用户后续监控或取消任务。
- 格式转换(过滤器系统):这是CUPS最核心的能力之一。它接收应用程序产生的页面描述(如“在此处换行”、“在此处画线”),然后自动确定并使用最佳的**过滤器(filters)**和打印机驱动程序,将这些描述转换成打印机能够理解的语言(如PostScript、PCL或打印机特定的光栅格式)。
- 数据传输(后端系统):经过转换后的可打印数据,最后被传递给**后端(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背后的过滤器系统(如pdftopdf、pstops、gstoraster等)和后端程序(如usb、socket后端等)。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 |
暂停/恢复某个任务 |
就像图片中的:
IPP打印流程大概如下:
- 发现打印机(mDNS/DNS-SD广播)
- Get-Printer-Attributes(查询打印机能力)
- 打印机返回支持的功能(纸张尺寸、双面、彩色等)
- Print-Job(提交打印数据)
- 打印机返回任务ID和状态
- Get-Job-Attributes(可选:轮询任务进度)
- 返回任务状态(pending/processing/completed)
IPP的安全性可以这样理解——HTTP : HTTPS = IPP : IPPS
IPP与无驱动打印
IPP最重要的贡献之一,是推动了无驱动打印的普及。
传统打印流程依赖PPD文件和过滤器进行格式转换,而现代IPP打印机可以直接接受标准化的页面描述格式(如PDF、PWG Raster)。客户端和打印机之间通过IPP协商:
- 客户端询问打印机:“你能接收什么格式的数据?”
- 打印机回答:“我支持PDF、PCL、PWG Raster……”
- 客户端选择双方都支持的格式(通常是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
看到左下角展开的Internet Printing Protocol,我们仔细分析
可以看到这里列出了
| 字段 | 值 | 含义 |
|---|---|---|
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包,分析打印机属性组(部分)
在这部分的属性里,我们能得到这些信息
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的包,分析作业属性组
| 属性名 | 值 | 类型 | 含义 |
|---|---|---|---|
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就是实际被打印的内容
与网络安全的联系
我觉得了解一下这个打印服务有助于拓宽攻击面认知,认识新的攻击入口
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结合时,攻击者可以在目标机器上远程执行任意命令,且在恶意打印机被打印时无需身份验证。
- UDP 631端口暴露:
cups-browsed服务默认监听在0.0.0.0:631,接收来自任何来源的UDP包 - 打印机自动添加:攻击者发送精心构造的UDP包(mDNS广播),受害系统会自动添加一台恶意打印机
- PPD污染:受害系统向攻击者控制的IPP服务器发起请求,攻击者返回一个包含恶意
FoomaticRIPCommandLine字段的PPD文件 - 命令注入:当用户尝试打印到这台恶意打印机时,
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片段并演示根命令的执行。
攻击链:
- 普通用户创建本地恶意IPP服务
- cupsd自动认证并获得一个可重用的Authorization: Local token
- 攻击者利用该token访问 /admin/ 管理接口
- 通过CUPS-Create-Local-Printer创建file://协议打印机
- 向该打印机发送打印任务 → 任意root文件写入
- 通过写入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权限 │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
Comments