Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,60 @@ int main(){
- Utilities/vm_map helpers: https://github.com/buzzer-re/playstation_research_utils
- Related projects: https://github.com/OpenOrbis/mira-project, https://github.com/ps5-payload-dev/gdbsrv

## Additional FreeBSD kernel exploitation audit patterns

The PS5 technique above assumes you already have kernel R/W. Calif's 2026 FreeBSD audit released three useful **pre-R/W bug patterns** that are worth checking in native FreeBSD code paths as well:

### 1. Copyin/copyout size confusion into a caller-owned stack buffer

When a helper chooses between a small **on-stack array** and a heap allocation, verify that **both** the allocation size and the later `copyin`/`copyout` length use the **element size**, not the pointer size. In the released `setcred(2)` LPE, a helper handling supplementary groups used `sizeof(pointer)` instead of `sizeof(gid_t)`, so a user-controlled group count copied `N*8` bytes into a caller frame that only reserved space for `N*4`-sized entries.

Things worth checking during audit/exploitation:

- Stack/heap dual paths such as `smallbuf` vs `malloc()` fallbacks.
- Copies that start at an **interior pointer** like `buf + 1`; the bug may miss the saved return address but still smash nearby locals or callee-saved registers.
- Whether the **privilege check happens after the copy**, turning a nominally privileged API into a reachable pre-check overflow.
- Exact **version-specific frame layout**. The same source bug may exist on multiple FreeBSD releases while only one build is exploitable because the compiler output, local-variable ordering, or mitigation state changed.

### 2. Redirected syscall numbers that are not re-validated before `sysent` lookup

Audit every path that can **translate or redirect a syscall number** (`SYS_syscall`, `SYS___syscall`, ptrace remote syscall helpers, compat/emulation wrappers). The important rule is: **bounds-check the final syscall number after redirection**, not just the original request.

If the redirected value reaches `sv->sv_table[sc]` unchecked, adjacent kernel memory may be interpreted as a fake `struct sysent`:

- `sy_call` can become an unintended kernel call target.
- `sy_narg` can be turned into a later copyin/copyout overflow.
- `sy_flags` / tracing metadata can expose secondary side effects.

Extra things to look for:

- `register_t` → `int` truncation or signedness bugs that enable **negative indices** as well as oversized positive ones.
- A **safe native syscall path** elsewhere in the kernel that already performs the missing post-redirect check; diffing the safe and unsafe paths is often enough to spot the bug quickly.

### 3. Embedded `selinfo` / poll waiter lifetime bugs that become linked-list writes

Any kernel object embedding `struct selinfo` (or related `knlist` state) must **drain waiters before the object is freed**. A common review pattern is:

- object is reachable from `poll(2)` / `select(2)` / `kqueue(2)`
- a wait path calls `selrecord()`
- the final free path destroys the lock/object **without** `seldrain()`

That leaves stale waiter metadata pointing into freed memory. If the freed slot is reclaimed with attacker-influenced data (Calif used `SCM_RIGHTS`-driven `filedescent` allocations against `procdesc`), the later timeout/cleanup path may run a stale `TAILQ_REMOVE()` or similar unlink logic on the reclaimed object.

Why this matters:

- list removal mutates **forward and backward pointers**, so reclaimed wait state can become a practical **kernel pointer write** primitive
- the trigger may be delayed until **poll timeout**, **close**, or another async cleanup path, which helps reclaim the freed slot first
- `selwakeup()` on one code path is **not enough** if another free path can skip it; what matters is that every terminal lifetime path drains the waiters before `free()`

A good FreeBSD-specific grep set is: `selrecord`, `seldrain`, `selwakeup`, `knlist_destroy`, `TAILQ_REMOVE`, and final free/destructor routines for objects reachable from `pdfork`, sockets, pipes, procdescs, and device file operations.

## References

- [Calif - An AI audit of FreeBSD](https://blog.calif.io/p/an-ai-audit-of-freebsd)
- [Calif setcred write-up](https://github.com/califio/publications/blob/main/MADBugs/freebsd/setcred-CVE-2026-45250/WRITEUP.md)
- [Calif ptrace PT_SC_REMOTE write-up](https://github.com/califio/publications/blob/main/MADBugs/freebsd/ptrace-CVE-2026-45253/WRITEUP.md)
- [Calif procdesc/file write-up](https://github.com/califio/publications/blob/main/MADBugs/freebsd/file-CVE-2026-45251/WRITEUP.md)
- [Usermode ELF injection on the PlayStation 5](https://reversing.codes/posts/PlayStation-5-ELF-Injection/)
- [ps5-payload-dev/sdk](https://github.com/ps5-payload-dev/sdk)
- [ps5-payload-dev/elfldr](https://github.com/ps5-payload-dev/elfldr)
Expand Down