From 83f9035d6b150adf0d5577756e0746c300db9664 Mon Sep 17 00:00:00 2001 From: kadinho Date: Tue, 29 Sep 2026 13:06:06 +0100 Subject: [PATCH] Initial Docker packaging --- .dockerignore | 14 +++++++++ .gitignore | 1 + .gitmodules | 3 ++ Dockerfile | 28 ++++++++++++++++++ README.md | 76 ++++++++++++++++++++++++++++++++++++++++++++++++ app | 1 + compose.yaml | 15 ++++++++++ docker/server.py | 46 +++++++++++++++++++++++++++++ payloads.sha256 | 5 ++++ 9 files changed, 189 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 Dockerfile create mode 100644 README.md create mode 160000 app create mode 100644 compose.yaml create mode 100644 docker/server.py create mode 100644 payloads.sha256 diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..82e9a94 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,14 @@ +.git +.gitmodules +.env +README.md +compose.yaml +app/.git +app/.github +app/.agents +app/.codex +app/LICENSE +app/README.md +app/serve.py +**/__pycache__ +**/*.pyc diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c49bd7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..5115abc --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "app"] + path = app + url = https://github.com/ntfargo/Relapse-Exploit.git diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f4e4d83 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,28 @@ +ARG PYTHON_IMAGE=python:3.14-alpine +FROM ${PYTHON_IMAGE} + +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 \ + PORT=8080 + +WORKDIR /srv + +COPY payloads.sha256 /tmp/payloads.sha256 +COPY app/index.html /srv/public/index.html +COPY app/src /srv/public/src +COPY app/offsets /srv/public/offsets +COPY app/payloads /srv/public/payloads +COPY docker/server.py /srv/server.py + +RUN cd /srv/public && sha256sum -c /tmp/payloads.sha256 \ + && rm /tmp/payloads.sha256 \ + && chown -R 65532:65532 /srv + +USER 65532:65532 + +EXPOSE 8080 + +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD ["python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8080/', timeout=2).close()"] + +CMD ["python", "/srv/server.py"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..e82106a --- /dev/null +++ b/README.md @@ -0,0 +1,76 @@ +# Relapse Exploit Docker + +Container packaging for [PS5 Relapse Exploit](https://github.com/ntfargo/Relapse-Exploit). +The upstream project is pinned as a Git submodule, and the image build verifies the +SHA-256 digest of every executable payload before including it. + +Use this only on consoles you own or are authorized to test. The exploit can panic +the console, corrupt kernel state, expose administrative services, and execute +arbitrary code with elevated privileges. + +## Clone and build + +```sh +git clone --recurse-submodules +cd relapse-exploit-docker +docker compose build +``` + +For an existing clone: + +```sh +git submodule update --init --recursive +docker compose build +``` + +## Run + +```sh +docker compose up -d +``` + +Open `http://HOST-IP:8000/` from the PS5. View the service status with: + +```sh +docker compose ps +docker compose logs -f +``` + +Stop it with `docker compose down`. + +By default, port 8000 is exposed on every host interface so the PS5 can reach it. +Restrict it to one host address when possible: + +```sh +HOST_BIND=192.168.1.10 HOST_PORT=8000 docker compose up -d +``` + +Do not expose this service to the internet. Prefer an isolated local network. + +## Update upstream + +Review upstream changes before updating because the served JavaScript and payloads +ultimately execute with kernel privileges. + +```sh +git -C app fetch origin +git -C app checkout +sha256sum app/payloads/* +``` + +After reviewing the new binaries, update `payloads.sha256`, rebuild, test, and commit +both the submodule pointer and checksum changes together. + +## Container hardening + +The runtime image: + +- contains only the files required by the exploit page; +- runs as numeric user/group `65532:65532`; +- has a read-only filesystem, all Linux capabilities dropped, and no-new-privileges; +- disables directory listings and browser caching; +- adds restrictive browser security headers; +- does not expose the upstream `.git` directory. + +These controls protect the host-side web server. They do not make the PS5 exploit or +its precompiled payloads intrinsically safe. diff --git a/app b/app new file mode 160000 index 0000000..09f10f5 --- /dev/null +++ b/app @@ -0,0 +1 @@ +Subproject commit 09f10f5b58fab8a9ae9e8df214da37271290b13e diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..3284279 --- /dev/null +++ b/compose.yaml @@ -0,0 +1,15 @@ +services: + relapse-exploit: + build: + context: . + image: relapse-exploit:local + ports: + - "${HOST_BIND:-0.0.0.0}:${HOST_PORT:-8000}:8080" + read_only: true + tmpfs: + - /tmp:size=1m,mode=1777 + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + restart: unless-stopped diff --git a/docker/server.py b/docker/server.py new file mode 100644 index 0000000..4799793 --- /dev/null +++ b/docker/server.py @@ -0,0 +1,46 @@ +import http.server +import mimetypes +import os +from pathlib import Path + + +PUBLIC_ROOT = Path("/srv/public") +PORT = int(os.environ.get("PORT", "8080")) + +mimetypes.add_type("text/javascript", ".js") +mimetypes.add_type("application/octet-stream", ".elf") +mimetypes.add_type("application/octet-stream", ".bin") + + +class Handler(http.server.SimpleHTTPRequestHandler): + def __init__(self, *args, **kwargs): + super().__init__(*args, directory=PUBLIC_ROOT, **kwargs) + + def list_directory(self, path): + self.send_error(404, "Not found") + return None + + def end_headers(self): + self.send_header("Cache-Control", "no-store, max-age=0") + self.send_header("Pragma", "no-cache") + self.send_header("X-Content-Type-Options", "nosniff") + self.send_header("X-Frame-Options", "DENY") + self.send_header("Referrer-Policy", "no-referrer") + self.send_header( + "Content-Security-Policy", + "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; " + "worker-src 'self'; child-src 'self'; connect-src 'self'; img-src 'self' data:; " + "object-src 'none'; base-uri 'none'; frame-ancestors 'none'", + ) + super().end_headers() + + +class Server(http.server.ThreadingHTTPServer): + daemon_threads = True + allow_reuse_address = True + + +if __name__ == "__main__": + with Server(("0.0.0.0", PORT), Handler) as server: + print(f"Serving Relapse Exploit on port {PORT}", flush=True) + server.serve_forever() diff --git a/payloads.sha256 b/payloads.sha256 new file mode 100644 index 0000000..615a3eb --- /dev/null +++ b/payloads.sha256 @@ -0,0 +1,5 @@ +de5dd480d12637527ba2d75e35de851adbb59c455293fa32588c492ef6b44b81 payloads/elfldr-ps5-1360.elf +26034dfbb88dd8be344fee1876c387bfcba0386edb079014e18ec25580368f6d payloads/etaHEN.elf +7cfb3a8cb86db67893c360ae3531f460f800e9f24039797038e6e81fc50f18a9 payloads/kexp_2026_05_25.bin +858880a8adbedaabfaced025384f4f6f908cfe44f1a049070ad8df911c22b972 payloads/kstuff.elf +0b30a1c23b83ebb2aa6523c8caaa5801ee89bde00e90cb36f392a29bb0bdb403 payloads/shadowmountplus.elf