339 lines
14 KiB
Markdown
339 lines
14 KiB
Markdown
# Maintainer and agent handoff
|
||
|
||
Last updated: 2026-10-02
|
||
|
||
This file is the durable handoff for future sessions. Start work from this
|
||
repository rather than from the upstream Relapse Exploit checkout.
|
||
|
||
## Current status
|
||
|
||
The Docker wrapper has been created, committed, pushed to the local Gitea, and
|
||
deployed successfully. The user confirmed that the exploit worked on the PS5 and
|
||
that opening the PS5 User Guide reached an exploit page directly.
|
||
|
||
The exact destination selected by the external DNS was not independently verified.
|
||
Do not assume that the User Guide page came from the local container merely because
|
||
it displayed Relapse. The local container is confirmed at the raw IP URL below.
|
||
|
||
## Repositories
|
||
|
||
Primary local workspace:
|
||
|
||
```text
|
||
/var/www/code/others/relapse-exploit-docker
|
||
```
|
||
|
||
Gitea push remote:
|
||
|
||
```text
|
||
[email protected]:TinyBacklog/relapse-exploit-docker.git
|
||
```
|
||
|
||
Gitea public clone URL:
|
||
|
||
```text
|
||
http://192.168.20.20:3000/TinyBacklog/relapse-exploit-docker.git
|
||
```
|
||
|
||
Upstream submodule:
|
||
|
||
```text
|
||
https://github.com/ntfargo/Relapse-Exploit.git
|
||
```
|
||
|
||
Pinned upstream commit:
|
||
|
||
```text
|
||
dd8e4e0914c5e9d066ee1f9b056be76cb992c8e0
|
||
```
|
||
|
||
Repository-local Git identity:
|
||
|
||
```text
|
||
kadinho <[email protected]>
|
||
```
|
||
|
||
## Live deployment
|
||
|
||
- host: `net-utils01`;
|
||
- host IP: `192.168.20.30`;
|
||
- SSH user used during setup: `root`;
|
||
- checkout: `/opt/relapse-exploit-docker`;
|
||
- service URL: `http://192.168.20.30:8000/`;
|
||
- image name: `relapse-exploit:local`;
|
||
- Compose service: `relapse-exploit`;
|
||
- container name generated by Compose:
|
||
`relapse-exploit-docker-relapse-exploit-1`;
|
||
- host port: `8000/TCP`;
|
||
- container port: `8080/TCP`;
|
||
- restart policy: `unless-stopped`.
|
||
|
||
The same Compose project also runs `ps5-guide-gateway`, bound specifically to
|
||
`192.168.20.30` on DNS UDP/TCP 53 and HTTP/HTTPS TCP 80/443. It resolves only
|
||
`manuals.playstation.net`, returns NXDOMAIN for every other name, and redirects the
|
||
User Guide to `http://192.168.20.30:8000/`.
|
||
|
||
Port `8080` on the host is already used by `unifi-network-application`; do not move
|
||
this service there. At the last verification, the Relapse container was healthy,
|
||
ran as `65532:65532`, used a read-only root filesystem, and returned HTTP 200 over
|
||
the LAN. `/.git/config` returned 404.
|
||
|
||
No firewall management tool was detected on `net-utils01` during setup.
|
||
|
||
## Deployment authentication detail
|
||
|
||
`net-utils01` does not currently have a Gitea SSH client key authorized for cloning.
|
||
The deployment clone therefore uses the public HTTP URL. Do not copy personal SSH
|
||
private keys to the server or enable SSH agent forwarding as a workaround. If write
|
||
or authenticated access becomes necessary, create a repository-scoped deploy key.
|
||
|
||
The Gitea server's SSH fingerprints were compared with the already trusted local
|
||
host before its host keys were added to `/root/.ssh/known_hosts` on `net-utils01`.
|
||
|
||
## Runtime design
|
||
|
||
The image is based on `python:3.14-alpine` and uses only Python's standard library.
|
||
It copies these upstream assets into `/srv/public`:
|
||
|
||
- `index.html`;
|
||
- `src/`;
|
||
- `offsets/`;
|
||
- `payloads/`.
|
||
|
||
The build validates every payload listed in `payloads.sha256`. The server disables
|
||
directory listing and caching, sends restrictive browser headers, and runs under
|
||
numeric UID/GID 65532. Compose additionally makes the root filesystem read-only,
|
||
drops all Linux capabilities, and enables `no-new-privileges`.
|
||
|
||
The base image tag is not digest-pinned in the Dockerfile. A prior successful build
|
||
resolved `python:3.14-alpine` to:
|
||
|
||
```text
|
||
sha256:9e9fde4d32eedce0b661d9ab91e826b62dddf28e928c230ec55f1866cac66b01
|
||
```
|
||
|
||
Treat that as historical build information, not as an instruction to pin it without
|
||
checking architecture support and update policy.
|
||
|
||
## Approved payload hashes
|
||
|
||
At the pinned upstream commit:
|
||
|
||
```text
|
||
de5dd480d12637527ba2d75e35de851adbb59c455293fa32588c492ef6b44b81 payloads/elfldr-ps5-1360.elf
|
||
7cfb3a8cb86db67893c360ae3531f460f800e9f24039797038e6e81fc50f18a9 payloads/kexp_2026_05_25.bin
|
||
```
|
||
|
||
The payloads are precompiled, stripped binaries. Hash matching proves consistency
|
||
with the reviewed snapshot; it does not prove that the binaries are safe.
|
||
|
||
This upstream revision removed etaHEN, KStuff, and ShadowMount+ plus the `R2`
|
||
auto-loader because those optional payloads caused compatibility issues. The page
|
||
now stops after starting the ELF loader; any additional reviewed ELF must be sent
|
||
externally to the PS5 on TCP port 9021.
|
||
|
||
## External payload candidate evidence
|
||
|
||
A controlled intake and startup smoke test was completed on 2026-10-02 for
|
||
kstuff-lite v1.11 and ShadowMountPlus 1.7beta3. Locally calculated SHA-256 digests
|
||
matched the digests published by GitHub's release API. The verified ELFs were sent
|
||
once from `net-utils01` to `192.168.40.14:9021`, in that order and separated by
|
||
more than two seconds. Both connected and returned successful startup output;
|
||
ShadowMountPlus identified firmware 13.20, installed its hooks, synchronized an
|
||
empty game library and bound its API only to `127.0.0.1:10101`. The temporary
|
||
server copies were removed, and no downloaded binary is stored in this repository.
|
||
|
||
The normalized, machine-readable evidence and the proposed payload-manager import
|
||
contract and test limitations are in
|
||
[`docs/payload-evidence/`](payload-evidence/README.md). The required order is
|
||
kstuff-lite first, followed by ShadowMountPlus only after kstuff startup has been
|
||
confirmed. Catalog entries remain disabled and non-favorite by default despite the
|
||
successful smoke test; user selection is separate from evidence.
|
||
|
||
## PS5 and DNS state
|
||
|
||
The PS5 now uses the static address:
|
||
|
||
```text
|
||
192.168.40.14
|
||
```
|
||
|
||
The local HTTP endpoint is available and verified at:
|
||
|
||
```text
|
||
http://192.168.20.30:8000/
|
||
```
|
||
|
||
The upstream public DNS `45.56.67.85` was used initially, but the PS5 did not contact
|
||
the local container. The local gateway was therefore added. Configure the PS5 with
|
||
primary DNS `192.168.20.30` and secondary DNS `0.0.0.0`, then restart the console and
|
||
accept the self-signed certificate warning in the User Guide.
|
||
|
||
The gateway intentionally provides no recursive DNS forwarding. Only
|
||
`manuals.playstation.net` resolves; all other names receive NXDOMAIN. This blocks
|
||
PSN and update resolution and keeps the setup independent of a public DNS operator.
|
||
The PS5 was previously identified as `192.168.10.26`; requests from
|
||
`192.168.10.6` seen at that time were from a different client. Those addresses
|
||
are historical and must not be used as the current payload destination.
|
||
|
||
The PS5 ELF loader listens on TCP port `9021` after a successful exploit run. On
|
||
2026-10-01, a connection-only probe from `net-utils01` to
|
||
`192.168.40.14:9021` succeeded after inter-VLAN access was enabled. The probe
|
||
used `nc -vz`, sent no ELF payload, and confirmed that a new TCP connection from
|
||
the server VLAN to the console VLAN was permitted. `netcat-openbsd` is installed
|
||
on `net-utils01` for this diagnostic. Keep the applicable network policy limited
|
||
to the required LAN source, destination and TCP port; never expose port `9021`
|
||
to the Internet.
|
||
|
||
The inter-VLAN firewall policy reported as configured is:
|
||
|
||
| Source | Destination | Protocol/port | Purpose/state |
|
||
| --- | --- | --- | --- |
|
||
| `net-utils01` (`192.168.20.30`) | PS5 (`192.168.40.14`) | TCP `9021` | payload manager to ELF loader; active |
|
||
| PS5 (`192.168.40.14`) | `net-utils01` (`192.168.20.30`) | TCP `80`, `443`, `8000`; UDP/TCP `53` | Relapse page, User Guide gateway and restricted DNS; active |
|
||
| PS5 (`192.168.40.14`) | `net-utils01` (`192.168.20.30`) | TCP `8001` | reserved for payload-manager phase 5; enable only during that phase |
|
||
| PS5 (`192.168.40.14`) | remaining `192.168.20.0/24` hosts | all | blocked |
|
||
| hosts other than `net-utils01` | PS5 (`192.168.40.14`) | TCP `9021` | blocked |
|
||
|
||
The specific allow rules must be evaluated before the broader deny rules. Keep
|
||
TCP `8001` disabled until phase 5 has a reviewed service that requires it. The
|
||
successful TCP `9021` probe independently verifies that particular allow path;
|
||
the complete firewall policy was not audited from this repository.
|
||
|
||
Full PS5 instructions and DNS options are in `docs/PS5_SETUP.md`.
|
||
|
||
### Y2JB controlled test (2026-10-02; kernel/loader startup verified)
|
||
|
||
The PS5 Pro is on firmware 13.20. Its separate offline test user is `PLAYER1`
|
||
(slot 2); ActRemoteLink activated that slot and enabled user/global Remote Play.
|
||
Fake sign-in was **not** executed: reviewed implementations also write base
|
||
registry state, so isolation from the existing personal account is unproven.
|
||
Remote Play pairing remains untested.
|
||
|
||
The local YouTube PKG metadata identifies `PPSA01652`, version `01.000.030`,
|
||
Content ID `JA0004-PPSA01652_00-YOUTUBESIEJA0000`, despite its filename saying EUR.
|
||
Y2JB 1.6 was initially placed under `PPSA01650`. A subsequent PSN/DNS dialog loop
|
||
persisted with both local restricted DNS and primary DNS `127.0.0.2`.
|
||
At that point neither the cause nor successful Y2JB launch had been established;
|
||
the later successful run is recorded in
|
||
[`docs/Y2JB_TEST_2026-10-02.md`](Y2JB_TEST_2026-10-02.md).
|
||
|
||
The approved directory correction placed the verified Y2JB 1.6 file at
|
||
`/user/download/PPSA01652/download0.dat` (336789504 bytes). Its source and complete
|
||
destination reread matched SHA-256
|
||
`946fb64427d340a1a302d260e0cc448b50fe15cfcff8ea167a0d33d3aa76f39d`.
|
||
An existing, different file was preserved on the PS5 as
|
||
`/user/download/PPSA01652/download0.dat.before-y2jb-20261002T150112Z`;
|
||
its reread SHA-256 is
|
||
`dc553ea85aab9da884316c0fe9bb222da3e8d384ddf2c1fd119779b11c8a79c1`.
|
||
The original `PPSA01650` copy remains untouched. Do not delete the backup without
|
||
approval. After a normal reboot the user's screenshot confirmed Y2JB 1.6,
|
||
firmware 13.20, Title ID PPSA01652, app version 01.000.030, popups disabled, and
|
||
Remote JS Loader listening at `192.168.40.14:50000`. This demonstrates working
|
||
userland startup without fake sign-in; the exact cause of the earlier loop is
|
||
not isolated because both directory and file contents differed.
|
||
|
||
After the user enabled a temporary net-utils01 → PS5 TCP 50000 exception,
|
||
`edisnord/relapse-y2jb` v1.0.0 `relapse.js` (261458 bytes, SHA-256
|
||
`f85129b861f43a19f6810be5182aecdcf82a25e59ee5fbcd0604e4a4e2a40d69`)
|
||
was sent once. Output identified 13.20 offsets, kernel privileges ready, kexp
|
||
return zero, restored eboot descriptors, both real pipe buffers restored with
|
||
10/10 writes verified, completed teardown and ELF loader on 9021. A subsequent
|
||
connection-only TCP 9021 probe succeeded. The JS temporary copy was removed.
|
||
This is a startup smoke test, not proof of long-term stability, safe app closure
|
||
on this firmware, Remote Play pairing or homebrew operation in this boot.
|
||
The payload leaves three kernel sysctl OIDs modified until reboot, including
|
||
`kern.smp.cpus` returning a kernel address instead of a CPU count. App-close safety
|
||
is asserted by the output but upstream measured it only on 12.60; do not present
|
||
that as independently verified on 13.20. Do not send another kernel exploit in
|
||
an already exploited session. Disable temporary TCP 50000 when not needed.
|
||
|
||
FTP used `ps5-payload-dev/ftpsrv` v0.21.1, SHA-256
|
||
`7d4b31c83eae4e056580482a3a074e1db25922efc74ac1e439473b45d71a5938`.
|
||
Active-mode tests failed with `550 Bad file descriptor`; passive mode succeeded.
|
||
Temporary pfSense exceptions from net-utils01 to PS5 are TCP 2121 and
|
||
49152–65535, restricted to those two IPs. FTP was terminated with KILL. Disable
|
||
these rules when the test is complete; TCP 31337 (ActRemoteLink) is also temporary.
|
||
The obsolete reverse active-FTP exception PS5 → net-utils01 TCP 20212 is not needed.
|
||
|
||
PKG Manager v1.4.1 runs in memory on PS5 TCP 8844, with Direct Install TCP 18842;
|
||
restart the reviewed payload after a PS5 reboot. Access through loopback SSH
|
||
forwards, with matching net-utils01 → PS5 firewall exceptions only. This does not
|
||
install or start the Y2JB autoloader. itsPLK autoloader v0.9.1 has no supported
|
||
kernel exploit for 13.20; v1.0.0-dev-794049f adds Relapse but remains experimental
|
||
and has not been installed in this test.
|
||
|
||
**Server memory constraint:** net-utils01 has about 1.9 GiB RAM and no swap;
|
||
`/tmp` is tmpfs. Staging approximately 622 MiB of Y2JB files there caused severe
|
||
memory pressure with SSH/HTTP stalls; removal restored SSH. Use a verified
|
||
disk-backed path instead (`/var/tmp` is currently on ext4 `/dev/sda1`, about 70 GiB
|
||
free). Check memory and mount type before future payload staging. Test downloads
|
||
must be removed explicitly after verification; do not clear service-owned tmp dirs.
|
||
|
||
## Common operations
|
||
|
||
Inspect the live service:
|
||
|
||
```sh
|
||
ssh net-utils01
|
||
cd /opt/relapse-exploit-docker
|
||
docker compose ps
|
||
docker compose logs -f
|
||
```
|
||
|
||
Synchronize documentation-only changes:
|
||
|
||
```sh
|
||
cd /opt/relapse-exploit-docker
|
||
git pull --ff-only
|
||
git submodule update --init --recursive
|
||
```
|
||
|
||
Deploy runtime changes:
|
||
|
||
```sh
|
||
cd /opt/relapse-exploit-docker
|
||
git pull --ff-only
|
||
git submodule update --init --recursive
|
||
docker compose up -d --build
|
||
docker compose ps
|
||
curl -I http://127.0.0.1:8000/
|
||
```
|
||
|
||
Stop or restart:
|
||
|
||
```sh
|
||
docker compose down
|
||
docker compose up -d
|
||
# or
|
||
docker compose restart
|
||
```
|
||
|
||
## Known risks and review findings
|
||
|
||
The upstream project deliberately exploits WebKit and the PS5 kernel, escalates the
|
||
browser process to root, escapes its sandbox, and executes ELF payloads.
|
||
|
||
Important upstream findings from the initial static review:
|
||
|
||
1. Payloads are executable privileged binaries without upstream signature
|
||
verification or reproducible-build evidence.
|
||
2. The AIO cleanup path marks groups as defused even when cleanup reports failure,
|
||
which can leave risky kernel state behind.
|
||
3. Some early kernel failures return a result that the UI can misreport as root and
|
||
sandbox escape success.
|
||
4. The ELF loader on port 9021 is an arbitrary-payload interface by design.
|
||
|
||
The wrapper's checksum and container hardening mitigate host-side packaging risks;
|
||
they do not correct these upstream kernel or payload risks.
|
||
|
||
## Next-session checklist
|
||
|
||
1. Read this file, `AGENTS.md`, and `docs/PS5_SETUP.md`.
|
||
2. Run `git status --short --branch` and `git submodule status`.
|
||
3. Confirm the requested task's scope before changing the upstream submodule.
|
||
4. Preserve the payload-integrity and container-hardening invariants.
|
||
5. Validate proportionally to the change.
|
||
6. Update this handoff if infrastructure or operational state changes.
|
||
7. Push `main` to Gitea and, when appropriate, synchronize `net-utils01`.
|