This is an 'easy mode' guide to the NFPC at Defcon 20. Let's begin: starting at packet 253, there is a TCP/LPD session from 10.0.1.4 to 10.0.1.3. A quick scan of the reconstructed session reveals little:

Having never seen LPD traffic, we gave the RFC 1179 a quick read, lucky it's relatively short. Section 5 outlines the LPD commands.

Let's look at each part of the session:

5.2 |02|Queue|LF|

00000000 02 50 41 53 53 54 48 52 55 0a .PASSTHR U.

"Receiving a job is controlled by a second level of commands. The daemon is given commands by sending them over the same connection. The commands are described in the next section (6).

After this command is sent, the client must read an acknowledgement octet from the daemon. A positive acknowledgement is an octet of zero bits. A negative acknowledgement is an octet of any other pattern."

6.2 |02|Count|SP|Name|LF|

0000000A 02 37 35 20 63 66 41 30 30 37 46 4f 58 2d 57 53 .75 cfA0 07FOX-WS 0000001A 0a

"The control file must be an ASCII stream with the ends of lines indicated by ASCII LF. The total number of bytes in the stream is sent as the first operand. The name of the control file is sent as the second. It should start with ASCII "cfA", followed by a three digit job number, followed by the host name which has constructed the control file. Acknowledgement processing must occur as usual after the command is sent.

The next "Operand 1" octets over the same TCP connection are the intended contents of the control file. Once all of the contents have been delivered, an octet of zero bits is sent as an indication that the file being sent is complete. A second level of acknowledgement processing must occur at this point."

0000001B 48 46 4f 58 2d 57 53 0a 50 62 6f 62 0a 4a 45 78 HFOX-WS. Pbob.JEx 0000002B 70 65 6e 73 65 46 6f 72 6d 2e 70 64 66 0a 6c 64 penseFor m.pdf.ld 0000003B 66 41 30 30 37 46 4f 58 2d 57 53 0a 55 64 66 41 fA007FOX -WS.UdfA 0000004B 30 30 37 46 4f 58 2d 57 53 0a 4e 45 78 70 65 6e 007FOX-W S.NExpen 0000005B 73 65 46 6f 72 6d 2e 70 64 66 0a 00 seForm.p df..

Note: following the description of 6.2 the ending 0a 00 indicates the end of the control file information transfer.

Let's look at what was transferred, outlined in section 7:

7.2 H - Host name: FOX-WS

7.8 P - User identification: bob

7.4 J - Job name for banner page: ExpenseForm.pdf

7.22 l - Print file leaving control characters: dfA007FOX-WS

7.11 U - Unlink data file: dfA007FOX-WS

7.7 N - Name of source file: ExpenseForm.pdf

Does any of this matter? Not really, but walking through the data format will uncover a corruption that will need correction later.

Back to section 6!

6.3 |03|Count|SP|Name|LF|

00000067 03 31 32 35 38 39 39 39 30 36 38 34 33 30 30 30 .1258999 06843000 00000077 20 64 66 41 30 30 37 46 4f 58 2d 57 53 0a dfA007F OX-WS.

"The data file may contain any 8 bit values at all. The total number of bytes in the stream may be sent as the first operand, otherwise the field should be cleared to 0. The name of the data file should start with ASCII "dfA". This should be followed by a three digit job number. The job number should be followed by the host name which has constructed the data file. Interpretation of the contents of the data file is determined by the contents of the corresponding control file. If a data file length has been specified, the next "Operand 1" octets over the same TCP connection are the intended contents of the data file. In this case, once all of the contents have been delivered, an octet of zero bits is sent as an indication that the file being sent is complete. A second level of acknowledgement processing must occur at this point."

And then the file:

00000085 00 00 00 1b 01 40 45 4a 4c 20 31 32 38 34 2e 34 .....@EJ L 1284.4 00000095 0a 40 45 4a 4c 20 20 20 20 20 0a 1b 40 1b 40 1b .@EJL ..@.@. 000000A5 28 52 08 00 00 52 45 4d 4f 54 45 31 4a 53 04 00 (R...REM OTE1JS.. 000000B5 00 00 00 00 50 4d 02 00 00 00 54 49 08 00 00 07 ....PM.. ..TI.... 000000C5 dc 06 1b 0f 09 36 49 52 02 00 00 03 53 4e 4c 00 .....6IR ....SNL. [...]

But wait. The 6.3 (receive data file) command is saying the file is 125899906843000 bytes, this is a problem, but will stash it for a moment while we look into the file transferred. We're close right? This is where the RFC ends and Google (@EJL) picks up. Undocumented Printing says this is called "EPSON Job Language". Read the page and you'll find something familiar:

Enter IEEE 1284.4 Command

@EJL<SP>1284.4<LF>~ @EJL<SP><SP><SP><SP><SP><LF>

"This command specifically takes the printer out of the Epson packet mode communication protocol (whatever that is) and enables IEEE 1284.4 communications mode."

Awesome, now let's start searching for IEEE 1284.4... and find nothing helpful. This is where we started getting frustrated and took out our anger on the craps machines. Hundreds of dollars later, we had some ideas.

Using a Windows-based virtual spooler we created a print-to-file device from an EPSON NX420 driver (you can find the model of the printer used, if you search the XML data after the LPD session). Then we used a short python script to quickly scan the Round4.pcap and replay what 10.0.1.4 sent.

from scapy.all import * import sys import socket def getsends(port): hold ="" sends = [] for i in range(len(packets)): if IP in packets[i] and TCP in packets[i]: if packets[i][TCP].sport == port and packets[i][TCP].flags == 24L: sends.append(hold) hold = "" elif packets[i][TCP].dport == port and Raw in packets[i]: hold += packets[i][Raw].load sends.append(hold) return sends def sendsends(host, port, ps): host = host port = port s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((host, int(port))) for i in range(len(ps)): s.send(ps[i]) print "SENT", i, len(ps), len(ps[i]) buf = s.recv(1024) print buf # Read packets packets = rdpcap(sys.argv[1]) prints = getsends(515) # Fix file size, noted earlier bytes = '145938' prints[3] = "03".decode("hex") + bytes + ' dfA007FOX-WS' + '\x0a' # Send packets host = sys.argv[2] sendsends(host, 515, prints)

This worked, sort of... the print job shows up, '007' and all. But printing the job results in just scribble (actually it tries to print the EJL commands as ASCII). After some additional 'angry-gambling', we switched to easy mode and searched for a printer. The logic being: we could spool the data correctly, if we could find a printer that speaks the proprietary EPSON Job Language, along with LPD, we'd be able to print the job and find the answer?

How to 'Easy Mode':

Drive to Fry's (insert address). Find a printer similar to the EPSON NX420, like another NX series. Walk around until you find some paper, try the 8.5x11 advertising sheets, or look in nearby printers. Network the printer (you can find equipment around the store to create the network), we used a Verizon MiFi. Use the above python script to send the job to the printer. Profit.

Fun competition, thanks LMGSecurity, Paul dot com, and everything Defcon!