Analyze the compromised network printer as a DFIR analyst.
An internal host port-scanned a network printer and found its raw print port open. The attacker then spoke PJL directly to the printer over that port, fingerprinted the model, and used the file system commands to walk the printer’s storage. A queued print job tied to a suspected insider was downloaded, and a backup directory on the device turned out to hold an RDP file and an admin PowerShell script. The RDP file gave away the address of an internal jumphost, so a printer with no authentication on its job port became a map to the next hop.
capture.pcap → open with Wireshark or tshark. A single capture holds the scan and the whole printer sessionTip: The printer session is plain text over TCP. Following the stream or dumping the payload as ASCII reads like a console log, so there is no need to carve anything.
Start with the TCP conversation table. One internal host sends short, one-way SYN probes to a single target (the printer) across many ports, with almost no data exchanged on most of them. That pattern is a SYN scan, and the source of those probes is the attacker’s machine.
tshark -r capture.pcap -q -z conv,tcp
From Q1 the scan target is the printer. Open ports are the ones that completed the handshake, and only one of them goes on to carry a long session with real payload. That is the raw print port, and its stream is the PJL traffic used in the rest of the case.
tshark -r capture.pcap -q -z conv,tcp -Y 'ip.addr==172.31.40.241'
Port 9100 is the JetDirect raw print service, and anything sent to it goes straight to the printer’s job interpreter. Following the stream shows lines starting with @PJL, including FSDIRLIST and FSUPLOAD, which list and read files on the printer’s own storage. That is Printer Job Language.
tshark -r capture.pcap -q -z follow,tcp,ascii,0
The attacker’s first PJL commands identify the device. The @PJL INFO ID request gets the model string back in the response from the printer, so read the server side of the 9100 stream.
tshark -r capture.pcap -Y 'tcp.port==9100 && data' -T fields -e data | xxd -r -p
Gotcha: The format hint (
** ******** *** ******) is four words, so the answer is the whole model string as the printer reports it, including theproanddnparts, not just “HP LaserJet”.
Later in the same PJL session the attacker lists the saved-jobs area of the printer and then runs FSUPLOAD against one file in it, pulling the content back over the connection. The path in that command is the answer. Printer volumes are addressed with a 0: prefix.
tshark -r capture.pcap -Y 'tcp.port==9100 && data' -T fields -e data | xxd -r -p | grep -a 'FSUPLOAD'
The file pulled in Q5 comes back in the stream as plain text, and it is a layoff notice. The organization named in that document is the target. The answer is read from the file body in the upload response.
tshark -r capture.pcap -q -z follow,tcp,ascii,0
Further along the stream the attacker runs FSDIRLIST on a backup directory. The listing returns an RDP connection file, internal.rdp, next to an admin PowerShell script. The directory in that FSDIRLIST command is the answer, and it starts with a slash rather than a volume prefix.
tshark -r capture.pcap -Y 'tcp.port==9100 && data' -T fields -e data | xxd -r -p | grep -a -A5 'FSDIRLIST'
From Q7, the attacker reads internal.rdp from that directory. RDP files store the target in the full address field, and that value is the jumphost.
tshark -r capture.pcap -Y 'tcp.port==9100 && data' -T fields -e data | xxd -r -p | grep -a 'full address'
The same Q7 directory listing shows a second file beside the RDP file, with a .ps1 extension. That is the admin script.
tshark -r capture.pcap -Y 'tcp.port==9100 && data' -T fields -e data | xxd -r -p | grep -a '\.ps1'
| Phase | Action |
|---|---|
| Discovery | SYN scan of the printer from an internal host |
| Discovery | Raw print port found open and used for the rest of the intrusion |
| Execution | PJL commands sent directly to the printer, model identified |
| Discovery | Printer file system listed with FSDIRLIST |
| Collection | Scheduled print job pulled from the saved-jobs folder with FSUPLOAD |
| Credential Access | Backup directory listed, exposing an RDP file and an admin PowerShell script |
| Credential Access | RDP file read, revealing the jumphost address |
| Type | Value |
|---|---|
| IP (attacker host) | 172.31.35[.]23 |
| IP (printer, scan target) | 172.31.40[.]241 |
| IP (jumphost) | 172.31.23[.]97 |
| Port | 9100/tcp |
| Printer path | 0:/saveDevice/SavedJobs/InProgress/scheduled.ps |
| Directory | /backup/jumphost/2023 |
| File | internal.rdp, remote-service.ps1 |
| Technique | ID | Description |
|---|---|---|
| Network Service Discovery | T1046 | SYN scan of the printer to find open ports |
| File and Directory Discovery | T1083 | FSDIRLIST walk of the printer storage |
| Data from Local System | T1005 | FSUPLOAD of the scheduled print job |
| Unsecured Credentials: Credentials In Files | T1552.001 | RDP file and admin script read from a backup folder, exposing the jumphost |
Port 9100 should not be reachable from user and server segments. Raw JetDirect has no authentication, so anyone who can connect can run PJL. Restrict it to the print server and put printers in their own VLAN.
Disable PJL file system access and clear stored jobs. The attacker listed and downloaded files from the printer because the file system commands were enabled. Turn off remote file access on the device and avoid leaving sensitive jobs saved on it.
Do not keep infrastructure notes on printers. An RDP file and an admin script in a backup folder gave away the jumphost with one read. Keep connection files and admin scripts off devices that are not hardened for secrets.
Alert on a single host sweeping ports on an internal device. The SYN scan was a clear precursor to the session. Flow data showing one internal source probing many ports on a printer is cheap to detect.
The attacker reached the printer’s job port from inside the network and used it to read stored files, including a sensitive print job and the address of the jumphost. The printer was the foothold and the data store, and none of it needed a credential.