Initial Docker packaging
This commit is contained in:
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
.env
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "app"]
|
||||
path = app
|
||||
url = https://github.com/ntfargo/Relapse-Exploit.git
|
||||
+28
@@ -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"]
|
||||
@@ -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 <repository-url>
|
||||
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 <reviewed-commit>
|
||||
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.
|
||||
Submodule
+1
Submodule app added at 09f10f5b58
@@ -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
|
||||
@@ -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()
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user